use std::collections::BTreeSet;
use std::fmt;
use std::net::IpAddr;
use jiff::Timestamp;
use secrecy::SecretString;
use crate::classify::Classification;
use crate::emergency::Emergency;
use crate::label::Labels;
use crate::retention::Retention;
use crate::transformation::Transformation;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Action {
Query,
Read,
Create,
Update,
Delete,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Outcome {
Success,
MinorFailure,
SeriousFailure,
MajorFailure,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Purpose {
pub system: Option<String>,
pub code: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Acting {
Person,
Client,
}
impl Acting {
#[must_use]
pub const fn code(self) -> &'static str {
match self {
Self::Person => "person",
Self::Client => "client",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AssuranceLevel {
Low,
Substantial,
High,
}
impl AssuranceLevel {
#[must_use]
pub const fn code(self) -> &'static str {
match self {
Self::Low => "low",
Self::Substantial => "substantial",
Self::High => "high",
}
}
}
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Professional {
pub name: Option<String>,
pub identifier: Option<String>,
}
impl fmt::Debug for Professional {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Professional")
.field("name", &self.name.is_some())
.field("identifier", &self.identifier.is_some())
.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Coded {
pub system: Option<String>,
pub code: String,
}
#[derive(Clone, PartialEq, Eq)]
pub struct Relayed {
pub contact_point: String,
pub country_code: String,
pub professional: RelayedProfessional,
pub provider: RelayedProvider,
}
impl fmt::Debug for Relayed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Relayed")
.field("country_code", &self.country_code)
.finish_non_exhaustive()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct RelayedProfessional {
pub family_name: String,
pub given_name: String,
pub identifier: String,
pub issuing_authority: String,
pub roles: Vec<Coded>,
}
impl fmt::Debug for RelayedProfessional {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RelayedProfessional")
.field("roles", &self.roles.len())
.finish_non_exhaustive()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct RelayedProvider {
pub identifier: String,
pub issuing_authority: String,
pub name: String,
pub address: String,
}
impl fmt::Debug for RelayedProvider {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RelayedProvider").finish_non_exhaustive()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Accessor {
pub issuer: Option<String>,
pub subject: String,
pub client_id: Option<String>,
pub audience: Option<String>,
pub provider: Option<String>,
pub acting: Option<Acting>,
pub roles: Vec<Coded>,
pub token_id: Option<String>,
pub assurance: Option<AssuranceLevel>,
pub professional: Professional,
pub alt_id: Option<String>,
pub purposes: Vec<Purpose>,
pub relayed: Option<Relayed>,
}
impl fmt::Debug for Accessor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Accessor")
.field("provider", &self.provider.is_some())
.field("acting", &self.acting)
.field("roles", &self.roles)
.field("token_id", &self.token_id.is_some())
.field("assurance", &self.assurance)
.field("professional", &self.professional)
.field("alt_id", &self.alt_id.is_some())
.field("purposes", &self.purposes)
.field("relayed", &self.relayed)
.finish_non_exhaustive()
}
}
#[derive(Debug, Clone)]
pub struct PatientIdentifier {
pub namespace: Option<String>,
pub value: SecretString,
}
#[derive(Clone)]
#[non_exhaustive]
pub enum PatientLookup {
RequestNamed,
Found(Vec<PatientIdentifier>),
NotFound,
Unavailable,
NotConfigured,
Unsupported,
}
impl PatientLookup {
#[must_use]
pub const fn code(&self) -> &'static str {
match self {
Self::RequestNamed => "request-named",
Self::Found(_) => "found",
Self::NotFound => "not-found",
Self::Unavailable => "unavailable",
Self::NotConfigured => "not-configured",
Self::Unsupported => "unsupported",
}
}
}
impl fmt::Debug for PatientLookup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Found(found) => write!(f, "Found({})", found.len()),
other => f.write_str(other.code()),
}
}
}
#[derive(Clone)]
pub struct EhrAt {
pub endpoint: String,
pub ehr_id: String,
pub patient: PatientLookup,
}
impl fmt::Debug for EhrAt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EhrAt")
.field("endpoint", &self.endpoint)
.field("patient", &self.patient)
.finish_non_exhaustive()
}
}
#[derive(Clone, Default)]
pub struct DataSubject {
pub patient: Option<PatientIdentifier>,
pub ehrs: Vec<EhrAt>,
pub population: Option<Population>,
}
impl fmt::Debug for DataSubject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DataSubject")
.field("patient", &self.patient.is_some())
.field("ehrs", &self.ehrs.len())
.field("population", &self.population)
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PopulationReason {
NotProjected,
OverLimit,
NotDelivered,
Unattributed,
}
impl PopulationReason {
#[must_use]
pub const fn code(self) -> &'static str {
match self {
Self::NotProjected => "not-projected",
Self::OverLimit => "over-limit",
Self::NotDelivered => "not-delivered",
Self::Unattributed => "unattributed",
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Population {
pub reason: PopulationReason,
pub ehrs: Option<usize>,
pub limit: usize,
pub templates: BTreeSet<String>,
pub archetypes: BTreeSet<String>,
}
impl fmt::Debug for Population {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Population")
.field("reason", &self.reason)
.field("ehrs", &self.ehrs)
.field("limit", &self.limit)
.field("templates", &self.templates.len())
.field("archetypes", &self.archetypes.len())
.finish()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Origin {
pub endpoint: Option<String>,
pub node: Option<String>,
pub system_id: Option<String>,
pub status: Option<String>,
pub rows: Option<usize>,
pub version: Option<WrittenVersion>,
pub created_ehr: Option<CreatedEhr>,
pub categories: Option<Classification>,
}
impl Origin {
#[must_use]
pub fn key(&self) -> Option<&str> {
self.endpoint.as_deref().or(self.system_id.as_deref())
}
}
impl fmt::Debug for Origin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Origin")
.field("endpoint", &self.endpoint)
.field("node", &self.node)
.field("status", &self.status)
.field("rows", &self.rows)
.field("version", &self.version)
.field("created_ehr", &self.created_ehr)
.field("categories", &self.categories)
.finish_non_exhaustive()
}
}
#[derive(Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum WrittenVersion {
Named(String),
Unnamed,
Malformed,
}
impl WrittenVersion {
#[must_use]
pub fn named(&self) -> Option<&str> {
match self {
Self::Named(version) => Some(version),
Self::Unnamed | Self::Malformed => None,
}
}
#[must_use]
pub fn unread(&self) -> Option<&'static str> {
match self {
Self::Named(_) => None,
Self::Unnamed => Some("unnamed"),
Self::Malformed => Some("malformed"),
}
}
}
impl fmt::Debug for WrittenVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Named(_) => f.write_str("Named(..)"),
Self::Unnamed => f.write_str("Unnamed"),
Self::Malformed => f.write_str("Malformed"),
}
}
}
#[derive(Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum CreatedEhr {
Named(String),
Unnamed,
Malformed,
}
impl CreatedEhr {
#[must_use]
pub fn named(&self) -> Option<&str> {
match self {
Self::Named(ehr_id) => Some(ehr_id),
Self::Unnamed | Self::Malformed => None,
}
}
#[must_use]
pub fn unread(&self) -> Option<&'static str> {
match self {
Self::Named(_) => None,
Self::Unnamed => Some("unnamed"),
Self::Malformed => Some("malformed"),
}
}
}
impl fmt::Debug for CreatedEhr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Named(_) => f.write_str("Named(..)"),
Self::Unnamed => f.write_str("Unnamed"),
Self::Malformed => f.write_str("Malformed"),
}
}
}
#[derive(Clone)]
pub struct Request {
pub id: Option<String>,
pub operation: String,
pub operation_system: Option<String>,
pub resource: Option<String>,
pub query: Option<SecretString>,
pub stored_query: Option<String>,
pub client_address: Option<IpAddr>,
pub correlation: Option<String>,
}
impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Request")
.field("id", &self.id)
.field("operation", &self.operation)
.field("query", &self.query.is_some())
.field("correlation", &self.correlation.is_some())
.finish_non_exhaustive()
}
}
#[derive(Clone)]
pub struct AccessRecord {
pub action: Action,
pub recorded: Timestamp,
pub outcome: Outcome,
pub outcome_description: Option<String>,
pub accessor: Accessor,
pub subject: DataSubject,
pub categories: Classification,
pub delivered: Option<usize>,
pub origins: Vec<Origin>,
pub origin_count: Option<usize>,
pub request: Request,
pub retention: Retention,
pub labels: Labels,
pub emergency: Option<Emergency>,
pub inserted: Option<Insertion>,
pub transformations: Vec<Transformation>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Insertion {
Subject,
Representative,
}
impl fmt::Debug for AccessRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AccessRecord")
.field("action", &self.action)
.field("recorded", &self.recorded)
.field("outcome", &self.outcome)
.field("outcome_description", &self.outcome_description.is_some())
.field("accessor", &self.accessor)
.field("subject", &self.subject)
.field("categories", &self.categories)
.field("delivered", &self.delivered)
.field("origins", &self.origins)
.field("origin_count", &self.origin_count)
.field("request", &self.request)
.field("retention", &self.retention)
.field("labels", &self.labels)
.field("emergency", &self.emergency)
.field("inserted", &self.inserted)
.field("transformations", &self.transformations)
.finish()
}
}