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
//! Object- or Map-style atom container. //! //! An [object](type.Object.html) contains a series of atoms which are tagged with a key. This way, //! one can view an atom in an object either as a property of an object (just like in //! object-oriented programming) or as an entry in a URID->Atom map. //! //! When initialized, an object does not contain any items. Instead, you have to push them to the //! end of the object using the [`ObjectWritingFrame`](trait.ObjectWritingFrame.html) trait. Every //! writing frame implements this trait via a blanket implementation and the trait is included in //! the crate's prelude. You can, therefore, act as if the extended methods were normal methods of a //! writing frame. //! //! Reading an object is accomplished by creating an iterator over the properties with the //! [`iter`](type.Object.html#method.iter) method. //! //! An example: //! //! extern crate lv2rs_atom as atom; //! extern crate lv2rs_urid as urid; //! //! use atom::prelude::*; //! use atom::ports::*; //! use urid::{CachedMap, debug::DebugMap}; //! use std::ffi::{CString, CStr}; //! //! pub struct Plugin { //! in_port: AtomInputPort<Object>, //! out_port: AtomOutputPort<Object>, //! urids: CachedMap, //! } //! //! impl Plugin { //! /// Simulated `run` method. //! fn run(&mut self) { //! let my_class_urid = self.urids.map( //! CStr::from_bytes_with_nul(b"https://example.org#MyClass\0").unwrap() //! ); //! let a_urid = self.urids.map( //! CStr::from_bytes_with_nul(b"https://example.org#a\0").unwrap() //! ); //! let b_urid = self.urids.map( //! CStr::from_bytes_with_nul(b"https://example.org#b\0").unwrap() //! ); //! //! // Writing //! { //! // We are writing an object that is an instance of `MyClass`. This information //! // is expressed by passing the URID of `MyClass` as the second parameter. In //! // real plugins, you would describe `MyClass` in a Turtle document. //! let mut frame = //! unsafe { //! self.out_port.write_atom_body( //! &(0, my_class_urid), //! &mut self.urids //! ) //! }.unwrap(); //! //! // Pushing a property requires a key, a context, the parameter for the atom type //! // and a mut reference to the URID map. //! frame.push_property::<i32>(a_urid, 0, &42, &mut self.urids).unwrap(); //! frame.push_property::<f32>(b_urid, 0, &17.0, &mut self.urids).unwrap(); //! } //! //! // Reading //! let object = unsafe { self.in_port.get_atom_body(&mut self.urids) }.unwrap(); //! // We're iterating through the properties. If a property matches our known key, //! // We assert that it has the right value. //! for (header, property_atom) in object.iter() { //! if header.key == a_urid { //! let a: &i32 = unsafe { property_atom.get_body(&mut self.urids) }.unwrap(); //! assert_eq!(42, *a); //! } else if header.key == b_urid { //! let b: &f32 = unsafe { property_atom.get_body(&mut self.urids) }.unwrap(); //! assert_eq!(17.0, *b); //! } else { //! panic!("Unknown property in object!"); //! } //! } //! } //! } //! //! // Getting a debug URID map. //! let mut debug_map = DebugMap::new(); //! let mut urids = unsafe {debug_map.create_cached_map()}; //! //! // Creating the plugin. //! let mut plugin = Plugin { //! in_port: AtomInputPort::new(), //! out_port: AtomOutputPort::new(), //! urids: urids, //! }; //! //! // Creating the atom space. //! let mut atom_space = vec![0u8; 256]; //! let atom = unsafe { (atom_space.as_mut_ptr() as *mut Atom).as_mut() }.unwrap(); //! *(atom.mut_size()) = 256 - 8; //! //! // Connecting the ports. //! plugin.in_port.connect_port(atom as &Atom); //! plugin.out_port.connect_port(atom); //! //! // Calling `run`. //! plugin.run(); use crate::atom::{array::*, *}; use crate::frame::{NestedFrame, WritingFrame, WritingFrameExt}; use crate::uris; use std::ffi::CStr; use urid::URID; /// The header of an object's property. /// /// In original LV2, a property is a standalone atom, but since it is only useful within objects, /// which doesn't need the atom properties of a property, it is not an atom. /// /// The `key` represents the name of the property. The `context` is described by the standard as /// "Context URID (may be, and generally is, 0)". It does not really say what it is used for, /// but since it says that it may be 0, you should set it to 0. /// /// This struct is also `repr(C)` and is used to interpret objects from raw data. #[repr(C)] pub struct PropertyHeader { pub key: URID, pub context: URID, } /// Header of an object. /// /// The important field is `otype`, which contains the URID of the class this object is an instance /// of. However, the `id` is only described as "URID, or 0 for blank" by the standard and therefore /// should be set to zero. /// /// This struct is also `repr(C)` and is used to interpret objects from raw data. #[repr(C)] pub struct ObjectHeader { pub id: URID, pub otype: URID, } impl ArrayAtomHeader for ObjectHeader { type InitializationParameter = (URID, URID); unsafe fn initialize<'a, W, T>( writer: &mut W, (id, otype): &(URID, URID), _urids: &mut urid::CachedMap, ) -> Result<(), ()> where T: 'static + Sized + Copy, ArrayAtomBody<Self, T>: AtomBody, W: WritingFrame<'a> + WritingFrameExt<'a, ArrayAtomBody<Self, T>>, { let header = ObjectHeader { id: *id, otype: *otype, }; writer.write_sized(&header).map(|_| ()) } } /// Object- or Map-style atom container. /// /// See the [module documentation](index.html) for more information. pub type Object = ArrayAtomBody<ObjectHeader, u8>; impl AtomBody for Object { type InitializationParameter = (URID, URID); fn get_uri() -> &'static CStr { unsafe { CStr::from_bytes_with_nul_unchecked(uris::OBJECT_TYPE_URI) } } unsafe fn initialize_body<'a, W>( writer: &mut W, (id, otype): &(URID, URID), urids: &mut urid::CachedMap, ) -> Result<(), ()> where W: WritingFrame<'a> + WritingFrameExt<'a, Self>, { Self::__initialize_body(writer, &(*id, *otype), urids) } fn create_ref<'a>(raw_body: &'a [u8]) -> Result<&'a Self, ()> { Self::__create_ref(raw_body) } } impl Object { /// Create an iterator over all properties of the object. /// /// This iterator is based on the [`ChunkIterator`](../unknown/struct.ChunkIterator.html). pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a PropertyHeader, &'a Atom)> { AtomIterator::<PropertyHeader>::new(&self.data) } } /// Extension for [`WritingFrame`](../frame/trait.WritingFrame.html) and /// [`WritingFrameExt`](../frame/trait.WritingFrameExt.html) for vectors. /// /// See the [module documentation](index.html) for more information. pub trait ObjectWritingFrame<'a>: WritingFrame<'a> + WritingFrameExt<'a, Object> { /// Add a property to the object. /// /// The `key` and the `context` are the same as in the /// [`PropertyHeader`](struct.PropertyHeader.html): The key represents the name of the property /// and the `context`'s purpose is unknown and should be set to zero. fn push_property<'b, A: AtomBody + ?Sized>( &'b mut self, key: URID, context: URID, parameter: &A::InitializationParameter, urids: &mut urid::CachedMap, ) -> Result<NestedFrame<'b, 'a, A>, ()> { let p_header = PropertyHeader { key: key, context: context, }; unsafe { self.write_sized(&p_header)?; let mut frame = self.create_nested_frame::<A>(urids)?; A::initialize_body(&mut frame, parameter, urids)?; Ok(frame) } } } impl<'a, W> ObjectWritingFrame<'a> for W where W: WritingFrame<'a> + WritingFrameExt<'a, Object> {}