use std::fmt;
use secrecy::SecretString;
use super::error::InvalidInput;
use ihe_atna::redact::REDACTED;
const URN_OID: &str = "urn:oid:";
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Oid(String);
impl Oid {
pub fn new(text: &str) -> Result<Self, InvalidInput> {
let dotted = text.strip_prefix(URN_OID).unwrap_or(text);
let mut arcs = dotted.split('.');
let first = arcs.next().unwrap_or_default();
if !matches!(first, "0" | "1" | "2") {
return Err(InvalidInput::Oid);
}
let mut count = 1_usize;
for arc in arcs {
let digits = !arc.is_empty() && arc.bytes().all(|byte| byte.is_ascii_digit());
if !digits || (arc.len() > 1 && arc.starts_with('0')) {
return Err(InvalidInput::Oid);
}
count = count.saturating_add(1);
}
if count < 2 {
return Err(InvalidInput::Oid);
}
Ok(Self(dotted.to_owned()))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn urn(&self) -> String {
format!("{URN_OID}{}", self.0)
}
}
impl fmt::Display for Oid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HomeCommunityId(Oid);
impl HomeCommunityId {
#[must_use]
pub fn new(oid: Oid) -> Self {
Self(oid)
}
#[must_use]
pub fn oid(&self) -> &Oid {
&self.0
}
}
impl fmt::Display for HomeCommunityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0.urn())
}
}
#[derive(Clone)]
pub struct PatientIdentifier {
authority: Oid,
value: SecretString,
}
impl PatientIdentifier {
pub fn new(authority: Oid, value: SecretString) -> Result<Self, InvalidInput> {
use secrecy::ExposeSecret as _;
if value.expose_secret().is_empty() {
return Err(InvalidInput::EmptyValue);
}
Ok(Self { authority, value })
}
#[must_use]
pub fn authority(&self) -> &Oid {
&self.authority
}
#[must_use]
pub fn value(&self) -> &SecretString {
&self.value
}
}
impl fmt::Debug for PatientIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PatientIdentifier")
.field("authority", &self.authority)
.field("value", &REDACTED)
.finish()
}
}
#[derive(Clone)]
pub struct CommunityPatientId {
root: SecretString,
extension: Option<SecretString>,
}
impl CommunityPatientId {
pub(super) fn new(root: SecretString, extension: Option<SecretString>) -> Self {
Self { root, extension }
}
#[must_use]
pub fn root(&self) -> &SecretString {
&self.root
}
#[must_use]
pub fn extension(&self) -> Option<&SecretString> {
self.extension.as_ref()
}
}
impl fmt::Debug for CommunityPatientId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use secrecy::ExposeSecret as _;
let root = match self.extension {
Some(_) => self.root.expose_secret(),
None => REDACTED,
};
f.debug_struct("CommunityPatientId")
.field("root", &root)
.field("extension", &self.extension.as_ref().map(|_| REDACTED))
.finish()
}
}