lofty/id3/v2/frame/header/
mod.rs1pub(super) mod parse;
2
3use crate::error;
4use crate::error::{Id3v2Error, Id3v2ErrorKind, LoftyError};
5use crate::id3::v2::FrameFlags;
6use crate::prelude::ItemKey;
7use crate::tag::TagType;
8
9use std::borrow::Cow;
10use std::fmt::{Display, Formatter};
11
12#[derive(Clone, Debug, PartialEq, Eq, Hash)]
17#[allow(missing_docs)]
18pub struct FrameHeader<'a> {
19 pub(crate) id: FrameId<'a>,
20 pub flags: FrameFlags,
21}
22
23impl<'a> FrameHeader<'a> {
24 pub const fn new(id: FrameId<'a>, flags: FrameFlags) -> Self {
28 Self { id, flags }
29 }
30
31 pub const fn id(&'a self) -> &'a FrameId<'a> {
33 &self.id
34 }
35}
36
37#[derive(PartialEq, Clone, Debug, Eq, Hash)]
41pub enum FrameId<'a> {
42 Valid(Cow<'a, str>),
44 Outdated(Cow<'a, str>),
50}
51
52impl<'a> FrameId<'a> {
53 pub fn new<I>(id: I) -> error::Result<Self>
62 where
63 I: Into<Cow<'a, str>>,
64 {
65 Self::new_cow(id.into())
66 }
67
68 pub(in crate::id3::v2::frame) fn new_cow(id: Cow<'a, str>) -> error::Result<Self> {
70 Self::verify_id(&id)?;
71
72 match id.len() {
73 3 => Ok(FrameId::Outdated(id)),
74 4 => Ok(FrameId::Valid(id)),
75 _ => Err(
76 Id3v2Error::new(Id3v2ErrorKind::BadFrameId(id.into_owned().into_bytes())).into(),
77 ),
78 }
79 }
80
81 pub fn as_str(&self) -> &str {
83 match self {
84 FrameId::Valid(v) | FrameId::Outdated(v) => v,
85 }
86 }
87
88 pub(in crate::id3::v2::frame) fn verify_id(id_str: &str) -> error::Result<()> {
89 for c in id_str.chars() {
90 if !c.is_ascii_uppercase() && !c.is_ascii_digit() {
91 return Err(Id3v2Error::new(Id3v2ErrorKind::BadFrameId(
92 id_str.as_bytes().to_vec(),
93 ))
94 .into());
95 }
96 }
97
98 Ok(())
99 }
100
101 pub fn as_borrowed(&'a self) -> Self {
103 match self {
104 Self::Valid(inner) => Self::Valid(Cow::Borrowed(inner)),
105 Self::Outdated(inner) => Self::Outdated(Cow::Borrowed(inner)),
106 }
107 }
108
109 pub fn into_owned(self) -> FrameId<'static> {
111 match self {
112 Self::Valid(inner) => FrameId::Valid(Cow::Owned(inner.into_owned())),
113 Self::Outdated(inner) => FrameId::Outdated(Cow::Owned(inner.into_owned())),
114 }
115 }
116
117 pub fn into_inner(self) -> Cow<'a, str> {
119 match self {
120 FrameId::Valid(v) | FrameId::Outdated(v) => v,
121 }
122 }
123}
124
125impl Display for FrameId<'_> {
126 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
127 f.write_str(self.as_str())
128 }
129}
130
131impl<'a> Into<Cow<'a, str>> for FrameId<'a> {
132 fn into(self) -> Cow<'a, str> {
133 self.into_inner()
134 }
135}
136
137impl<'a> TryFrom<&'a ItemKey> for FrameId<'a> {
138 type Error = LoftyError;
139
140 fn try_from(value: &'a ItemKey) -> std::prelude::rust_2015::Result<Self, Self::Error> {
141 match value {
142 ItemKey::Unknown(unknown) if unknown.len() == 4 => {
143 Self::verify_id(unknown)?;
144 Ok(Self::Valid(Cow::Borrowed(unknown)))
145 },
146 k => {
147 if let Some(mapped) = k.map_key(TagType::Id3v2, false) {
148 if mapped.len() == 4 {
149 Self::verify_id(mapped)?;
150 return Ok(Self::Valid(Cow::Borrowed(mapped)));
151 }
152 }
153
154 Err(Id3v2Error::new(Id3v2ErrorKind::UnsupportedFrameId(k.clone())).into())
155 },
156 }
157 }
158}