1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
use std::fmt;
use std::ops::{Deref, DerefMut};

/// **!!NOT AN ACTUAL IDENTIFIER!!** Internally used as a wildcard.
pub(crate) const WILDCARD: Fourcc = Fourcc([255, 255, 255, 255]);

/// (`ftyp`) Identifier of an atom information about the filetype.
pub(crate) const FILETYPE: Fourcc = Fourcc(*b"ftyp");
/// (`mdat`)
pub(crate) const MEDIA_DATA: Fourcc = Fourcc(*b"mdat");
/// (`moov`) Identifier of an atom containing a structure of children storing metadata.
pub(crate) const MOVIE: Fourcc = Fourcc(*b"moov");
/// (`mvhd`) Identifier of an atom containing information about the whole movie (or audio file).
pub(crate) const MOVIE_HEADER: Fourcc = Fourcc(*b"mvhd");
/// (`trak`) Identifier of an atom containing information about a single track.
pub(crate) const TRACK: Fourcc = Fourcc(*b"trak");
/// (`mdia`) Identifier of an atom containing information about a tracks media type and data.
pub(crate) const MEDIA: Fourcc = Fourcc(*b"mdia");
/// (`minf`)
pub(crate) const MEDIA_INFORMATION: Fourcc = Fourcc(*b"minf");
/// (`stbl`)
pub(crate) const SAMPLE_TABLE: Fourcc = Fourcc(*b"stbl");
/// (`stco`)
pub(crate) const SAMPLE_TABLE_CHUNK_OFFSET: Fourcc = Fourcc(*b"stco");
/// (`co64`)
pub(crate) const SAMPLE_TABLE_CHUNK_OFFSET_64: Fourcc = Fourcc(*b"co64");
/// (`stsd`)
pub(crate) const SAMPLE_TABLE_SAMPLE_DESCRIPTION: Fourcc = Fourcc(*b"stsd");
/// (`mp4a`)
pub(crate) const MP4_AUDIO: Fourcc = Fourcc(*b"mp4a");
/// (`esds`)
pub(crate) const ELEMENTARY_STREAM_DESCRIPTION: Fourcc = Fourcc(*b"esds");
/// (`udta`) Identifier of an atom containing user metadata.
pub(crate) const USER_DATA: Fourcc = Fourcc(*b"udta");
/// (`meta`) Identifier of an atom containing a metadata item list.
pub(crate) const METADATA: Fourcc = Fourcc(*b"meta");
/// (`hdlr`) Identifier of an atom specifying the handler component that should interpret the medias data.
pub(crate) const HANDLER_REFERENCE: Fourcc = Fourcc(*b"hdlr");
/// (`ilst`) Identifier of an atom containing a list of metadata atoms.
pub(crate) const ITEM_LIST: Fourcc = Fourcc(*b"ilst");
/// (`data`) Identifier of an atom containing typed data.
pub(crate) const DATA: Fourcc = Fourcc(*b"data");
/// (`mean`)
pub(crate) const MEAN: Fourcc = Fourcc(*b"mean");
/// (`name`)
pub(crate) const NAME: Fourcc = Fourcc(*b"name");
/// (`free`)
pub(crate) const FREE: Fourcc = Fourcc(*b"free");

/// (`----`)
pub const FREEFORM: Fourcc = Fourcc(*b"----");

// iTunes 4.0 atoms
/// (`rtng`)
pub const ADVISORY_RATING: Fourcc = Fourcc(*b"rtng");
/// (`©alb`)
pub const ALBUM: Fourcc = Fourcc(*b"\xa9alb");
/// (`aART`)
pub const ALBUM_ARTIST: Fourcc = Fourcc(*b"aART");
/// (`©ART`)
pub const ARTIST: Fourcc = Fourcc(*b"\xa9ART");
/// (`covr`)
pub const ARTWORK: Fourcc = Fourcc(*b"covr");
/// (`tmpo`)
pub const BPM: Fourcc = Fourcc(*b"tmpo");
/// (`©cmt`)
pub const COMMENT: Fourcc = Fourcc(*b"\xa9cmt");
/// (`cpil`)
pub const COMPILATION: Fourcc = Fourcc(*b"cpil");
/// (`©wrt`)
pub const COMPOSER: Fourcc = Fourcc(*b"\xa9wrt");
/// (`cprt`)
pub const COPYRIGHT: Fourcc = Fourcc(*b"cprt");
/// (`©gen`)
pub const CUSTOM_GENRE: Fourcc = Fourcc(*b"\xa9gen");
/// (`disk`)
pub const DISC_NUMBER: Fourcc = Fourcc(*b"disk");
/// (`©too`)
pub const ENCODER: Fourcc = Fourcc(*b"\xa9too");
/// (`gnre`)
pub const STANDARD_GENRE: Fourcc = Fourcc(*b"gnre");
/// (`©nam`)
pub const TITLE: Fourcc = Fourcc(*b"\xa9nam");
/// (`trkn`)
pub const TRACK_NUMBER: Fourcc = Fourcc(*b"trkn");
/// (`©day`)
pub const YEAR: Fourcc = Fourcc(*b"\xa9day");

