Skip to content

Commit 13ef4d6

Browse files
committed
aud_io: Move IO utils from lofty
1 parent d604ee1 commit 13ef4d6

File tree

30 files changed

+394
-386
lines changed

30 files changed

+394
-386
lines changed

aud_io/src/error.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ pub enum AudioError {
1515
StringFromUtf8(std::string::FromUtf8Error),
1616
/// Unable to convert bytes to a str
1717
StrFromUtf8(std::str::Utf8Error),
18+
/// This should **never** be encountered
19+
Infallible(std::convert::Infallible),
1820
}
1921

2022
impl From<std::io::Error> for AudioError {
@@ -35,6 +37,12 @@ impl From<std::str::Utf8Error> for AudioError {
3537
}
3638
}
3739

40+
impl From<std::convert::Infallible> for AudioError {
41+
fn from(input: std::convert::Infallible) -> Self {
42+
AudioError::Infallible(input)
43+
}
44+
}
45+
3846
impl Display for AudioError {
3947
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
4048
match self {
@@ -43,6 +51,7 @@ impl Display for AudioError {
4351
AudioError::StrFromUtf8(err) => write!(f, "{err}"),
4452
AudioError::Io(err) => write!(f, "{err}"),
4553
AudioError::TextDecode(message) => write!(f, "Text decoding: {message}"),
54+
AudioError::Infallible(_) => write!(f, "An expected condition was not upheld"),
4655
}
4756
}
4857
}

