use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
pub type TraitId = Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Trait {
pub id: TraitId,
pub name: String,
pub version: u32,
pub data: TraitData,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TraitData {
String(String),
Number(f64),
Boolean(bool),
Object(HashMap<String, serde_json::Value>),
Array(Vec<serde_json::Value>),
Binary(Vec<u8>),
}
impl Trait {
#[inline]
pub fn new(name: impl Into<String>, data: TraitData) -> Self {
Self {
id: Uuid::new_v4(),
name: name.into(),
version: 1,
data,
metadata: HashMap::new(),
}
}
pub fn with_metadata(
name: impl Into<String>,
data: TraitData,
metadata: HashMap<String, String>,
) -> Self {
Self {
id: Uuid::new_v4(),
name: name.into(),
version: 1,
data,
metadata,
}
}
pub fn with_capacity(
name: impl Into<String>,
data: TraitData,
metadata_capacity: usize,
) -> Self {
Self {
id: Uuid::new_v4(),
name: name.into(),
version: 1,
data,
metadata: HashMap::with_capacity(metadata_capacity),
}
}
#[inline]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
pub fn data(&self) -> &TraitData {
&self.data
}
#[inline]
pub fn data_mut(&mut self) -> &mut TraitData {
&mut self.data
}
#[inline]
pub fn id(&self) -> TraitId {
self.id
}
#[inline]
pub fn version(&self) -> u32 {
self.version
}
#[inline]
pub fn get_metadata(&self, key: &str) -> Option<&String> {
self.metadata.get(key)
}
#[inline]
pub fn set_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.metadata.insert(key.into(), value.into());
}
pub fn new_version(&self, data: TraitData) -> Self {
Self {
id: Uuid::new_v4(),
name: self.name.clone(),
version: self.version + 1,
data,
metadata: self.metadata.clone(),
}
}
#[inline]
pub fn metadata_count(&self) -> usize {
self.metadata.len()
}
#[inline]
pub fn reserve_metadata(&mut self, additional: usize) {
self.metadata.reserve(additional);
}
#[inline]
pub fn clear_metadata(&mut self) {
self.metadata.clear();
}
}
impl TraitData {
pub fn is_string(&self) -> bool {
matches!(self, TraitData::String(_))
}
pub fn is_number(&self) -> bool {
matches!(self, TraitData::Number(_))
}
pub fn is_boolean(&self) -> bool {
matches!(self, TraitData::Boolean(_))
}
pub fn is_object(&self) -> bool {
matches!(self, TraitData::Object(_))
}
pub fn is_array(&self) -> bool {
matches!(self, TraitData::Array(_))
}
pub fn is_binary(&self) -> bool {
matches!(self, TraitData::Binary(_))
}
pub fn as_string(&self) -> Option<&String> {
match self {
TraitData::String(s) => Some(s),
_ => None,
}
}
pub fn as_number(&self) -> Option<f64> {
match self {
TraitData::Number(n) => Some(*n),
_ => None,
}
}
pub fn as_boolean(&self) -> Option<bool> {
match self {
TraitData::Boolean(b) => Some(*b),
_ => None,
}
}
pub fn as_object(&self) -> Option<&HashMap<String, serde_json::Value>> {
match self {
TraitData::Object(o) => Some(o),
_ => None,
}
}
pub fn as_array(&self) -> Option<&Vec<serde_json::Value>> {
match self {
TraitData::Array(a) => Some(a),
_ => None,
}
}
pub fn as_binary(&self) -> Option<&Vec<u8>> {
match self {
TraitData::Binary(b) => Some(b),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_trait_creation() {
let trait_data = TraitData::String("test".to_string());
let trait_obj = Trait::new("test_trait", trait_data);
assert_eq!(trait_obj.name(), "test_trait");
assert_eq!(trait_obj.version, 1);
assert!(trait_obj.data.is_string());
}
#[test]
fn test_trait_data_methods() {
let string_data = TraitData::String("hello".to_string());
let number_data = TraitData::Number(42.0);
let bool_data = TraitData::Boolean(true);
assert!(string_data.is_string());
assert!(number_data.is_number());
assert!(bool_data.is_boolean());
assert_eq!(string_data.as_string(), Some(&"hello".to_string()));
assert_eq!(number_data.as_number(), Some(42.0));
assert_eq!(bool_data.as_boolean(), Some(true));
}
#[test]
fn test_trait_metadata() {
let mut trait_obj = Trait::new("test", TraitData::String("value".to_string()));
trait_obj.set_metadata("key", "value");
assert_eq!(trait_obj.get_metadata("key"), Some(&"value".to_string()));
assert_eq!(trait_obj.get_metadata("nonexistent"), None);
}
}