use crate::prelude::*;
use crate::space::*;
use urid::*;
pub struct Literal;
unsafe impl UriBound for Literal {
const URI: &'static [u8] = sys::LV2_ATOM__Literal;
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum LiteralInfo {
Language(URID),
Datatype(URID),
}
impl<'a, 'b> Atom<'a, 'b> for Literal
where
'a: 'b,
{
type ReadParameter = ();
type ReadHandle = (LiteralInfo, &'a str);
type WriteParameter = LiteralInfo;
type WriteHandle = StringWriter<'a, 'b>;
fn read(body: Space<'a>, _: ()) -> Option<(LiteralInfo, &'a str)> {
let (header, body) = body.split_type::<sys::LV2_Atom_Literal_Body>()?;
let info = if header.lang != 0 && header.datatype == 0 {
LiteralInfo::Language(URID::new(header.lang)?)
} else if header.lang == 0 && header.datatype != 0 {
LiteralInfo::Datatype(URID::new(header.datatype)?)
} else {
return None;
};
let data = body.data()?;
std::str::from_utf8(&data[0..data.len() - 1])
.or_else(|error| std::str::from_utf8(&data[0..error.valid_up_to()]))
.ok()
.map(|string| (info, string))
}
fn init(mut frame: FramedMutSpace<'a, 'b>, info: LiteralInfo) -> Option<StringWriter<'a, 'b>> {
(&mut frame as &mut dyn MutSpace).write(
&match info {
LiteralInfo::Language(lang) => sys::LV2_Atom_Literal_Body {
lang: lang.get(),
datatype: 0,
},
LiteralInfo::Datatype(datatype) => sys::LV2_Atom_Literal_Body {
lang: 0,
datatype: datatype.get(),
},
},
true,
)?;
Some(StringWriter { frame })
}
}
pub struct String;
unsafe impl UriBound for String {
const URI: &'static [u8] = sys::LV2_ATOM__String;
}
impl<'a, 'b> Atom<'a, 'b> for String
where
'a: 'b,
{
type ReadParameter = ();
type ReadHandle = &'a str;
type WriteParameter = ();
type WriteHandle = StringWriter<'a, 'b>;
fn read(body: Space<'a>, _: ()) -> Option<&'a str> {
body.data()
.and_then(|data| std::str::from_utf8(data).ok())
.map(|string| &string[..string.len() - 1]) }
fn init(frame: FramedMutSpace<'a, 'b>, _: ()) -> Option<StringWriter<'a, 'b>> {
Some(StringWriter { frame })
}
}
pub struct StringWriter<'a, 'b> {
frame: FramedMutSpace<'a, 'b>,
}
impl<'a, 'b> StringWriter<'a, 'b> {
pub fn append(&mut self, string: &str) -> Option<&mut str> {
let data = string.as_bytes();
let space = self.frame.write_raw(data, false)?;
unsafe { Some(std::str::from_utf8_unchecked_mut(space)) }
}
}
impl<'a, 'b> Drop for StringWriter<'a, 'b> {
fn drop(&mut self) {
(&mut self.frame as &mut dyn MutSpace).write(&0u8, false);
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::space::*;
use std::ffi::CStr;
use std::mem::{size_of, size_of_val};
use urid::*;
struct German;
unsafe impl UriBound for German {
const URI: &'static [u8] = b"http://lexvo.org/id/iso639-1/de\0";
}
#[derive(URIDCollection)]
pub struct TestURIDs {
atom: AtomURIDCollection,
german: URID<German>,
}
const SAMPLE0: &str = "Da steh ich nun, ich armer Tor! ";
const SAMPLE1: &str = "Und bin so klug als wie zuvor;";
#[test]
fn test_literal() {
let map = HashURIDMapper::new();
let urids = TestURIDs::from_map(&map).unwrap();
let mut raw_space: Box<[u8]> = Box::new([0; 256]);
{
let mut space = RootMutSpace::new(raw_space.as_mut());
let mut writer = (&mut space as &mut dyn MutSpace)
.init(
urids.atom.literal,
LiteralInfo::Language(urids.german.into_general()),
)
.unwrap();
writer.append(SAMPLE0).unwrap();
writer.append(SAMPLE1).unwrap();
}
{
let (atom, space) = raw_space.split_at(size_of::<sys::LV2_Atom_Literal>());
let literal = unsafe { &*(atom.as_ptr() as *const sys::LV2_Atom_Literal) };
assert_eq!(literal.atom.type_, urids.atom.literal.get());
assert_eq!(
literal.atom.size as usize,
size_of::<sys::LV2_Atom_Literal_Body>()
+ size_of_val(SAMPLE0)
+ size_of_val(SAMPLE1)
+ 1
);
assert_eq!(literal.body.lang, urids.german.get());
assert_eq!(literal.body.datatype, 0);
let size = literal.atom.size as usize - size_of::<sys::LV2_Atom_Literal_Body>();
let string = CStr::from_bytes_with_nul(space.split_at(size).0)
.unwrap()
.to_str()
.unwrap();
assert_eq!(SAMPLE0.to_owned() + SAMPLE1, string);
}
{
let space = Space::from_slice(raw_space.as_ref());
let (body, _) = space.split_atom_body(urids.atom.literal).unwrap();
let (info, text) = Literal::read(body, ()).unwrap();
assert_eq!(info, LiteralInfo::Language(urids.german.into_general()));
assert_eq!(text, SAMPLE0.to_owned() + SAMPLE1);
}
}
#[test]
fn test_string() {
let map = HashURIDMapper::new();
let urids = crate::AtomURIDCollection::from_map(&map).unwrap();
let mut raw_space: Box<[u8]> = Box::new([0; 256]);
{
let mut space = RootMutSpace::new(raw_space.as_mut());
let mut writer = (&mut space as &mut dyn MutSpace)
.init(urids.string, ())
.unwrap();
writer.append(SAMPLE0).unwrap();
writer.append(SAMPLE1).unwrap();
}
{
let (string, space) = raw_space.split_at(size_of::<sys::LV2_Atom_String>());
let string = unsafe { &*(string.as_ptr() as *const sys::LV2_Atom_String) };
assert_eq!(string.atom.type_, urids.string);
assert_eq!(string.atom.size as usize, SAMPLE0.len() + SAMPLE1.len() + 1);
let string = std::str::from_utf8(space.split_at(string.atom.size as usize).0).unwrap();
assert_eq!(string[..string.len() - 1], SAMPLE0.to_owned() + SAMPLE1);
}
{
let space = Space::from_slice(raw_space.as_ref());
let (body, _) = space.split_atom_body(urids.string).unwrap();
let string = String::read(body, ()).unwrap();
assert_eq!(string, SAMPLE0.to_owned() + SAMPLE1);
}
}
}