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