aud_io/src/io.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
//! Various traits for reading and writing to file-like objects
2+
3+
use crate::error::{AudioError, Result};
4+
use crate::math::F80;
5+
6+
use std::collections::VecDeque;
7+
use std::fs::File;
8+
use std::io::{Cursor, Read, Seek, Write};
9+
10+
/// Provides a method to truncate an object to the specified length
11+
///
12+
/// This is one component of the [`FileLike`] trait, which is used to provide implementors access to any
13+
/// file saving methods such as [`AudioFile::save_to`](crate::file::AudioFile::save_to).
14+
///
15+
/// Take great care in implementing this for downstream types, as Lofty will assume that the
16+
/// container has the new length specified. If this assumption were to be broken, files **will** become corrupted.
17+
///
18+
/// # Examples
19+
///
20+
/// ```rust
21+
/// use lofty::io::Truncate;
22+
///
23+
/// let mut data = vec![1, 2, 3, 4, 5];
24+
/// data.truncate(3);
25+
///
26+
/// assert_eq!(data, vec![1, 2, 3]);
27+
/// ```
28+
pub trait Truncate {
29+
/// The error type of the truncation operation
30+
type Error: Into<AudioError>;
31+
32+
/// Truncate a storage object to the specified length
33+
///
34+
/// # Errors
35+
///
36+
/// Errors depend on the object being truncated, which may not always be fallible.
37+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error>;
38+
}
39+
40+
impl Truncate for File {
41+
type Error = std::io::Error;
42+
43+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
44+
self.set_len(new_len)
45+
}
46+
}
47+
48+
impl Truncate for Vec<u8> {
49+
type Error = std::convert::Infallible;
50+
51+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
52+
self.truncate(new_len as usize);
53+
Ok(())
54+
}
55+
}
56+
57+
impl Truncate for VecDeque<u8> {
58+
type Error = std::convert::Infallible;
59+
60+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
61+
self.truncate(new_len as usize);
62+
Ok(())
63+
}
64+
}
65+
66+
impl<T> Truncate for Cursor<T>
67+
where
68+
T: Truncate,
69+
{
70+
type Error = <T as Truncate>::Error;
71+
72+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
73+
self.get_mut().truncate(new_len)
74+
}
75+
}
76+
77+
impl<T> Truncate for Box<T>
78+
where
79+
T: Truncate,
80+
{
81+
type Error = <T as Truncate>::Error;
82+
83+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
84+
self.as_mut().truncate(new_len)
85+
}
86+
}
87+
88+
impl<T> Truncate for &mut T
89+
where
90+
T: Truncate,
91+
{
92+
type Error = <T as Truncate>::Error;
93+
94+
fn truncate(&mut self, new_len: u64) -> std::result::Result<(), Self::Error> {
95+
(**self).truncate(new_len)
96+
}
97+
}
98+
99+
/// Provides a method to get the length of a storage object
100+
///
101+
/// This is one component of the [`FileLike`] trait, which is used to provide implementors access to any
102+
/// file saving methods such as [`AudioFile::save_to`](crate::file::AudioFile::save_to).
103+
///
104+
/// Take great care in implementing this for downstream types, as Lofty will assume that the
105+
/// container has the exact length specified. If this assumption were to be broken, files **may** become corrupted.
106+
///
107+
/// # Examples
108+
///
109+
/// ```rust
110+
/// use lofty::io::Length;
111+
///
112+
/// let data = vec![1, 2, 3, 4, 5];
113+
/// assert_eq!(data.len(), 5);
114+
/// ```
115+
pub trait Length {
116+
/// The error type of the length operation
117+
type Error: Into<AudioError>;
118+
119+
/// Get the length of a storage object
120+
///
121+
/// # Errors
122+
///
123+
/// Errors depend on the object being read, which may not always be fallible.
124+
fn len(&self) -> std::result::Result<u64, Self::Error>;
125+
}
126+
127+
impl Length for File {
128+
type Error = std::io::Error;
129+
130+
fn len(&self) -> std::result::Result<u64, Self::Error> {
131+
self.metadata().map(|m| m.len())
132+
}
133+
}
134+
135+
impl Length for Vec<u8> {
136+
type Error = std::convert::Infallible;
137+
138+
fn len(&self) -> std::result::Result<u64, Self::Error> {
139+
Ok(self.len() as u64)
140+
}
141+
}
142+
143+
impl Length for VecDeque<u8> {
144+
type Error = std::convert::Infallible;
145+
146+
fn len(&self) -> std::result::Result<u64, Self::Error> {
147+
Ok(self.len() as u64)
148+
}
149+
}
150+
151+
impl<T> Length for Cursor<T>
152+
where
153+
T: Length,
154+
{
155+
type Error = <T as Length>::Error;
156+
157+
fn len(&self) -> std::result::Result<u64, Self::Error> {
158+
Length::len(self.get_ref())
159+
}
160+
}
161+
162+
impl<T> Length for Box<T>
163+
where
164+
T: Length,
165+
{
166+
type Error = <T as Length>::Error;
167+
168+
fn len(&self) -> std::result::Result<u64, Self::Error> {
169+
Length::len(self.as_ref())
170+
}
171+
}
172+
173+
impl<T> Length for &T
174+
where
175+
T: Length,
176+
{
177+
type Error = <T as Length>::Error;
178+
179+
fn len(&self) -> std::result::Result<u64, Self::Error> {
180+
Length::len(*self)
181+
}
182+
}
183+
184+
impl<T> Length for &mut T
185+
where
186+
T: Length,
187+
{
188+
type Error = <T as Length>::Error;
189+
190+
fn len(&self) -> std::result::Result<u64, Self::Error> {
191+
Length::len(*self)
192+
}
193+
}
194+
195+
/// Provides a set of methods to read and write to a file-like object
196+
///
197+
/// This is a combination of the [`Read`], [`Write`], [`Seek`], [`Truncate`], and [`Length`] traits.
198+
/// It is used to provide implementors access to any file saving methods such as [`AudioFile::save_to`](crate::file::AudioFile::save_to).
199+
///
200+
/// Take great care in implementing this for downstream types, as Lofty will assume that the
201+
/// trait implementations are correct. If this assumption were to be broken, files **may** become corrupted.
202+
pub trait FileLike: Read + Write + Seek + Truncate + Length
203+
where
204+
<Self as Truncate>::Error: Into<AudioError>,
205+
<Self as Length>::Error: Into<AudioError>,
206+
{
207+
}
208+
209+
impl<T> FileLike for T
210+
where
211+
T: Read + Write + Seek + Truncate + Length,
212+
<T as Truncate>::Error: Into<AudioError>,
213+
<T as Length>::Error: Into<AudioError>,
214+
{
215+
}
216+
217+
pub trait ReadExt: Read {
218+
fn read_f80(&mut self) -> Result<F80>;
219+
}
220+
221+
impl<R> ReadExt for R
222+
where
223+
R: Read,
224+
{
225+
fn read_f80(&mut self) -> Result<F80> {
226+
let mut bytes = [0; 10];
227+
self.read_exact(&mut bytes)?;
228+
229+
Ok(F80::from_be_bytes(bytes))
230+
}
231+
}

aud_io/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pub mod text;
22
pub mod error;
33
pub(crate) mod macros;
44
pub mod math;
5+
pub mod io;

lofty/src/ape/tag/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ use crate::tag::{
1111
Accessor, ItemKey, ItemValue, MergeTag, SplitTag, Tag, TagExt, TagItem, TagType, try_parse_year,
1212
};
1313
use crate::util::flag_item;
14-
use crate::util::io::{FileLike, Truncate};
1514

