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> {}