diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-10-22 19:12:34 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-10-22 19:12:34 -0400 |
| commit | 6430854352745f45281021c305b4e350bc92d535 (patch) | |
| tree | c6901c22a45e36415f63efe988d4d4f2a309df81 /src/name.rs | |
| parent | 98af8ff80da919a1126ba7c6afa65e6654b5ecde (diff) | |
| parent | db940bacd096a33a65f29759e70ea1acf6186a67 (diff) | |
Merge branch 'unicode-normalization'
Diffstat (limited to 'src/name.rs')
| -rw-r--r-- | src/name.rs | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/name.rs b/src/name.rs new file mode 100644 index 0000000..9187d33 --- /dev/null +++ b/src/name.rs @@ -0,0 +1,85 @@ +use std::fmt; + +use crate::normalize::{ident, nfc}; + +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, sqlx::Type)] +#[serde(from = "String", into = "String")] +pub struct Name { + display: nfc::String, + canonical: ident::String, +} + +impl Name { + pub fn new<D, C>(display: D, canonical: C) -> Result<Self, Error> + where + D: AsRef<str>, + C: AsRef<str>, + { + let name = Self::from(display); + + if name.canonical.as_str() == canonical.as_ref() { + Ok(name) + } else { + Err(Error::CanonicalMismatch( + canonical.as_ref().into(), + name.canonical, + name.display, + )) + } + } + + pub fn optional<D, C>(display: Option<D>, canonical: Option<C>) -> Result<Option<Self>, Error> + where + D: AsRef<str>, + C: AsRef<str>, + { + display + .zip(canonical) + .map(|(display, canonical)| Self::new(display, canonical)) + .transpose() + } + + pub fn display(&self) -> &nfc::String { + &self.display + } + + pub fn canonical(&self) -> &ident::String { + &self.canonical + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("stored canonical form {0:#?} does not match computed canonical form {:#?} for name {:#?}", .1.as_str(), .2.as_str())] + CanonicalMismatch(String, ident::String, nfc::String), +} + +impl Default for Name { + fn default() -> Self { + Self::from(String::default()) + } +} + +impl fmt::Display for Name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.display.fmt(f) + } +} + +impl<S> From<S> for Name +where + S: AsRef<str>, +{ + fn from(name: S) -> Self { + let display = nfc::String::from(&name); + let canonical = ident::String::from(&name); + + Self { display, canonical } + } +} + +impl From<Name> for String { + fn from(name: Name) -> Self { + name.display.into() + } +} |
