lofty/id3/v2/items/
key_value_frame.rs1use crate::config::WriteOptions;
2use crate::error::Result;
3use crate::id3::v2::frame::content::verify_encoding;
4use crate::id3::v2::header::Id3v2Version;
5use crate::id3::v2::{FrameFlags, FrameHeader, FrameId};
6use crate::util::text::{TextDecodeOptions, TextEncoding, decode_text};
7
8use std::borrow::Cow;
9use std::io::Read;
10
11use byteorder::ReadBytesExt;
12
13#[derive(Clone, Debug, Eq, PartialEq, Hash)]
15pub struct KeyValueFrame<'a> {
16 pub(crate) header: FrameHeader<'a>,
17 pub encoding: TextEncoding,
19 pub key_value_pairs: Vec<(Cow<'a, str>, Cow<'a, str>)>,
21}
22
23impl<'a> KeyValueFrame<'a> {
24 pub fn new(
26 id: FrameId<'a>,
27 encoding: TextEncoding,
28 key_value_pairs: Vec<(Cow<'a, str>, Cow<'a, str>)>,
29 ) -> Self {
30 let header = FrameHeader::new(id, FrameFlags::default());
31 Self {
32 header,
33 encoding,
34 key_value_pairs,
35 }
36 }
37
38 pub fn id(&self) -> &FrameId<'_> {
40 &self.header.id
41 }
42
43 pub fn flags(&self) -> FrameFlags {
45 self.header.flags
46 }
47
48 pub fn set_flags(&mut self, flags: FrameFlags) {
50 self.header.flags = flags;
51 }
52
53 pub fn parse<R>(
65 reader: &mut R,
66 id: FrameId<'a>,
67 frame_flags: FrameFlags,
68 version: Id3v2Version,
69 ) -> Result<Option<Self>>
70 where
71 R: Read,
72 {
73 let Ok(encoding_byte) = reader.read_u8() else {
74 return Ok(None);
75 };
76
77 let encoding = verify_encoding(encoding_byte, version)?;
78
79 let mut values = Vec::new();
80
81 let mut text_decode_options = TextDecodeOptions::new().encoding(encoding).terminated(true);
82
83 let first_key = decode_text(reader, text_decode_options)?;
86
87 if first_key.bytes_read == 0 {
88 return Ok(None);
89 }
90
91 if encoding == TextEncoding::UTF16 {
92 text_decode_options = text_decode_options.bom(first_key.bom);
93 }
94
95 values.push((
96 Cow::Owned(first_key.content),
97 Cow::Owned(decode_text(reader, text_decode_options)?.content),
98 ));
99
100 loop {
101 let key = decode_text(reader, text_decode_options)?;
102 let value = decode_text(reader, text_decode_options)?;
103 if key.bytes_read == 0 || value.bytes_read == 0 {
104 break;
105 }
106
107 values.push((Cow::Owned(key.content), Cow::Owned(value.content)));
108 }
109
110 let header = FrameHeader::new(id, frame_flags);
111 Ok(Some(Self {
112 header,
113 encoding,
114 key_value_pairs: values,
115 }))
116 }
117
118 pub fn as_bytes(&self, write_options: WriteOptions) -> Result<Vec<u8>> {
124 let mut encoding = self.encoding;
125 if write_options.use_id3v23 {
126 encoding = encoding.to_id3v23();
127 }
128
129 let mut content = vec![encoding as u8];
130
131 for (key, value) in &self.key_value_pairs {
132 content.append(&mut encoding.encode(key, true, write_options.lossy_text_encoding)?);
133 content.append(&mut encoding.encode(value, true, write_options.lossy_text_encoding)?);
134 }
135 Ok(content)
136 }
137}
138
139impl KeyValueFrame<'static> {
140 pub(crate) fn downgrade(&self) -> KeyValueFrame<'_> {
141 KeyValueFrame {
142 header: self.header.downgrade(),
143 encoding: self.encoding,
144 key_value_pairs: self.key_value_pairs.clone(),
146 }
147 }
148}