use crate::{
Account, Agent, AgentId, CIString, DataError, Fingerprint, Group, GroupId, MyEmailAddress,
Validate, ValidationError, emit_error, fingerprint::fingerprint_it,
};
use core::fmt;
use iri_string::types::UriStr;
use serde::{
Deserialize, Serialize,
de::{self, Error},
};
use serde_json::{Map, Value};
use std::{hash::Hasher, str::FromStr};
use tracing::{debug, error};
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(untagged)]
pub enum Actor {
Agent(Agent),
Group(Group),
}
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub(crate) enum ActorId {
Agent(AgentId),
Group(GroupId),
}
impl From<Actor> for ActorId {
fn from(value: Actor) -> Self {
match value {
Actor::Agent(agent) => ActorId::Agent(AgentId::from(agent)),
Actor::Group(group) => ActorId::Group(GroupId::from(group)),
}
}
}
impl From<ActorId> for Actor {
fn from(value: ActorId) -> Self {
match value {
ActorId::Agent(x) => Actor::Agent(Agent::from(x)),
ActorId::Group(x) => Actor::Group(Group::from(x)),
}
}
}
impl Actor {
pub fn from_json_obj(map: Map<String, Value>) -> Result<Self, DataError> {
match map.get("objectType") {
Some(x) => {
if x == &serde_json::json!("Agent") {
Ok(Actor::Agent(Agent::from_json_obj(map)?))
} else if x == &serde_json::json!("Group") {
Ok(Actor::Group(Group::from_json_obj(map)?))
} else {
emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
format!("Unknown objectType ({x})").into()
)))
}
}
None => {
debug!("Missing 'objectType'. Assume Agent + continue...");
Ok(Actor::Agent(Agent::from_json_obj(map)?))
}
}
}
pub fn from_agent(actor: Agent) -> Self {
Actor::Agent(actor)
}
pub fn from_group(actor: Group) -> Self {
Actor::Group(actor)
}
pub fn is_agent(&self) -> bool {
matches!(self, Actor::Agent(_))
}
pub fn is_group(&self) -> bool {
matches!(self, Actor::Group(_))
}
pub fn as_agent(&self) -> Result<Agent, DataError> {
match self {
Actor::Agent(x) => Ok(x.to_owned()),
_ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
format!("This ({self}) is NOT an Agent").into()
))),
}
}
pub fn as_group(&self) -> Result<Group, DataError> {
match self {
Actor::Group(x) => Ok(x.to_owned()),
_ => emit_error!(DataError::Validation(ValidationError::ConstraintViolation(
format!("This ({self}) is NOT a Group").into()
))),
}
}
pub fn name(&self) -> Option<&CIString> {
match self {
Actor::Agent(x) => x.name(),
Actor::Group(x) => x.name(),
}
}
pub fn name_as_str(&self) -> Option<&str> {
match self {
Actor::Agent(x) => x.name_as_str(),
Actor::Group(x) => x.name_as_str(),
}
}
pub fn mbox(&self) -> Option<&MyEmailAddress> {
match self {
Actor::Agent(x) => x.mbox(),
Actor::Group(x) => x.mbox(),
}
}
pub fn mbox_sha1sum(&self) -> Option<&str> {
match self {
Actor::Agent(x) => x.mbox_sha1sum(),
Actor::Group(x) => x.mbox_sha1sum(),
}
}
pub fn openid(&self) -> Option<&UriStr> {
match self {
Actor::Agent(x) => x.openid(),
Actor::Group(x) => x.openid(),
}
}
pub fn account(&self) -> Option<&Account> {
match self {
Actor::Agent(x) => x.account(),
Actor::Group(x) => x.account(),
}
}
pub fn uid(&self) -> u64 {
fingerprint_it(self)
}
pub fn equivalent(&self, that: &Actor) -> bool {
self.uid() == that.uid()
}
}
impl<'de> Deserialize<'de> for Actor {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let val = serde_json::Value::deserialize(deserializer)?;
match Map::deserialize(val.clone()) {
Ok(x) => {
if x.contains_key("objectType") {
if let Ok(x) = Agent::deserialize(val.clone())
&& x.check_object_type()
{
return Ok(Actor::Agent(x));
}
match Group::deserialize(val) {
Ok(x) => Ok(Actor::Group(x)),
_ => Err(D::Error::unknown_variant("actor", &["Agent", "Group"])),
}
} else {
match Agent::deserialize(val.clone()) {
Ok(x) => Ok(Actor::Agent(x)),
_ => {
error!("Alleged Actor has no 'objectType' and is NOT an Agent");
Err(D::Error::unknown_field("actor", &["Agent", "Group"]))
}
}
}
}
Err(x) => {
error!("Failed deserializing '{}' as Actor: {}", val, x);
Err(de::Error::unknown_field("actor", &["Agent", "Group"]))
}
}
}
}
impl fmt::Display for Actor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Actor::Agent(x) => write!(f, "{x}"),
Actor::Group(x) => write!(f, "{x}"),
}
}
}
impl Fingerprint for Actor {
fn fingerprint<H: Hasher>(&self, state: &mut H) {
match self {
Actor::Agent(x) => x.fingerprint(state),
Actor::Group(x) => x.fingerprint(state),
}
}
}
impl Validate for Actor {
fn validate(&self) -> Vec<ValidationError> {
match self {
Actor::Agent(x) => x.validate(),
Actor::Group(x) => x.validate(),
}
}
}
impl FromStr for Actor {
type Err = DataError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let map = serde_json::from_str::<Map<String, Value>>(s)?;
Self::from_json_obj(map)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tracing_test::traced_test;
#[test]
fn test_serde_actor_agent() -> Result<(), DataError> {
const JSON: &str =
r#"{"objectType":"Agent","name":"Z User","mbox":"mailto:zuser@somewhere.net"}"#;
let a1 = Agent::builder()
.with_object_type()
.name("Z User")?
.mbox("zuser@somewhere.net")?
.build()?;
let actor = Actor::Agent(a1);
let se_result = serde_json::to_string(&actor);
assert!(se_result.is_ok());
let json = se_result.unwrap();
assert_eq!(json, JSON);
let de_result = serde_json::from_str::<Actor>(JSON);
assert!(de_result.is_ok());
if let Ok(Actor::Agent(a2)) = de_result {
assert_eq!(a2.name().unwrap(), "Z User");
}
Ok(())
}
#[test]
fn test_de_actor_agent() {
const JSON: &str = r#"{
"objectType":"Agent",
"name":"Z User",
"mbox":"mailto:zuser@somewhere.net"
}"#;
let de_result = serde_json::from_str::<Actor>(JSON);
assert!(de_result.is_ok());
}
#[traced_test]
#[test]
fn test_actor_bad() {
const IN1: &str = r#"{ "objectType": "Foo", "foo": 42 }"#;
const IN2: &str = r#"{ "foo": 42 }"#;
let r1 = serde_json::from_str::<Actor>(IN1);
assert!(r1.is_err()); assert!(r1.err().unwrap().is_data());
let r2 = serde_json::from_str::<Actor>(IN2);
assert!(r2.is_err()); assert!(r2.err().unwrap().is_data());
}
}