lv2rs_atom/
object.rs

1//! Object- or Map-style atom container.
2//!
3//! An [object](type.Object.html) contains a series of atoms which are tagged with a key. This way,
4//! one can view an atom in an object either as a property of an object (just like in
5//! object-oriented programming) or as an entry in a URID->Atom map.
6//!
7//! When initialized, an object does not contain any items. Instead, you have to push them to the
8//! end of the object using the [`ObjectWritingFrame`](trait.ObjectWritingFrame.html) trait. Every
9//! writing frame implements this trait via a blanket implementation and the trait is included in
10//! the crate's prelude. You can, therefore, act as if the extended methods were normal methods of a
11//! writing frame.
12//!
13//! Reading an object is accomplished by creating an iterator over the properties with the
14//! [`iter`](type.Object.html#method.iter) method.
15//!
16//! An example:
17//!
18//!     extern crate lv2rs_atom as atom;
19//!     extern crate lv2rs_urid as urid;
20//!
21//!     use atom::prelude::*;
22//!     use atom::ports::*;
23//!     use urid::{CachedMap, debug::DebugMap};
24//!     use std::ffi::{CString, CStr};
25//!
26//!     pub struct Plugin {
27//!         in_port: AtomInputPort<Object>,
28//!         out_port: AtomOutputPort<Object>,
29//!         urids: CachedMap,
30//!     }
31//!
32//!     impl Plugin {
33//!         /// Simulated `run` method.
34//!         fn run(&mut self) {
35//!             let my_class_urid = self.urids.map(
36//!                 CStr::from_bytes_with_nul(b"https://example.org#MyClass\0").unwrap()
37//!             );
38//!             let a_urid = self.urids.map(
39//!                 CStr::from_bytes_with_nul(b"https://example.org#a\0").unwrap()
40//!             );
41//!             let b_urid = self.urids.map(
42//!                 CStr::from_bytes_with_nul(b"https://example.org#b\0").unwrap()
43//!             );
44//!
45//!             // Writing
46//!             {
47//!                 // We are writing an object that is an instance of `MyClass`. This information
48//!                 // is expressed by passing the URID of `MyClass` as the second parameter. In
49//!                 // real plugins, you would describe `MyClass` in a Turtle document.
50//!                 let mut frame =
51//!                     unsafe {
52//!                         self.out_port.write_atom_body(
53//!                             &(0, my_class_urid),
54//!                             &mut self.urids
55//!                         )
56//!                     }.unwrap();
57//!
58//!                 // Pushing a property requires a key, a context, the parameter for the atom type
59//!                 // and a mut reference to the URID map.
60//!                 frame.push_property::<i32>(a_urid, 0, &42, &mut self.urids).unwrap();
61//!                 frame.push_property::<f32>(b_urid, 0, &17.0, &mut self.urids).unwrap();
62//!             }
63//!
64//!             // Reading
65//!             let object = unsafe { self.in_port.get_atom_body(&mut self.urids) }.unwrap();
66//!             // We're iterating through the properties. If a property matches our known key,
67//!             // We assert that it has the right value.
68//!             for (header, property_atom) in object.iter() {
69//!                 if header.key == a_urid {
70//!                     let a: &i32 = unsafe { property_atom.get_body(&mut self.urids) }.unwrap();
71//!                     assert_eq!(42, *a);
72//!                 } else if header.key == b_urid {
73//!                     let b: &f32 = unsafe { property_atom.get_body(&mut self.urids) }.unwrap();
74//!                     assert_eq!(17.0, *b);
75//!                 } else {
76//!                     panic!("Unknown property in object!");
77//!                 }
78//!             }
79//!         }
80//!     }
81//!
82//!     // Getting a debug URID map.
83//!     let mut debug_map = DebugMap::new();
84//!     let mut urids = unsafe {debug_map.create_cached_map()};
85//!
86//!     // Creating the plugin.
87//!     let mut plugin = Plugin {
88//!         in_port: AtomInputPort::new(),
89//!         out_port: AtomOutputPort::new(),
90//!         urids: urids,
91//!     };
92//!
93//!     // Creating the atom space.
94//!     let mut atom_space = vec![0u8; 256];
95//!     let atom = unsafe { (atom_space.as_mut_ptr() as *mut Atom).as_mut() }.unwrap();
96//!     *(atom.mut_size()) = 256 - 8;
97//!
98//!     // Connecting the ports.
99//!     plugin.in_port.connect_port(atom as &Atom);
100//!     plugin.out_port.connect_port(atom);
101//!
102//!     // Calling `run`.
103//!     plugin.run();
104use crate::atom::{array::*, *};
105use crate::frame::{NestedFrame, WritingFrame, WritingFrameExt};
106use crate::uris;
107use std::ffi::CStr;
108use urid::URID;
109
110/// The header of an object's property.
111///
112/// In original LV2, a property is a standalone atom, but since it is only useful within objects,
113/// which doesn't need the atom properties of a property, it is not an atom.
114///
115/// The `key` represents the name of the property. The `context` is described by the standard as
116/// "Context URID (may be, and generally is, 0)". It does not really say what it is used for,
117/// but since it says that it may be 0, you should set it to 0.
118///
119/// This struct is also `repr(C)` and is used to interpret objects from raw data.
120#[repr(C)]
121pub struct PropertyHeader {
122    pub key: URID,
123    pub context: URID,
124}
125
126/// Header of an object.
127///
128/// The important field is `otype`, which contains the URID of the class this object is an instance
129/// of. However, the `id` is only described as "URID, or 0 for blank" by the standard and therefore
130/// should be set to zero.
131///
132/// This struct is also `repr(C)` and is used to interpret objects from raw data.
133#[repr(C)]
134pub struct ObjectHeader {
135    pub id: URID,
136    pub otype: URID,
137}
138
139impl ArrayAtomHeader for ObjectHeader {
140    type InitializationParameter = (URID, URID);
141
142    unsafe fn initialize<'a, W, T>(
143        writer: &mut W,
144        (id, otype): &(URID, URID),
145        _urids: &mut urid::CachedMap,
146    ) -> Result<(), ()>
147    where
148        T: 'static + Sized + Copy,
149        ArrayAtomBody<Self, T>: AtomBody,
150        W: WritingFrame<'a> + WritingFrameExt<'a, ArrayAtomBody<Self, T>>,
151    {
152        let header = ObjectHeader {
153            id: *id,
154            otype: *otype,
155        };
156        writer.write_sized(&header).map(|_| ())
157    }
158}
159
160/// Object- or Map-style atom container.
161///
162/// See the [module documentation](index.html) for more information.
163pub type Object = ArrayAtomBody<ObjectHeader, u8>;
164
165impl AtomBody for Object {
166    type InitializationParameter = (URID, URID);
167
168    fn get_uri() -> &'static CStr {
169        unsafe { CStr::from_bytes_with_nul_unchecked(uris::OBJECT_TYPE_URI) }
170    }
171
172    unsafe fn initialize_body<'a, W>(
173        writer: &mut W,
174        (id, otype): &(URID, URID),
175        urids: &mut urid::CachedMap,
176    ) -> Result<(), ()>
177    where
178        W: WritingFrame<'a> + WritingFrameExt<'a, Self>,
179    {
180        Self::__initialize_body(writer, &(*id, *otype), urids)
181    }
182
183    fn create_ref<'a>(raw_body: &'a [u8]) -> Result<&'a Self, ()> {
184        Self::__create_ref(raw_body)
185    }
186}
187
188impl Object {
189    /// Create an iterator over all properties of the object.
190    ///
191    /// This iterator is based on the [`ChunkIterator`](../unknown/struct.ChunkIterator.html).
192    pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a PropertyHeader, &'a Atom)> {
193        AtomIterator::<PropertyHeader>::new(&self.data)
194    }
195}
196
197/// Extension for [`WritingFrame`](../frame/trait.WritingFrame.html) and
198/// [`WritingFrameExt`](../frame/trait.WritingFrameExt.html) for vectors.
199///
200/// See the [module documentation](index.html) for more information.
201pub trait ObjectWritingFrame<'a>: WritingFrame<'a> + WritingFrameExt<'a, Object> {
202    /// Add a property to the object.
203    ///
204    /// The `key` and the `context` are the same as in the
205    /// [`PropertyHeader`](struct.PropertyHeader.html): The key represents the name of the property
206    /// and the `context`'s purpose is unknown and should be set to zero.
207    fn push_property<'b, A: AtomBody + ?Sized>(
208        &'b mut self,
209        key: URID,
210        context: URID,
211        parameter: &A::InitializationParameter,
212        urids: &mut urid::CachedMap,
213    ) -> Result<NestedFrame<'b, 'a, A>, ()> {
214        let p_header = PropertyHeader {
215            key: key,
216            context: context,
217        };
218        unsafe {
219            self.write_sized(&p_header)?;
220            let mut frame = self.create_nested_frame::<A>(urids)?;
221            A::initialize_body(&mut frame, parameter, urids)?;
222            Ok(frame)
223        }
224    }
225}
226
227impl<'a, W> ObjectWritingFrame<'a> for W where W: WritingFrame<'a> + WritingFrameExt<'a, Object> {}