use crate::error::{Error, Result};
use chrono::{DateTime, Utc};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum AgentType {
Person,
Organization,
EcologicalAgent,
}
impl Default for AgentType {
fn default() -> Self {
AgentType::Person
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct Agent {
pub id: String,
pub agent_type: AgentType,
pub name: String,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub image: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub note: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub primary_location: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub classified_as: Vec<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl Agent {
pub fn builder() -> AgentBuilder {
AgentBuilder::default()
}
pub fn is_person(&self) -> bool {
matches!(self.agent_type, AgentType::Person)
}
pub fn is_organization(&self) -> bool {
matches!(self.agent_type, AgentType::Organization)
}
pub fn is_ecological(&self) -> bool {
matches!(self.agent_type, AgentType::EcologicalAgent)
}
}
#[derive(Debug, Default)]
pub struct AgentBuilder {
id: Option<String>,
agent_type: Option<AgentType>,
name: Option<String>,
image: Option<String>,
note: Option<String>,
primary_location: Option<String>,
classified_as: Vec<String>,
}
impl AgentBuilder {
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn agent_type(mut self, agent_type: AgentType) -> Self {
self.agent_type = Some(agent_type);
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn image(mut self, image: impl Into<String>) -> Self {
self.image = Some(image.into());
self
}
pub fn note(mut self, note: impl Into<String>) -> Self {
self.note = Some(note.into());
self
}
pub fn primary_location(mut self, location: impl Into<String>) -> Self {
self.primary_location = Some(location.into());
self
}
pub fn classified_as(mut self, classification: impl Into<String>) -> Self {
self.classified_as.push(classification.into());
self
}
pub fn build(self) -> Result<Agent> {
let id = self.id.ok_or_else(|| Error::missing_field("id"))?;
let name = self.name.ok_or_else(|| Error::missing_field("name"))?;
let agent_type = self.agent_type.unwrap_or_default();
let now = Utc::now();
Ok(Agent {
id,
agent_type,
name,
image: self.image,
note: self.note,
primary_location: self.primary_location,
classified_as: self.classified_as,
created_at: now,
updated_at: now,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum AgentRelationshipType {
MemberOf,
EmployedBy,
SupplierOf,
CustomerOf,
PartnerOf,
SubsidiaryOf,
AffiliateOf,
Custom(String),
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct AgentRelationship {
pub id: String,
pub subject: String,
pub object: String,
pub relationship_type: AgentRelationshipType,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub note: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub in_scope_of: Option<String>,
pub created_at: DateTime<Utc>,
}
impl AgentRelationship {
pub fn builder() -> AgentRelationshipBuilder {
AgentRelationshipBuilder::default()
}
}
#[derive(Debug, Default)]
pub struct AgentRelationshipBuilder {
id: Option<String>,
subject: Option<String>,
object: Option<String>,
relationship_type: Option<AgentRelationshipType>,
note: Option<String>,
in_scope_of: Option<String>,
}
impl AgentRelationshipBuilder {
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn subject(mut self, agent_id: impl Into<String>) -> Self {
self.subject = Some(agent_id.into());
self
}
pub fn object(mut self, agent_id: impl Into<String>) -> Self {
self.object = Some(agent_id.into());
self
}
pub fn relationship_type(mut self, rel_type: AgentRelationshipType) -> Self {
self.relationship_type = Some(rel_type);
self
}
pub fn note(mut self, note: impl Into<String>) -> Self {
self.note = Some(note.into());
self
}
pub fn in_scope_of(mut self, scope: impl Into<String>) -> Self {
self.in_scope_of = Some(scope.into());
self
}
pub fn build(self) -> Result<AgentRelationship> {
let id = self.id.ok_or_else(|| Error::missing_field("id"))?;
let subject = self
.subject
.ok_or_else(|| Error::missing_field("subject"))?;
let object = self.object.ok_or_else(|| Error::missing_field("object"))?;
let relationship_type = self
.relationship_type
.ok_or_else(|| Error::missing_field("relationship_type"))?;
Ok(AgentRelationship {
id,
subject,
object,
relationship_type,
note: self.note,
in_scope_of: self.in_scope_of,
created_at: Utc::now(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_agent_builder() {
let agent = Agent::builder()
.id("agent-001")
.name("Test Person")
.agent_type(AgentType::Person)
.note("A test agent")
.build()
.unwrap();
assert_eq!(agent.id, "agent-001");
assert_eq!(agent.name, "Test Person");
assert!(agent.is_person());
assert_eq!(agent.note, Some("A test agent".to_string()));
}
#[test]
fn test_agent_builder_missing_required() {
let result = Agent::builder().id("test").build();
assert!(result.is_err());
}
#[test]
fn test_agent_relationship() {
let rel = AgentRelationship::builder()
.id("rel-001")
.subject("agent-001")
.object("agent-002")
.relationship_type(AgentRelationshipType::MemberOf)
.build()
.unwrap();
assert_eq!(rel.subject, "agent-001");
assert_eq!(rel.object, "agent-002");
}
#[cfg(feature = "serde")]
#[test]
fn test_agent_serialization() {
let agent = Agent::builder()
.id("agent-001")
.name("Test")
.agent_type(AgentType::Organization)
.build()
.unwrap();
let json = serde_json::to_string(&agent).unwrap();
let parsed: Agent = serde_json::from_str(&json).unwrap();
assert_eq!(agent.id, parsed.id);
}
}