// iTunes 4.2 atoms
/// (`©grp`)
pub const GROUPING: Fourcc = Fourcc(*b"\xa9grp");
/// (`stik`)
pub const MEDIA_TYPE: Fourcc = Fourcc(*b"stik");

// iTunes 4.9 atoms
/// (`catg`)
pub const CATEGORY: Fourcc = Fourcc(*b"catg");
/// (`keyw`)
pub const KEYWORD: Fourcc = Fourcc(*b"keyw");
/// (`pcst`)
pub const PODCAST: Fourcc = Fourcc(*b"pcst");
/// (`egid`)
pub const PODCAST_EPISODE_GLOBAL_UNIQUE_ID: Fourcc = Fourcc(*b"egid");
/// (`purl`)
pub const PODCAST_URL: Fourcc = Fourcc(*b"purl");

// iTunes 5.0
/// (`desc`)
pub const DESCRIPTION: Fourcc = Fourcc(*b"desc");
/// (`©lyr`)
pub const LYRICS: Fourcc = Fourcc(*b"\xa9lyr");

// iTunes 6.0
/// (`tves`)
pub const TV_EPISODE: Fourcc = Fourcc(*b"tves");
/// (`tven`)
pub const TV_EPISODE_NAME: Fourcc = Fourcc(*b"tven");
/// (`tvnn`)
pub const TV_NETWORK_NAME: Fourcc = Fourcc(*b"tvnn");
/// (`tvsn`)
pub const TV_SEASON: Fourcc = Fourcc(*b"tvsn");
/// (`tvsh`)
pub const TV_SHOW_NAME: Fourcc = Fourcc(*b"tvsh");

// iTunes 6.0.2
/// (`purd`)
pub const PURCHASE_DATE: Fourcc = Fourcc(*b"purd");

// iTunes 7.0
/// (`pgap`)
pub const GAPLESS_PLAYBACK: Fourcc = Fourcc(*b"pgap");

// Work, Movement
/// (`©mvn`)
pub const MOVEMENT: Fourcc = Fourcc(*b"\xa9mvn");
/// (`©mvc`)
pub const MOVEMENT_COUNT: Fourcc = Fourcc(*b"\xa9mvc");
/// (`©mvi`)
pub const MOVEMENT_INDEX: Fourcc = Fourcc(*b"\xa9mvi");
/// (`©wrk`)
pub const WORK: Fourcc = Fourcc(*b"\xa9wrk");
/// (`shwm`)
pub const SHOW_MOVEMENT: Fourcc = Fourcc(*b"shwm");

// Freeform
/// Mean string of most freeform identifiers (`com.apple.iTunes`)
pub const APPLE_ITUNES_MEAN: &str = "com.apple.iTunes";

/// (`----:com.apple.iTunes:ISRC`)
pub const ISRC: FreeformIdent = FreeformIdent::new(APPLE_ITUNES_MEAN, "ISRC");
/// (`----:com.apple.iTunes:LYRICIST`)
pub const LYRICIST: FreeformIdent = FreeformIdent::new(APPLE_ITUNES_MEAN, "LYRICIST");

/// A trait providing information about an identifier.
pub trait Ident: PartialEq<DataIdent> {
    /// Returns a 4 byte atom identifier.
    fn fourcc(&self) -> Option<Fourcc>;
    /// Returns a freeform identifier.
    fn freeform(&self) -> Option<FreeformIdent>;
}

