1use crate::prelude::*;
31use crate::space::*;
32use urid::*;
33
34pub struct Literal;
38
39unsafe impl UriBound for Literal {
40 const URI: &'static [u8] = sys::LV2_ATOM__Literal;
41}
42
43#[derive(Clone, Copy, PartialEq, Eq, Debug)]
44pub enum LiteralInfo {
46 Language(URID),
47 Datatype(URID),
48}
49
50impl<'a, 'b> Atom<'a, 'b> for Literal
51where
52 'a: 'b,
53{
54 type ReadParameter = ();
55 type ReadHandle = (LiteralInfo, &'a str);
56 type WriteParameter = LiteralInfo;
57 type WriteHandle = StringWriter<'a, 'b>;
58
59 fn read(body: Space<'a>, _: ()) -> Option<(LiteralInfo, &'a str)> {
60 let (header, body) = body.split_type::<sys::LV2_Atom_Literal_Body>()?;
61 let info = if header.lang != 0 && header.datatype == 0 {
62 LiteralInfo::Language(URID::new(header.lang)?)
63 } else if header.lang == 0 && header.datatype != 0 {
64 LiteralInfo::Datatype(URID::new(header.datatype)?)
65 } else {
66 return None;
67 };
68 let data = body.data()?;
69 std::str::from_utf8(&data[0..data.len() - 1])
70 .or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()]))
71 .ok()
72 .map(|string| (info, string))
73 }
74
75 fn init(mut frame: FramedMutSpace<'a, 'b>, info: LiteralInfo) -> Option<StringWriter<'a, 'b>> {
76 (&mut frame as &mut dyn MutSpace).write(
77 &match info {
78 LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body {
79 lang: lang.get(),
80 datatype: 0,
81 },
82 LiteralInfo::Datatype(datatype) => sys::LV2_Atom_Literal_Body {
83 lang: 0,
84 datatype: datatype.get(),
85 },
86 },
87 true,
88 )?;
89 Some(StringWriter { frame })
90 }
91}
92
93pub struct String;
97
98unsafe impl UriBound for String {
99 const URI: &'static [u8] = sys::LV2_ATOM__String;
100}
101
102impl<'a, 'b> Atom<'a, 'b> for String
103where
104 'a: 'b,
105{
106 type ReadParameter = ();
107 type ReadHandle = &'a str;
108 type WriteParameter = ();
109 type WriteHandle = StringWriter<'a, 'b>;
110
111 fn read(body: Space<'a>, _: ()) -> Option<&'a str> {
112 body.data()
113 .and_then(|data| std::str::from_utf8(data).ok())
114 .map(|string| &string[..string.len() - 1]) }
116
117 fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option<StringWriter<'a, 'b>> {
118 Some(StringWriter { frame })
119 }
120}
121
122pub struct StringWriter<'a, 'b> {
124 frame: FramedMutSpace<'a, 'b>,
125}
126
127impl<'a, 'b> StringWriter<'a, 'b> {
128 pub fn append(&mut self, string: &str) -> Option<&mut str> {
134 let data = string.as_bytes();
135 let space = self.frame.write_raw(data, false)?;
136 unsafe { Some(std::str::from_utf8_unchecked_mut(space)) }
137 }
138}
139
140impl<'a, 'b> Drop for StringWriter<'a, 'b> {
141 fn drop(&mut self) {
142 (&mut self.frame as &mut dyn MutSpace).write(&0u8, false);
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use crate::prelude::*;
150 use crate::space::*;
151 use std::ffi::CStr;
152 use std::mem::{size_of, size_of_val};
153 use urid::*;
154
155 struct German;
156 unsafe impl UriBound for German {
157 const URI: &'static [u8] = b"http://lexvo.org/id/iso639-1/de\0";
158 }
159
160 #[derive(URIDCollection)]
161 pub struct TestURIDs {
162 atom: AtomURIDCollection,
163 german: URID<German>,
164 }
165
166 const SAMPLE0: &str = "Da steh ich nun, ich armer Tor! ";
167 const SAMPLE1: &str = "Und bin so klug als wie zuvor;";
168
169 #[test]
170 fn test_literal() {
171 let map = HashURIDMapper::new();
172 let urids = TestURIDs::from_map(&map).unwrap();
173
174 let mut raw_space: Box<[u8]> = Box::new([0; 256]);
175
176 {
178 let mut space = RootMutSpace::new(raw_space.as_mut());
179 let mut writer = (&mut space as &mut dyn MutSpace)
180 .init(
181 urids.atom.literal,
182 LiteralInfo::Language(urids.german.into_general()),
183 )
184 .unwrap();
185 writer.append(SAMPLE0).unwrap();
186 writer.append(SAMPLE1).unwrap();
187 }
188
189 {
191 let (atom, space) = raw_space.split_at(size_of::<sys::LV2_Atom_Literal>());
192
193 let literal = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom_Literal) };
194 assert_eq!(literal.atom.type_, urids.atom.literal.get());
195 assert_eq!(
196 literal.atom.size as usize,
197 size_of::<sys::LV2_Atom_Literal_Body>()
198 + size_of_val(SAMPLE0)
199 + size_of_val(SAMPLE1)
200 + 1
201 );
202 assert_eq!(literal.body.lang, urids.german.get());
203 assert_eq!(literal.body.datatype, 0);
204
205 let size = literal.atom.size as usize - size_of::<sys::LV2_Atom_Literal_Body>();
206 let string = CStr::from_bytes_with_nul(space.split_at(size).0)
207 .unwrap()
208 .to_str()
209 .unwrap();
210 assert_eq!(SAMPLE0.to_owned() + SAMPLE1, string);
211 }
212
213 {
215 let space = Space::from_slice(raw_space.as_ref());
216 let (body, _) = space.split_atom_body(urids.atom.literal).unwrap();
217 let (info, text) = Literal::read(body, ()).unwrap();
218
219 assert_eq!(info, LiteralInfo::Language(urids.german.into_general()));
220 assert_eq!(text, SAMPLE0.to_owned() + SAMPLE1);
221 }
222 }
223
224 #[test]
225 fn test_string() {
226 let map = HashURIDMapper::new();
227 let urids = crate::AtomURIDCollection::from_map(&map).unwrap();
228
229 let mut raw_space: Box<[u8]> = Box::new([0; 256]);
230
231 {
233 let mut space = RootMutSpace::new(raw_space.as_mut());
234 let mut writer = (&mut space as &mut dyn MutSpace)
235 .init(urids.string, ())
236 .unwrap();
237 writer.append(SAMPLE0).unwrap();
238 writer.append(SAMPLE1).unwrap();
239 }
240
241 {
243 let (string, space) = raw_space.split_at(size_of::<sys::LV2_Atom_String>());
244
245 let string = unsafe { &*(string.as_ptr() as *const sys::LV2_Atom_String) };
246 assert_eq!(string.atom.type_, urids.string);
247 assert_eq!(string.atom.size as usize, SAMPLE0.len() + SAMPLE1.len() + 1);
248
249 let string = std::str::from_utf8(space.split_at(string.atom.size as usize).0).unwrap();
250 assert_eq!(string[..string.len() - 1], SAMPLE0.to_owned() + SAMPLE1);
251 }
252
253 {
255 let space = Space::from_slice(raw_space.as_ref());
256 let (body, _) = space.split_atom_body(urids.string).unwrap();
257 let string = String::read(body, ()).unwrap();
258 assert_eq!(string, SAMPLE0.to_owned() + SAMPLE1);
259 }
260 }
261}