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
228
229
230
231
232
233
234
235
236
237
238
239
240
//! This crate provides a core ebml specification that is used by the ebml-iterable crate.
//!
//! The related ebml-iterable-specification-derive crate can be used to simplify implementation of this spec.
//!

///
/// Contains an empty specification for use with examples or very basic testing.
///
pub mod empty_spec;

///
/// Different data types defined in the EBML specification.
///
/// # Notes
///
/// This library made a concious decision to not work with "Date" elements from EBML due to lack of built-in support for dates in Rust. Specification implementations should treat Date elements as Binary so that consumers have the option of parsing the unaltered data using their library of choice, if needed.
///

// Possible future feature flag to enable Date functionality by having `chrono` as an optional dependency?
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum TagDataType {
    Master,
    UnsignedInt,
    Integer,
    Utf8,
    Binary,
    Float,
}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum PathPart {
    Id(u64),
    Global((Option<u64>,Option<u64>)),
}

///
/// This trait, along with [`EbmlTag`], should be implemented to define a specification so that EBML can be parsed correctly.  Typically implemented on an Enum of tag variants.
///
/// Any specification using EBML can take advantage of this library to parse or write binary data.  As stated in the docs, [`TagWriter`](https://docs.rs/ebml-iterable/latest/ebml_iterable/struct.TagWriter.html) needs nothing special if you stick with the `write_raw` method, but [`TagIterator`](https://docs.rs/ebml-iterable/latest/ebml_iterable/struct.TagIterator.html) requires a struct implementing this trait.  Custom specification implementations can refer to [webm-iterable](https://crates.io/crates/webm_iterable) as an example.
///
/// This trait and [`EbmlTag`] are typically implemented simultaneously.  They are separate traits as they have primarily different uses - [`EbmlSpecification`] should be brought into scope when dealing with the specification as a whole, whereas [`EbmlTag`] should be brought into scope when dealing with specific tags.
///

pub trait EbmlSpecification<T: EbmlSpecification<T> + EbmlTag<T> + Clone> {
    ///
    /// Pulls the data type for a tag from the spec, based on the tag id.
    ///
    /// This function *must* return [`None`] if the input id is not in the specification.  Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_tag_data_type(id: u64) -> Option<TagDataType>;

    ///
    /// Gets the id of a specific tag variant.
    ///
    /// Default implementation uses the [`EbmlTag`] implementation. Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_tag_id(item: &T) -> u64 {
        item.get_id()
    }

    ///
    /// Gets the schema path of a specific tag.
    /// 
    /// This function is used to find the schema defined path of a tag.  If the tag is a root element, this function should return an empty array.
    /// 
    fn get_path_by_id(id: u64) -> &'static [PathPart];

    ///
    /// Gets the schema path of a specific tag variant.
    /// 
    /// Default implementation uses [`Self::get_path_by_id`] after obtaining the tag id using the [`EbmlTag`] implementation.
    /// 
    fn get_path_by_tag(item: &T) -> &'static [PathPart] {
        Self::get_path_by_id(item.get_id())
    }

    ///
    /// Creates an unsigned integer type tag from the spec.
    ///
    /// This function *must* return `None` if the input id is not in the specification or if the input id data type is not [`TagDataType::UnsignedInt`]. Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_unsigned_int_tag(id: u64, data: u64) -> Option<T>;

    ///
    /// Creates a signed integer type tag from the spec.
    ///
    /// This function *must* return `None` if the input id is not in the specification or if the input id data type is not [`TagDataType::Integer`]. Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_signed_int_tag(id: u64, data: i64) -> Option<T>;

    ///
    /// Creates a utf8 type tag from the spec.
    ///
    /// This function *must* return `None` if the input id is not in the specification or if the input id data type is not [`TagDataType::Utf8`]. Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_utf8_tag(id: u64, data: String) -> Option<T>;

    ///
    /// Creates a binary type tag from the spec.
    ///
    /// This function *must* return `None` if the input id is not in the specification or if the input id data type is not [`TagDataType::Binary`]. Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_binary_tag(id: u64, data: &[u8]) -> Option<T>;

    ///
    /// Creates a float type tag from the spec.
    ///
    /// This function *must* return `None` if the input id is not in the specification or if the input id data type is not [`TagDataType::Float`]. Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_float_tag(id: u64, data: f64) -> Option<T>;

    ///
    /// Creates a master type tag from the spec.
    ///
    /// This function *must* return `None` if the input id is not in the specification or if the input id data type is not [`TagDataType::Master`]. Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_master_tag(id: u64, data: Master<T>) -> Option<T>;

    ///
    /// Creates a tag that does not conform to the spec.
    ///
    /// This function should return a "RawTag" variant that contains the tag id and tag data.  Tag data should only be retrievable as binary data. Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_raw_tag(id: u64, data: &[u8]) -> T;

}

