use fhir_types::codec::Object;
use crate::issue::IssueType;
use crate::issue::MessageId;
use crate::issue::Severity;
use crate::report::Constraint;
use crate::report::Reason;
use crate::set::ResolveError;
use crate::validate::ValidateError;
use crate::validate::structure::Structure;
use crate::validate::walk::Cursor;
use crate::validate::walk::Walk;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Placement {
Extension,
Modifier,
Child,
}
impl Walk<'_, '_, '_> {
pub(crate) fn extension_cursor(
&mut self,
object: &Object,
placement: Placement,
(location, from): (&str, &Structure),
) -> Result<Cursor, ValidateError> {
let fallback = |walk: &Self| -> Result<Cursor, ValidateError> {
Ok(Cursor::new(
walk.validator().core("Extension")?,
"Extension",
))
};
let Some(url) = object.get("url").and_then(|url| url.as_str()) else {
return fallback(self);
};
if !url.contains(':') {
if placement != Placement::Child {
self.out().issue(
Severity::Error,
IssueType::Extension,
MessageId::RelativeExtensionUrl,
location,
format!("the extension url {url} is relative outside a complex extension"),
);
}
return fallback(self);
}
let structure = match self.validator().structure(url, Some(from)) {
Ok(structure) => structure,
Err(ValidateError::Resolve(ResolveError::NotFound { .. })) => {
let (severity, id) = if placement == Placement::Modifier {
(Severity::Error, MessageId::UnknownModifierExtension)
} else {
(Severity::Warning, MessageId::UnknownExtension)
};
self.out().issue(
severity,
IssueType::Extension,
id,
location,
format!("the package set defines no extension {url}"),
);
return fallback(self);
}
Err(error) => return Err(error),
};
if structure.type_name() != "Extension" {
self.out().issue(
Severity::Error,
IssueType::Extension,
MessageId::NotAnExtension,
location,
format!(
"{url} defines a {}, not an extension",
structure.type_name()
),
);
return fallback(self);
}
let modifier = structure
.element("Extension")
.and_then(|root| root.is_modifier.as_ref())
.and_then(|flag| flag.value)
.unwrap_or(false);
if modifier != (placement == Placement::Modifier) {
let wanted = if modifier {
"modifierExtension"
} else {
"extension"
};
self.out().issue(
Severity::Error,
IssueType::Extension,
MessageId::ModifierMismatch,
location,
format!("the extension {url} belongs in {wanted}"),
);
}
if !structure.definition().context.is_empty() {
self.out().unevaluated(
structure.url(),
"Extension",
Constraint::ExtensionContext,
location,
Reason::ExtensionContext,
);
}
Ok(Cursor::new(structure, "Extension"))
}
}