use serde::{Deserialize, Serialize};
use crate::{
KipError,
ast::{Json, Map},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EntityType {
ConceptNode,
PropositionLink,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(tag = "_type")]
pub enum Entity {
ConceptNode(ConceptNode),
PropositionLink(PropositionLink),
}
#[derive(Debug, Serialize)]
#[serde(tag = "_type")]
pub enum EntityRef<'a> {
ConceptNode(ConceptNodeRef<'a>),
PropositionLink(PropositionLinkRef<'a>),
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct ConceptNode {
pub id: String,
pub r#type: String,
pub name: String,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub attributes: Map<String, Json>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub metadata: Map<String, Json>,
}
#[derive(Debug, Serialize)]
pub struct ConceptNodeRef<'a> {
pub id: &'a str,
pub r#type: &'a str,
pub name: &'a str,
#[serde(skip_serializing_if = "Map::is_empty")]
pub attributes: &'a Map<String, Json>,
#[serde(skip_serializing_if = "Map::is_empty")]
pub metadata: &'a Map<String, Json>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct PropositionLink {
pub id: String,
pub subject: String,
pub object: String,
pub predicate: String,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub attributes: Map<String, Json>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub metadata: Map<String, Json>,
}
#[derive(Debug, Serialize)]
pub struct PropositionLinkRef<'a> {
pub id: &'a str,
pub subject: &'a str,
pub object: &'a str,
pub predicate: &'a str,
#[serde(skip_serializing_if = "Map::is_empty")]
pub attributes: &'a Map<String, Json>,
#[serde(skip_serializing_if = "Map::is_empty")]
pub metadata: &'a Map<String, Json>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct UpsertResult {
pub blocks: usize,
pub upsert_concept_nodes: Vec<String>,
pub upsert_proposition_links: Vec<String>,
}
pub fn validate_dot_path_var(path: &[String], et: EntityType) -> Result<(), KipError> {
match path.len() {
0 => Ok(()),
1 => match path[0].as_str() {
"id" | "attributes" | "metadata" => Ok(()),
"type" | "name" if et == EntityType::ConceptNode => Ok(()),
"subject" | "predicate" | "object" if et == EntityType::PropositionLink => Ok(()),
_ => Err(KipError::invalid_syntax(format!(
"Dot notation path: invalid path component {:?}",
path[0]
))),
},
2 => match (path[0].as_str(), path[1].as_str()) {
("attributes", _) | ("metadata", _) => Ok(()),
_ => Err(KipError::invalid_syntax(format!(
"Dot notation path: invalid path components {:?}",
path.join("."),
))),
},
_ => Err(KipError::invalid_syntax(format!(
"Dot notation path: too many components in path {:?}",
path.join(".")
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_entity() {
let concept = Entity::ConceptNode(ConceptNode {
id: "C:123".to_string(),
r#type: "Drug".to_string(),
name: "Test".to_string(),
attributes: Map::new(),
metadata: Map::new(),
});
let rt = serde_json::to_string(&concept).unwrap();
assert_eq!(
rt,
r#"{"_type":"ConceptNode","id":"C:123","type":"Drug","name":"Test"}"#
);
let rt: Entity = serde_json::from_str(&rt).unwrap();
assert_eq!(rt, concept);
let prop = Entity::PropositionLink(PropositionLink {
id: "P:123:treats".to_string(),
subject: "C:123".to_string(),
object: "C:456".to_string(),
predicate: "treats".to_string(),
attributes: Map::new(),
metadata: Map::new(),
});
let rt = serde_json::to_string(&prop).unwrap();
assert_eq!(
rt,
r#"{"_type":"PropositionLink","id":"P:123:treats","subject":"C:123","object":"C:456","predicate":"treats"}"#
);
let rt: Entity = serde_json::from_str(&rt).unwrap();
assert_eq!(rt, prop);
}
}