// TODO: figure out how to implement PartialEq for Ident or require an implementation as a trait bound.
/// Returns wheter the identifiers match.
pub fn idents_match(a: &impl Ident, b: &impl Ident) -> bool {
    a.fourcc() == b.fourcc() && a.freeform() == b.freeform()
}

/// A 4 byte atom identifier (four character code).
#[derive(Clone, Copy, Default, Eq, PartialEq)]
pub struct Fourcc(pub [u8; 4]);

impl Deref for Fourcc {
    type Target = [u8; 4];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Fourcc {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl PartialEq<DataIdent> for Fourcc {
    fn eq(&self, other: &DataIdent) -> bool {
        match other {
            DataIdent::Fourcc(f) => self == f,
            DataIdent::Freeform { .. } => false,
        }
    }
}

impl Ident for Fourcc {
    fn fourcc(&self) -> Option<Fourcc> {
        Some(*self)
    }

    fn freeform(&self) -> Option<FreeformIdent> {
        None
    }
}

impl fmt::Debug for Fourcc {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Fourcc({})", self.0.iter().map(|b| char::from(*b)).collect::<String>())
    }
}

impl fmt::Display for Fourcc {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0.iter().map(|b| char::from(*b)).collect::<String>())
    }
}

/// An identifier of a freeform (`----`) atom containing borrowd mean and name strings.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FreeformIdent<'a> {
    /// The mean string, typically in reverse domain notation.
    pub mean: &'a str,
    /// The name string used to identify the freeform atom.
    pub name: &'a str,
}

impl PartialEq<DataIdent> for FreeformIdent<'_> {
    fn eq(&self, other: &DataIdent) -> bool {
        match other {
            DataIdent::Fourcc(_) => false,
            DataIdent::Freeform { mean, name } => self.mean == mean && self.name == name,
        }
    }
}

impl Ident for FreeformIdent<'_> {
    fn fourcc(&self) -> Option<Fourcc> {
        None
    }

    fn freeform(&self) -> Option<FreeformIdent> {
        Some(self.clone())
    }
}

impl fmt::Display for FreeformIdent<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "----:{}:{}", self.mean, self.name)
    }
}

impl<'a> FreeformIdent<'a> {
    /// Creates a new freeform ident containing the mean and name as borrowed strings.
    pub const fn new(mean: &'a str, name: &'a str) -> Self {
        Self { mean, name }
    }
}

/// An identifier for data.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DataIdent {
    /// A standard identifier containing a 4 byte atom identifier.
    Fourcc(Fourcc),
    /// An identifier of a freeform (`----`) atom containing owned mean and name strings.
    Freeform {
        /// The mean string, typically in reverse domain notation.
        mean: String,
        /// The name string used to identify the freeform atom.
        name: String,
    },
}

impl Ident for DataIdent {
    fn fourcc(&self) -> Option<Fourcc> {
        match self {
            Self::Fourcc(i) => Some(*i),
            Self::Freeform { .. } => None,
        }
    }

    fn freeform(&self) -> Option<FreeformIdent> {
        match self {
            Self::Fourcc(_) => None,
            Self::Freeform { mean, name } => Some(FreeformIdent::new(mean.as_str(), name.as_str())),
        }
    }
}

impl fmt::Display for DataIdent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Fourcc(ident) => write!(f, "{}", ident),
            Self::Freeform { mean, name } => write!(f, "----:{}:{}", mean, name),
        }
    }
}

impl From<Fourcc> for DataIdent {
    fn from(value: Fourcc) -> Self {
        Self::Fourcc(value)
    }
}

impl From<FreeformIdent<'_>> for DataIdent {
    fn from(value: FreeformIdent) -> Self {
        Self::freeform(value.mean, value.name)
    }
}

impl From<&FreeformIdent<'_>> for DataIdent {
    fn from(value: &FreeformIdent) -> Self {
        Self::freeform(value.mean, value.name)
    }
}

impl DataIdent {
    /// Creates a new identifier of type [`DataIdent::Freeform`] containing the owned mean, and
    /// name string.
    pub fn freeform(mean: impl Into<String>, name: impl Into<String>) -> Self {
        Self::Freeform { mean: mean.into(), name: name.into() }
    }

    /// Creates a new identifier of type [`DataIdent::Fourcc`] containing an atom identifier with
    /// the 4-byte identifier.
    pub const fn fourcc(bytes: [u8; 4]) -> Self {
        Self::Fourcc(Fourcc(bytes))
    }
}