use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(feature = "typescript")]
use tsify::Tsify;
use crate::validation::stable_id;
static INT_SEQ: AtomicU64 = AtomicU64::new(1);
fn get_int_id() -> u64 {
INT_SEQ.fetch_add(1, Ordering::SeqCst)
}
#[derive(Default, Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "typescript", derive(Tsify))]
#[cfg_attr(feature = "typescript", tsify(into_wasm_abi, from_wasm_abi))]
pub struct NormalizedTermValue(String);
impl NormalizedTermValue {
pub fn new(term: String) -> Self {
let value = term.trim().to_lowercase();
Self(value)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<String> for NormalizedTermValue {
fn from(term: String) -> Self {
Self::new(term)
}
}
impl From<&str> for NormalizedTermValue {
fn from(term: &str) -> Self {
Self::new(term.to_string())
}
}
impl Display for NormalizedTermValue {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl AsRef<[u8]> for NormalizedTermValue {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct NormalizedTerm {
pub id: u64,
#[serde(rename = "nterm")]
pub value: NormalizedTermValue,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub display_value: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub action: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub priority: Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trigger: Option<String>,
#[serde(default)]
pub pinned: bool,
}
impl NormalizedTerm {
pub fn new(id: u64, value: NormalizedTermValue) -> Self {
Self {
id,
value,
display_value: None,
url: None,
action: None,
priority: None,
trigger: None,
pinned: false,
}
}
pub fn with_auto_id(value: NormalizedTermValue) -> Self {
Self {
id: get_int_id(),
value,
display_value: None,
url: None,
action: None,
priority: None,
trigger: None,
pinned: false,
}
}
pub fn with_stable_id(value: NormalizedTermValue) -> Self {
Self {
id: stable_id(value.as_str()),
value,
display_value: None,
url: None,
action: None,
priority: None,
trigger: None,
pinned: false,
}
}
pub fn with_display_value(mut self, display_value: String) -> Self {
self.display_value = Some(display_value);
self
}
pub fn with_url(mut self, url: String) -> Self {
self.url = Some(url);
self
}
pub fn with_action(mut self, action: String) -> Self {
self.action = Some(action);
self
}
pub fn with_priority(mut self, priority: u8) -> Self {
self.priority = Some(priority);
self
}
pub fn with_trigger(mut self, trigger: String) -> Self {
self.trigger = Some(trigger);
self
}
pub fn with_pinned(mut self, pinned: bool) -> Self {
self.pinned = pinned;
self
}
pub fn display(&self) -> &str {
self.display_value
.as_deref()
.unwrap_or_else(|| self.value.as_str())
}
pub fn action(&self) -> Option<&String> {
self.action.as_ref()
}
pub fn priority(&self) -> Option<&u8> {
self.priority.as_ref()
}
pub fn trigger(&self) -> Option<&String> {
self.trigger.as_ref()
}
pub fn pinned(&self) -> bool {
self.pinned
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Concept {
pub id: u64,
pub value: NormalizedTermValue,
}
impl Concept {
pub fn new(value: NormalizedTermValue) -> Self {
Self {
id: get_int_id(),
value,
}
}
pub fn with_stable_id(value: NormalizedTermValue) -> Self {
Self {
id: stable_id(value.as_str()),
value,
}
}
pub fn with_id(id: u64, value: NormalizedTermValue) -> Self {
Self { id, value }
}
}
impl From<String> for Concept {
fn from(concept: String) -> Self {
let concept = NormalizedTermValue::new(concept);
Self::new(concept)
}
}
impl Display for Concept {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}