use crate::structured_storage::StorageReader;
use crate::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::io::{Read, Seek};
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Auid([u8; 16]);
impl Auid {
#[must_use]
pub fn from_bytes(bytes: &[u8; 16]) -> Self {
Self(*bytes)
}
#[must_use]
pub fn from_uuid(uuid: Uuid) -> Self {
Self(*uuid.as_bytes())
}
#[must_use]
pub fn to_uuid(&self) -> Uuid {
Uuid::from_bytes(self.0)
}
#[must_use]
pub fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
#[must_use]
pub fn null() -> Self {
Self([0u8; 16])
}
#[must_use]
pub fn is_null(&self) -> bool {
self.0.iter().all(|&b| b == 0)
}
#[must_use]
pub fn is_picture(&self) -> bool {
*self == Self::PICTURE || *self == Self::PICTURE_WITH_MATTE
}
#[must_use]
pub fn is_sound(&self) -> bool {
*self == Self::SOUND
}
#[must_use]
pub fn is_timecode(&self) -> bool {
*self == Self::TIMECODE || *self == Self::EDGECODE
}
pub const PICTURE: Auid = Auid([
0x01, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01,
0x01,
]);
pub const SOUND: Auid = Auid([
0x01, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01,
0x01,
]);
pub const TIMECODE: Auid = Auid([
0x01, 0x03, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01,
0x01,
]);
pub const EDGECODE: Auid = Auid([
0x01, 0x03, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01,
0x01,
]);
pub const PICTURE_WITH_MATTE: Auid = Auid([
0x05, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01,
0x01,
]);
pub const AUXILIARY: Auid = Auid([
0x01, 0x03, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01,
0x01,
]);
pub const DATA: Auid = Auid([
0x01, 0x03, 0x02, 0x03, 0x02, 0x00, 0x00, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01,
0x01,
]);
pub const CLASS_HEADER: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x2f, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_COMPOSITION_MOB: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_MASTER_MOB: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x32, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_SOURCE_MOB: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x33, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_TIMELINE_MOB_SLOT: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x37, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_SEQUENCE: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0f, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_SOURCE_CLIP: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_FILLER: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x09, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_TRANSITION: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x12, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
pub const CLASS_OPERATION_GROUP: Auid = Auid([
0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0a, 0x00, 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x06, 0x01,
0x01,
]);
}
impl fmt::Display for Auid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_uuid())
}
}
impl Default for Auid {
fn default() -> Self {
Self::null()
}
}
impl From<Uuid> for Auid {
fn from(uuid: Uuid) -> Self {
Self::from_uuid(uuid)
}
}
impl From<Auid> for Uuid {
fn from(auid: Auid) -> Self {
auid.to_uuid()
}
}
#[derive(Debug, Clone)]
pub struct Dictionary {
classes: HashMap<Auid, ClassDefinition>,
properties: HashMap<Auid, PropertyDefinition>,
types: HashMap<Auid, TypeDefinition>,
data_definitions: HashMap<Auid, DataDefinition>,
}
impl Dictionary {
#[must_use]
pub fn new() -> Self {
let mut dict = Self {
classes: HashMap::new(),
properties: HashMap::new(),
types: HashMap::new(),
data_definitions: HashMap::new(),
};
dict.add_baseline_types();
dict.add_baseline_classes();
dict.add_baseline_properties();
dict.add_baseline_data_definitions();
dict
}
pub fn add_class(&mut self, class: ClassDefinition) {
self.classes.insert(class.auid, class);
}
pub fn add_property(&mut self, property: PropertyDefinition) {
self.properties.insert(property.auid, property);
}
pub fn add_type(&mut self, type_def: TypeDefinition) {
self.types.insert(type_def.auid, type_def);
}
pub fn add_data_definition(&mut self, data_def: DataDefinition) {
self.data_definitions.insert(data_def.auid, data_def);
}
#[must_use]
pub fn get_class(&self, auid: &Auid) -> Option<&ClassDefinition> {
self.classes.get(auid)
}
#[must_use]
pub fn get_property(&self, auid: &Auid) -> Option<&PropertyDefinition> {
self.properties.get(auid)
}
#[must_use]
pub fn get_type(&self, auid: &Auid) -> Option<&TypeDefinition> {
self.types.get(auid)
}
#[must_use]
pub fn get_data_definition(&self, auid: &Auid) -> Option<&DataDefinition> {
self.data_definitions.get(auid)
}
#[must_use]
pub fn get_class_by_name(&self, name: &str) -> Option<&ClassDefinition> {
self.classes.values().find(|c| c.name == name)
}
#[must_use]
pub fn get_property_by_name(&self, name: &str) -> Option<&PropertyDefinition> {
self.properties.values().find(|p| p.name == name)
}
#[must_use]
pub fn get_type_by_name(&self, name: &str) -> Option<&TypeDefinition> {
self.types.values().find(|t| t.name == name)
}
fn add_baseline_types(&mut self) {
let types = vec![
TypeDefinition::new(Auid::null(), "Boolean", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "Int8", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "UInt8", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "Int16", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "UInt16", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "Int32", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "UInt32", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "Int64", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "UInt64", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "Float", TypeKind::Float),
TypeDefinition::new(Auid::null(), "Double", TypeKind::Float),
TypeDefinition::new(Auid::null(), "String", TypeKind::String),
TypeDefinition::new(Auid::null(), "AUID", TypeKind::Record),
TypeDefinition::new(Auid::null(), "MobID", TypeKind::Record),
TypeDefinition::new(Auid::null(), "Position", TypeKind::Integer),
TypeDefinition::new(Auid::null(), "Length", TypeKind::Integer),
];
for type_def in types {
self.types.insert(type_def.auid, type_def);
}
}
fn add_baseline_classes(&mut self) {
let classes = vec![
ClassDefinition::new(Auid::CLASS_HEADER, "Header", None),
ClassDefinition::new(Auid::CLASS_COMPOSITION_MOB, "CompositionMob", None),
ClassDefinition::new(Auid::CLASS_MASTER_MOB, "MasterMob", None),
ClassDefinition::new(Auid::CLASS_SOURCE_MOB, "SourceMob", None),
ClassDefinition::new(Auid::CLASS_TIMELINE_MOB_SLOT, "TimelineMobSlot", None),
ClassDefinition::new(Auid::CLASS_SEQUENCE, "Sequence", None),
ClassDefinition::new(Auid::CLASS_SOURCE_CLIP, "SourceClip", None),
ClassDefinition::new(Auid::CLASS_FILLER, "Filler", None),
ClassDefinition::new(Auid::CLASS_TRANSITION, "Transition", None),
ClassDefinition::new(Auid::CLASS_OPERATION_GROUP, "OperationGroup", None),
];
for class in classes {
self.classes.insert(class.auid, class);
}
}
fn add_baseline_properties(&mut self) {
}
fn add_baseline_data_definitions(&mut self) {
let data_defs = vec![
DataDefinition::new(Auid::PICTURE, "Picture"),
DataDefinition::new(Auid::SOUND, "Sound"),
DataDefinition::new(Auid::TIMECODE, "Timecode"),
DataDefinition::new(Auid::EDGECODE, "Edgecode"),
];
for data_def in data_defs {
self.data_definitions.insert(data_def.auid, data_def);
}
}
}
impl Default for Dictionary {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ClassDefinition {
pub auid: Auid,
pub name: String,
pub parent: Option<Auid>,
pub properties: Vec<Auid>,
pub is_concrete: bool,
}
impl ClassDefinition {
pub fn new(auid: Auid, name: impl Into<String>, parent: Option<Auid>) -> Self {
Self {
auid,
name: name.into(),
parent,
properties: Vec::new(),
is_concrete: true,
}
}
pub fn add_property(&mut self, property_auid: Auid) {
if !self.properties.contains(&property_auid) {
self.properties.push(property_auid);
}
}
#[must_use]
pub fn has_property(&self, property_auid: &Auid) -> bool {
self.properties.contains(property_auid)
}
}
#[derive(Debug, Clone)]
pub struct PropertyDefinition {
pub auid: Auid,
pub name: String,
pub type_auid: Auid,
pub is_optional: bool,
pub local_id: Option<u16>,
}
impl PropertyDefinition {
pub fn new(auid: Auid, name: impl Into<String>, type_auid: Auid) -> Self {
Self {
auid,
name: name.into(),
type_auid,
is_optional: false,
local_id: None,
}
}
#[must_use]
pub fn with_optional(mut self, optional: bool) -> Self {
self.is_optional = optional;
self
}
#[must_use]
pub fn with_local_id(mut self, local_id: u16) -> Self {
self.local_id = Some(local_id);
self
}
}
#[derive(Debug, Clone)]
pub struct TypeDefinition {
pub auid: Auid,
pub name: String,
pub kind: TypeKind,
}
impl TypeDefinition {
pub fn new(auid: Auid, name: impl Into<String>, kind: TypeKind) -> Self {
Self {
auid,
name: name.into(),
kind,
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeKind {
Integer,
Float,
String,
Record,
Enum,
FixedArray,
VariableArray,
Set,
StrongRef,
WeakRef,
Opaque,
}
#[derive(Debug, Clone)]
pub struct DataDefinition {
pub auid: Auid,
pub name: String,
pub description: Option<String>,
}
impl DataDefinition {
pub fn new(auid: Auid, name: impl Into<String>) -> Self {
Self {
auid,
name: name.into(),
description: None,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
}
pub fn read_dictionary<R: Read + Seek>(_storage: &mut StorageReader<R>) -> Result<Dictionary> {
Ok(Dictionary::new())
}
pub struct DictionaryBuilder {
dictionary: Dictionary,
}
impl DictionaryBuilder {
#[must_use]
pub fn new() -> Self {
Self {
dictionary: Dictionary::new(),
}
}
pub fn add_custom_class(
mut self,
auid: Auid,
name: impl Into<String>,
parent: Option<Auid>,
) -> Self {
let class = ClassDefinition::new(auid, name, parent);
self.dictionary.add_class(class);
self
}
pub fn add_custom_property(
mut self,
auid: Auid,
name: impl Into<String>,
type_auid: Auid,
) -> Self {
let property = PropertyDefinition::new(auid, name, type_auid);
self.dictionary.add_property(property);
self
}
pub fn add_custom_type(mut self, auid: Auid, name: impl Into<String>, kind: TypeKind) -> Self {
let type_def = TypeDefinition::new(auid, name, kind);
self.dictionary.add_type(type_def);
self
}
#[must_use]
pub fn build(self) -> Dictionary {
self.dictionary
}
}
impl Default for DictionaryBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auid_creation() {
let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let auid = Auid::from_bytes(&bytes);
assert_eq!(auid.as_bytes(), &bytes);
}
#[test]
fn test_auid_null() {
let auid = Auid::null();
assert!(auid.is_null());
}
#[test]
fn test_auid_uuid_conversion() {
let uuid = Uuid::new_v4();
let auid = Auid::from_uuid(uuid);
assert_eq!(auid.to_uuid(), uuid);
}
#[test]
fn test_auid_data_definitions() {
assert!(Auid::PICTURE.is_picture());
assert!(Auid::SOUND.is_sound());
assert!(Auid::TIMECODE.is_timecode());
}
#[test]
fn test_dictionary_creation() {
let dict = Dictionary::new();
assert!(!dict.classes.is_empty());
assert!(!dict.types.is_empty());
}
#[test]
fn test_dictionary_get_class() {
let dict = Dictionary::new();
let class = dict.get_class(&Auid::CLASS_HEADER);
assert!(class.is_some());
assert_eq!(class.expect("test expectation failed").name, "Header");
}
#[test]
fn test_dictionary_get_class_by_name() {
let dict = Dictionary::new();
let class = dict.get_class_by_name("CompositionMob");
assert!(class.is_some());
assert_eq!(
class.expect("test expectation failed").auid,
Auid::CLASS_COMPOSITION_MOB
);
}
#[test]
fn test_class_definition() {
let mut class = ClassDefinition::new(Auid::null(), "TestClass", None);
assert_eq!(class.name, "TestClass");
assert!(class.is_concrete);
let prop_auid = Auid::null();
class.add_property(prop_auid);
assert!(class.has_property(&prop_auid));
}
#[test]
fn test_property_definition() {
let prop = PropertyDefinition::new(Auid::null(), "TestProp", Auid::null())
.with_optional(true)
.with_local_id(0x1234);
assert_eq!(prop.name, "TestProp");
assert!(prop.is_optional);
assert_eq!(prop.local_id, Some(0x1234));
}
#[test]
fn test_type_definition() {
let type_def = TypeDefinition::new(Auid::null(), "TestType", TypeKind::Integer);
assert_eq!(type_def.name(), "TestType");
assert_eq!(type_def.kind, TypeKind::Integer);
}
#[test]
fn test_dictionary_builder() {
let dict = DictionaryBuilder::new()
.add_custom_class(Auid::null(), "CustomClass", None)
.add_custom_property(Auid::null(), "CustomProp", Auid::null())
.add_custom_type(Auid::null(), "CustomType", TypeKind::String)
.build();
assert!(dict.get_class_by_name("CustomClass").is_some());
}
}