use crate::{
Agent, AgentId, DataError, Fingerprint, ObjectType, Validate, ValidationError, emit_error,
};
use core::fmt;
use iri_string::types::{IriStr, IriString};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::hash::{Hash, Hasher};
#[skip_serializing_none]
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct ContextAgent {
#[serde(default = "default_object_type")]
object_type: ObjectType,
agent: Agent,
relevant_types: Vec<IriString>,
}
#[skip_serializing_none]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ContextAgentId {
object_type: ObjectType,
agent: AgentId,
relevant_types: Vec<IriString>,
}
impl From<ContextAgent> for ContextAgentId {
fn from(value: ContextAgent) -> Self {
ContextAgentId {
object_type: ObjectType::ContextAgent,
agent: AgentId::from(value.agent),
relevant_types: value.relevant_types,
}
}
}
impl From<ContextAgentId> for ContextAgent {
fn from(value: ContextAgentId) -> Self {
ContextAgent {
object_type: ObjectType::ContextAgent,
agent: Agent::from(value.agent),
relevant_types: value.relevant_types,
}
}
}
impl ContextAgent {
pub fn builder() -> ContextAgentBuilder {
ContextAgentBuilder::default()
}
pub fn check_object_type(&self) -> bool {
self.object_type == ObjectType::ContextAgent
}
pub fn agent(&self) -> &Agent {
&self.agent
}
pub fn relevant_types(&self) -> &[IriString] {
self.relevant_types.as_ref()
}
}
impl Fingerprint for ContextAgent {
fn fingerprint<H: Hasher>(&self, state: &mut H) {
self.agent.fingerprint(state);
for s in &self.relevant_types {
s.hash(state)
}
}
}
impl fmt::Display for ContextAgent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut vec = vec![];
vec.push(format!("agent: {}", self.agent));
vec.push(format!(
"relevantTypes: [{}]",
self.relevant_types
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")
));
let res = vec
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ");
write!(f, "ContextAgent{{ {res} }}")
}
}
impl Validate for ContextAgent {
fn validate(&self) -> Vec<ValidationError> {
let mut vec = vec![];
if !self.check_object_type() {
vec.push(ValidationError::WrongObjectType {
expected: ObjectType::ContextAgent,
found: self.object_type.to_string().into(),
})
}
vec.extend(self.agent.validate());
if self.relevant_types.is_empty() {
vec.push(ValidationError::Empty("relevant_types".into()))
} else {
for iri in self.relevant_types.iter() {
if iri.is_empty() {
vec.push(ValidationError::InvalidIRI(iri.to_string().into()))
}
}
}
vec
}
}
#[derive(Debug, Default)]
pub struct ContextAgentBuilder {
_agent: Option<Agent>,
_relevant_types: Vec<IriString>,
}
impl ContextAgentBuilder {
pub fn agent(mut self, val: Agent) -> Result<Self, DataError> {
val.check_validity()?;
self._agent = Some(val);
Ok(self)
}
pub fn relevant_type(mut self, val: &str) -> Result<Self, DataError> {
if val.is_empty() {
emit_error!(DataError::Validation(ValidationError::Empty(
"relevant-type IRI".into()
)))
} else {
let iri = IriStr::new(val)?;
if self._relevant_types.is_empty() {
self._relevant_types = vec![];
}
self._relevant_types.push(iri.to_owned());
Ok(self)
}
}
pub fn build(self) -> Result<ContextAgent, DataError> {
if let Some(z_agent) = self._agent {
if self._relevant_types.is_empty() {
emit_error!(DataError::Validation(ValidationError::Empty(
"relevant_types".into()
)))
} else {
let mut relevant_types = vec![];
for item in self._relevant_types.iter() {
let iri = IriString::try_from(item.as_str())?;
relevant_types.push(iri);
}
Ok(ContextAgent {
object_type: ObjectType::ContextAgent,
agent: z_agent,
relevant_types,
})
}
} else {
emit_error!(DataError::Validation(ValidationError::MissingField(
"agent".into()
)))
}
}
}
fn default_object_type() -> ObjectType {
ObjectType::ContextAgent
}