///
/// This trait, along with [`EbmlSpecification`], should be implemented to define a specification so that EBML can be parsed correctly.  Typically implemented on an Enum of tag variants.
///
/// Any specification using EBML can take advantage of this library to parse or write binary data.  As stated in the docs, [`TagWriter`](https://docs.rs/ebml-iterable/latest/ebml_iterable/struct.TagWriter.html) needs nothing special if you stick with the `write_raw` method, but [`TagIterator`](https://docs.rs/ebml-iterable/latest/ebml_iterable/struct.TagIterator.html) requires a struct implementing this trait.  Custom specification implementations can refer to [webm-iterable](https://crates.io/crates/webm_iterable) as an example.
///
/// This trait and [`EbmlSpecification`] are typically implemented simultaneously.  They are separate traits as they have primarily different uses - [`EbmlSpecification`] should be brought into scope when dealing with the specification as a whole, whereas [`EbmlTag`] should be brought into scope when dealing with specific tags.
///

pub trait EbmlTag<T: Clone> {
    ///
    /// Gets the id of `self`.
    ///
    /// Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn get_id(&self) -> u64;

    ///
    /// Gets a reference to the data contained in `self` as an unsigned integer.
    ///
    /// This function *must* return `None` if the associated data type of `self` is not [`TagDataType::UnsignedInt`].  Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn as_unsigned_int(&self) -> Option<&u64>;

    ///
    /// Gets a reference to the data contained in `self` as an integer.
    ///
    /// This function *must* return `None` if the associated data type of `self` is not [`TagDataType::Integer`].  Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn as_signed_int(&self) -> Option<&i64>;

    ///
    /// Gets a reference to the data contained in `self` as string slice.
    ///
    /// This function *must* return `None` if the associated data type of `self` is not [`TagDataType::Utf8`].  Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn as_utf8(&self) -> Option<&str>;

    ///
    /// Gets a reference to the data contained in `self` as binary data.
    ///
    /// This function *must* return `None` if the associated data type of `self` is not [`TagDataType::Binary`].  Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn as_binary(&self) -> Option<&[u8]>;

    ///
    /// Gets a reference to the data contained in `self` as float data.
    ///
    /// This function *must* return `None` if the associated data type of `self` is not [`TagDataType::Float`].  Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn as_float(&self) -> Option<&f64>;

    ///
    /// Gets a reference to master data contained in `self`.
    ///
    /// This function *must* return `None` if the associated data type of `self` is not [`TagDataType::Master`].  Implementors can reference [webm-iterable](https://crates.io/crates/webm_iterable) for an example.
    ///
    fn as_master(&self) -> Option<&Master<T>>;
}

///
/// An enum that defines different possible states of a [`TagDataType::Master`] tag.
///
/// A "master" tag is a type of tag that contains other tags within it.  Because these tags are dynamically sized, the [`TagIterator`](https://docs.rs/ebml-iterable/latest/ebml_iterable/struct.TagIterator.html) emits these tags as [`Master::Start`] and [`Master::End`] variants by default so that the entire tag does not need to be buffered into memory all at once.  The [`Master::Full`] variant is a complete "master" tag that includes all child tags within it.
///
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Master<T: Clone> {

    ///
    /// Designates the start of a tag.
    ///
    Start,

    ///
    /// Designates the end of a tag.
    ///
    End,

    ///
    /// Designates a full tag.  `Vec<T>` contains all child tags contained in this master tag.
    ///
    Full(Vec<T>),
}

impl<T: Clone> Master<T> {

    ///
    /// Convenience method to pull children from a master tag.
    ///
    /// # Panics
    ///
    /// Panics if `self` is not a `Full` variant.
    ///
    /// # Examples
    ///
    /// ```
    /// # use ebml_iterable_specification::empty_spec::EmptySpec;
    /// use ebml_iterable_specification::Master;
    ///
    /// let children = vec![EmptySpec::with_data(0x1253, &[1]), EmptySpec::with_data(0x1234, &[2])];
    /// // Clone children because creating a Master consumes it
    /// let tag = Master::Full(children.clone());
    /// let retrieved_children = tag.get_children();
    /// assert_eq!(retrieved_children, children);
    /// ```
    ///
    pub fn get_children(self) -> Vec<T> {
        match self {
            Master::Full(data) => data,
            Master::Start => panic!("`get_children` called on Master::Start variant"),
            Master::End => panic!("`get_children` called on Master::End variant"),
        }
    }
}