use std::fmt;
use secrecy::SecretString;
use ihe_atna::redact::REDACTED;
use super::error::{FeedError, InvalidInput};
use super::message;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum EventKind {
Create,
Update,
Delete,
Merge,
}
impl EventKind {
pub const ALL: [Self; 4] = [Self::Create, Self::Update, Self::Delete, Self::Merge];
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Create => "create",
Self::Update => "update",
Self::Delete => "delete",
Self::Merge => "merge",
}
}
}
#[derive(Clone)]
pub struct Identifier {
system: Option<String>,
value: SecretString,
}
impl Identifier {
#[must_use]
pub fn new(system: Option<String>, value: SecretString) -> Self {
Self { system, value }
}
#[must_use]
pub fn system(&self) -> Option<&str> {
self.system.as_deref()
}
#[must_use]
pub fn value(&self) -> &SecretString {
&self.value
}
}
impl fmt::Debug for Identifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Identifier")
.field("system", &self.system)
.field("value", &REDACTED)
.finish()
}
}
#[derive(Clone, Default)]
pub struct PatientIdentity {
id: Option<SecretString>,
identifiers: Vec<Identifier>,
}
impl PatientIdentity {
#[must_use]
pub fn new(id: Option<SecretString>, identifiers: Vec<Identifier>) -> Self {
Self { id, identifiers }
}
#[must_use]
pub fn id(&self) -> Option<&SecretString> {
self.id.as_ref()
}
#[must_use]
pub fn identifiers(&self) -> &[Identifier] {
&self.identifiers
}
}
impl fmt::Debug for PatientIdentity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let systems: Vec<Option<&str>> = self.identifiers.iter().map(Identifier::system).collect();
f.debug_struct("PatientIdentity")
.field("id", &self.id.as_ref().map(|_| REDACTED))
.field("identifier_systems", &systems)
.finish()
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Event {
Created(PatientIdentity),
Updated(PatientIdentity),
Deleted(PatientIdentity),
Merged {
subsumed: PatientIdentity,
surviving: SecretString,
},
}
impl Event {
#[must_use]
pub const fn kind(&self) -> EventKind {
match self {
Self::Created(_) => EventKind::Create,
Self::Updated(_) => EventKind::Update,
Self::Deleted(_) => EventKind::Delete,
Self::Merged { .. } => EventKind::Merge,
}
}
}
#[derive(Debug, Clone)]
pub struct Feed {
message_id: String,
events: Vec<Event>,
}
impl Feed {
pub fn read(media: Option<&str>, body: &[u8]) -> Result<Self, FeedError> {
let (message_id, events) = message::read(media, body)?;
Ok(Self { message_id, events })
}
#[must_use]
pub fn message_id(&self) -> &str {
&self.message_id
}
#[must_use]
pub fn events(&self) -> &[Event] {
&self.events
}
#[must_use]
pub fn count(&self, kind: EventKind) -> usize {
self.events
.iter()
.filter(|event| event.kind() == kind)
.count()
}
pub fn acknowledgement(
&self,
response_id: &ResponseId,
source: &str,
) -> Result<Vec<u8>, Unwritable> {
message::acknowledgement(&self.message_id, response_id.as_str(), source)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResponseId(String);
impl ResponseId {
pub fn new(text: &str) -> Result<Self, InvalidInput> {
let groups: Vec<&str> = text.split('-').collect();
let shaped = groups.iter().map(|group| group.len()).eq([8, 4, 4, 4, 12])
&& text
.chars()
.all(|character| character == '-' || matches!(character, '0'..='9' | 'a'..='f'));
if shaped {
Ok(Self(text.to_owned()))
} else {
Err(InvalidInput::ResponseId)
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, thiserror::Error)]
#[error("the response message cannot be written as FHIR JSON")]
pub struct Unwritable(#[source] pub(super) serde_json::Error);
pub fn refusal(error: &FeedError) -> Result<Vec<u8>, Unwritable> {
message::refusal(error)
}