use crate::{
load::ModuleLoader,
model::{check::Validate, modules::Module, References, Span},
store::ModuleStore,
};
use lazy_static::lazy_static;
use regex::Regex;
use sdml_errors::diagnostics::functions::invalid_language_tag;
use std::{fmt::Display, str::FromStr};
use tracing::warn;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct ControlledLanguageString {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
span: Option<Box<Span>>,
value: String,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
language: Option<ControlledLanguageTag>,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct ControlledLanguageTag {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
span: Option<Box<Span>>,
value: String,
}
impl From<String> for ControlledLanguageString {
fn from(value: String) -> Self {
Self {
span: Default::default(),
value,
language: Default::default(),
}
}
}
impl_has_source_span_for!(ControlledLanguageString);
impl References for ControlledLanguageString {}
impl Validate for ControlledLanguageString {
fn validate(
&self,
_top: &Module,
_cache: &impl ModuleStore,
_loader: &impl ModuleLoader,
_check_constraints: bool,
) {
warn!("Missing Validation for ControlledLanguageString");
}
}
impl ControlledLanguageString {
pub fn new<S>(value: S, language: ControlledLanguageTag) -> Self
where
S: Into<String>,
{
Self {
span: Default::default(),
value: value.into(),
language: Some(language),
}
}
get_and_set!(pub value, set_value => String);
get_and_set!(pub language, set_language, unset_language => optional has_language, ControlledLanguageTag);
}
lazy_static! {
static ref LANGUAGE_TAG: Regex = Regex::new(r"^[a-z]{2,3}(-[A-Z][A-Za-z]{1,9})?$").unwrap();
}
impl Display for ControlledLanguageTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "@{}", self.value)
}
}
impl FromStr for ControlledLanguageTag {
type Err = crate::error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if Self::is_valid_str(s) {
Ok(Self {
span: None,
value: s.to_string(),
})
} else {
Err(invalid_language_tag(0, None, s).into())
}
}
}
impl From<ControlledLanguageTag> for String {
fn from(value: ControlledLanguageTag) -> Self {
value.value
}
}
impl AsRef<str> for ControlledLanguageTag {
fn as_ref(&self) -> &str {
self.value.as_str()
}
}
impl PartialEq for ControlledLanguageTag {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl PartialEq<str> for ControlledLanguageTag {
fn eq(&self, other: &str) -> bool {
self.value == other
}
}
impl Eq for ControlledLanguageTag {}
impl_has_source_span_for!(ControlledLanguageTag);
impl Validate for ControlledLanguageTag {
fn validate(
&self,
_top: &Module,
_cache: &impl ModuleStore,
_loader: &impl ModuleLoader,
_check_constraints: bool,
) {
assert!(Self::is_valid_str(&self.value))
}
}
impl ControlledLanguageTag {
pub fn new_unchecked(s: &str) -> Self {
Self {
span: None,
value: s.to_string(),
}
}
get_and_set!(pub value, set_value => String);
pub fn is_valid_str(s: &str) -> bool {
LANGUAGE_TAG.is_match(s)
}
}