1615
use std::borrow::Cow;
1716
use std::io::Write;
1817
use std::ops::Deref;
1918

19+
use aud_io::io::{FileLike, Truncate};
2020
use lofty_attr::tag;
2121

2222
macro_rules! impl_accessor {

lofty/src/ape/tag/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use crate::id3::{FindId3v2Config, find_id3v1, find_id3v2, find_lyrics3v2};
88
use crate::macros::{decode_err, err};
99
use crate::probe::Probe;
1010
use crate::tag::item::ItemValueRef;
11-
use crate::util::io::{FileLike, Truncate};
1211

1312
use std::io::{Cursor, Seek, SeekFrom, Write};
1413

14+
use aud_io::io::{FileLike, Truncate};
1515
use byteorder::{LittleEndian, WriteBytesExt};
1616

1717
#[allow(clippy::shadow_unrelated)]

lofty/src/error.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ pub enum ErrorKind {
7070
Fmt(std::fmt::Error),
7171
/// Failure to allocate enough memory
7272
Alloc(TryReserveError),
73-
/// This should **never** be encountered
74-
Infallible(std::convert::Infallible),
7573
}
7674

7775
/// The types of errors that can occur while interacting with ID3v2 tags
@@ -506,7 +504,7 @@ impl From<std::collections::TryReserveError> for LoftyError {
506504
impl From<std::convert::Infallible> for LoftyError {
507505
fn from(input: std::convert::Infallible) -> Self {
508506
Self {
509-
kind: ErrorKind::Infallible(input),
507+
kind: ErrorKind::AudioIo(AudioError::Infallible(input)),
510508
}
511509
}
512510
}
@@ -554,8 +552,6 @@ impl Display for LoftyError {
554552
),
555553
ErrorKind::FileDecoding(ref file_decode_err) => write!(f, "{file_decode_err}"),
556554
ErrorKind::FileEncoding(ref file_encode_err) => write!(f, "{file_encode_err}"),
557-
558-
ErrorKind::Infallible(_) => write!(f, "A expected condition was not upheld"),
559555
}
560556
}
561557
}

lofty/src/file/audio_file.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use crate::config::{ParseOptions, WriteOptions};
33
use crate::error::{LoftyError, Result};
44
use crate::tag::TagType;
55

6-
use crate::util::io::{FileLike, Length, Truncate};
76
use std::fs::OpenOptions;
87
use std::io::{Read, Seek};
98
use std::path::Path;
109

10+
use aud_io::io::{FileLike, Length, Truncate};
11+
1112
/// Provides various methods for interaction with a file
1213
pub trait AudioFile: Into<TaggedFile> {
1314
/// The struct the file uses for audio properties

lofty/src/file/tagged_file.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use crate::config::{ParseOptions, WriteOptions};
44
use crate::error::{LoftyError, Result};
55
use crate::properties::FileProperties;
66
use crate::tag::{Tag, TagExt, TagSupport, TagType};
7-
use crate::util::io::{FileLike, Length, Truncate};
87

98
use std::io::{Read, Seek};
109

10+
use aud_io::io::{FileLike, Length, Truncate};
11+
1112
/// Provides a common interface between [`TaggedFile`] and [`BoundTaggedFile`]
1213
pub trait TaggedFileExt {
1314
/// Returns the file's [`FileType`]

lofty/src/flac/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ use crate::ogg::tag::VorbisCommentsRef;
1717
use crate::ogg::{OggPictureStorage, VorbisComments};
1818
use crate::picture::{Picture, PictureInformation};
1919
use crate::tag::TagExt;
20-
use crate::util::io::{FileLike, Length, Truncate};
2120

2221
use std::borrow::Cow;
2322

23+
use aud_io::io::{FileLike, Length, Truncate};
2424
use lofty_attr::LoftyFile;
2525

2626
// Exports

lofty/src/flac/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ use crate::ogg::tag::VorbisCommentsRef;
77
use crate::ogg::write::create_comments;
88
use crate::picture::{Picture, PictureInformation};
99
use crate::tag::{Tag, TagType};
10-
use crate::util::io::{FileLike, Length, Truncate};
1110

1211
use std::borrow::Cow;
1312
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
1413

14+
use aud_io::io::{FileLike, Length, Truncate};
1515
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
1616

1717
const BLOCK_HEADER_SIZE: usize = 4;

0 commit comments

Comments
 (0)