use crate::core::error::{XmpError, XmpResult};
use crate::types::qualifier::Qualifier;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArrayType {
Ordered,
Unordered,
Alternative,
}
impl ArrayType {
pub fn rdf_type(&self) -> &'static str {
match self {
ArrayType::Ordered => "Seq",
ArrayType::Unordered => "Bag",
ArrayType::Alternative => "Alt",
}
}
}
#[derive(Debug, Clone)]
pub struct SimpleNode {
pub value: String,
pub qualifiers: Vec<Qualifier>,
}
impl SimpleNode {
pub fn new(value: impl Into<String>) -> Self {
Self {
value: value.into(),
qualifiers: Vec::new(),
}
}
pub fn add_qualifier(&mut self, qualifier: Qualifier) {
self.qualifiers.push(qualifier);
}
pub fn get_qualifier(&self, namespace: &str, name: &str) -> Option<&Qualifier> {
self.qualifiers
.iter()
.find(|q| q.namespace == namespace && q.name == name)
}
pub fn remove_qualifier(&mut self, namespace: &str, name: &str) -> bool {
let initial_len = self.qualifiers.len();
self.qualifiers
.retain(|q| !(q.namespace == namespace && q.name == name));
self.qualifiers.len() < initial_len
}
}
#[derive(Debug, Clone)]
pub struct ArrayNode {
pub items: Vec<Node>,
pub array_type: ArrayType,
pub qualifiers: Vec<Qualifier>,
}
impl ArrayNode {
pub fn new(array_type: ArrayType) -> Self {
Self {
items: Vec::new(),
array_type,
qualifiers: Vec::new(),
}
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn get(&self, index: usize) -> Option<&Node> {
self.items.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut Node> {
self.items.get_mut(index)
}
pub fn append(&mut self, node: Node) {
self.items.push(node);
}
pub fn insert(&mut self, index: usize, node: Node) -> XmpResult<()> {
if index > self.items.len() {
return Err(XmpError::BadParam(format!(
"Index {} out of bounds for array of length {}",
index,
self.items.len()
)));
}
self.items.insert(index, node);
Ok(())
}
pub fn remove(&mut self, index: usize) -> XmpResult<Node> {
if index >= self.items.len() {
return Err(XmpError::BadParam(format!(
"Index {} out of bounds for array of length {}",
index,
self.items.len()
)));
}
Ok(self.items.remove(index))
}
pub fn add_qualifier(&mut self, qualifier: Qualifier) {
self.qualifiers.push(qualifier);
}
pub fn get_qualifier(&self, namespace: &str, name: &str) -> Option<&Qualifier> {
self.qualifiers
.iter()
.find(|q| q.namespace == namespace && q.name == name)
}
}
#[derive(Debug, Clone)]
pub struct StructureNode {
pub fields: HashMap<String, Node>,
pub qualifiers: Vec<Qualifier>,
}
impl StructureNode {
pub fn new() -> Self {
Self {
fields: HashMap::new(),
qualifiers: Vec::new(),
}
}
pub fn get_field(&self, name: &str) -> Option<&Node> {
self.fields.get(name)
}
pub fn get_field_mut(&mut self, name: &str) -> Option<&mut Node> {
self.fields.get_mut(name)
}
pub fn set_field(&mut self, name: impl Into<String>, node: Node) {
self.fields.insert(name.into(), node);
}
pub fn remove_field(&mut self, name: &str) -> Option<Node> {
self.fields.remove(name)
}
pub fn has_field(&self, name: &str) -> bool {
self.fields.contains_key(name)
}
pub fn field_names(&self) -> impl Iterator<Item = &String> {
self.fields.keys()
}
pub fn add_qualifier(&mut self, qualifier: Qualifier) {
self.qualifiers.push(qualifier);
}
pub fn get_qualifier(&self, namespace: &str, name: &str) -> Option<&Qualifier> {
self.qualifiers
.iter()
.find(|q| q.namespace == namespace && q.name == name)
}
}
impl Default for StructureNode {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum Node {
Simple(SimpleNode),
Array(ArrayNode),
Structure(StructureNode),
}
impl Node {
pub fn simple(value: impl Into<String>) -> Self {
Node::Simple(SimpleNode::new(value))
}
pub fn array(array_type: ArrayType) -> Self {
Node::Array(ArrayNode::new(array_type))
}
pub fn structure() -> Self {
Node::Structure(StructureNode::new())
}
pub fn is_simple(&self) -> bool {
matches!(self, Node::Simple(_))
}
pub fn is_array(&self) -> bool {
matches!(self, Node::Array(_))
}
pub fn is_structure(&self) -> bool {
matches!(self, Node::Structure(_))
}
pub fn as_simple(&self) -> Option<&SimpleNode> {
match self {
Node::Simple(node) => Some(node),
_ => None,
}
}
pub fn as_array(&self) -> Option<&ArrayNode> {
match self {
Node::Array(node) => Some(node),
_ => None,
}
}
pub fn as_structure(&self) -> Option<&StructureNode> {
match self {
Node::Structure(node) => Some(node),
_ => None,
}
}
pub fn as_simple_mut(&mut self) -> Option<&mut SimpleNode> {
match self {
Node::Simple(node) => Some(node),
_ => None,
}
}
pub fn as_array_mut(&mut self) -> Option<&mut ArrayNode> {
match self {
Node::Array(node) => Some(node),
_ => None,
}
}
pub fn as_structure_mut(&mut self) -> Option<&mut StructureNode> {
match self {
Node::Structure(node) => Some(node),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_node() {
let mut node = SimpleNode::new("test");
assert_eq!(node.value, "test");
assert_eq!(node.qualifiers.len(), 0);
let qual = Qualifier::new("http://ns.adobe.com/xap/1.0/", "lang", "en-US");
node.add_qualifier(qual.clone());
assert_eq!(node.qualifiers.len(), 1);
assert_eq!(
node.get_qualifier("http://ns.adobe.com/xap/1.0/", "lang"),
Some(&qual)
);
}
#[test]
fn test_array_node() {
let mut array = ArrayNode::new(ArrayType::Ordered);
assert_eq!(array.len(), 0);
assert!(array.is_empty());
array.append(Node::simple("item1"));
array.append(Node::simple("item2"));
assert_eq!(array.len(), 2);
assert_eq!(
array.get(0).and_then(|n| n.as_simple()).map(|n| &n.value),
Some(&"item1".to_string())
);
assert_eq!(
array.get(1).and_then(|n| n.as_simple()).map(|n| &n.value),
Some(&"item2".to_string())
);
let removed = array.remove(0).unwrap();
assert_eq!(array.len(), 1);
assert_eq!(
removed.as_simple().map(|n| &n.value),
Some(&"item1".to_string())
);
}
#[test]
fn test_structure_node() {
let mut structure = StructureNode::new();
assert!(!structure.has_field("field1"));
structure.set_field("field1", Node::simple("value1"));
assert!(structure.has_field("field1"));
assert_eq!(
structure
.get_field("field1")
.and_then(|n| n.as_simple())
.map(|n| &n.value),
Some(&"value1".to_string())
);
structure.remove_field("field1");
assert!(!structure.has_field("field1"));
}
#[test]
fn test_node_creation() {
let simple = Node::simple("test");
assert!(simple.is_simple());
let array = Node::array(ArrayType::Ordered);
assert!(array.is_array());
let structure = Node::structure();
assert!(structure.is_structure());
}
#[test]
fn test_array_type_rdf() {
assert_eq!(ArrayType::Ordered.rdf_type(), "Seq");
assert_eq!(ArrayType::Unordered.rdf_type(), "Bag");
assert_eq!(ArrayType::Alternative.rdf_type(), "Alt");
}
}