///|
/// One extension member. Its value is any JSON value, without coercion.
pub struct ExtensionMember {
  name : String
  value : Json
} derive(Eq, Debug)

///|
/// RFC 9457 Problem Details data model. Missing members remain missing.
pub struct ProblemDetails {
  type_raw : String?
  title : String?
  status : Int?
  detail : String?
  instance : String?
  extensions : Array[ExtensionMember]
} derive(Eq, Debug)

///|
pub fn problem_details(
  type_raw : String?,
  title : String?,
  status : Int?,
  detail : String?,
  instance : String?,
  extensions : Array[ExtensionMember],
) -> ProblemDetails {
  { type_raw, title, status, detail, instance, extensions }
}

///|
pub fn extension_member(name : String, value : Json) -> ExtensionMember {
  { name, value }
}

///|
pub fn ExtensionMember::name(self : ExtensionMember) -> String {
  self.name
}

///|
pub fn ExtensionMember::value(self : ExtensionMember) -> Json {
  self.value
}

///|
pub fn ProblemDetails::type_raw(self : ProblemDetails) -> String? {
  self.type_raw
}

///|
pub fn ProblemDetails::title(self : ProblemDetails) -> String? {
  self.title
}

///|
pub fn ProblemDetails::status(self : ProblemDetails) -> Int? {
  self.status
}

///|
pub fn ProblemDetails::detail(self : ProblemDetails) -> String? {
  self.detail
}

///|
pub fn ProblemDetails::instance(self : ProblemDetails) -> String? {
  self.instance
}

///|
pub fn ProblemDetails::extensions(
  self : ProblemDetails,
) -> Array[ExtensionMember] {
  self.extensions.copy()
}

///|
/// Missing type is semantically `about:blank`; parsing does not insert it.
pub fn ProblemDetails::effective_type(self : ProblemDetails) -> String {
  match self.type_raw {
    Some(value) => value
    None => "about:blank"
  }
}

///|
pub fn ProblemDetails::is_about_blank(self : ProblemDetails) -> Bool {
  self.effective_type() == "about:blank"
}

///|
pub fn ProblemDetails::extension(self : ProblemDetails, name : String) -> Json? {
  for extension in self.extensions {
    if extension.name == name {
      return Some(extension.value)
    }
  }
  None
}

///|
pub fn ProblemDetails::has_extension(
  self : ProblemDetails,
  name : String,
) -> Bool {
  self.extension(name) is Some(_)
}

///|
pub fn ProblemDetails::standard_member_count(self : ProblemDetails) -> Int {
  let mut count = 0
  if self.type_raw is Some(_) {
    count += 1
  }
  if self.title is Some(_) {
    count += 1
  }
  if self.status is Some(_) {
    count += 1
  }
  if self.detail is Some(_) {
    count += 1
  }
  if self.instance is Some(_) {
    count += 1
  }
  count
}

///|
pub fn ProblemDetails::member_count(self : ProblemDetails) -> Int {
  self.standard_member_count() + self.extensions.length()
}

///|
pub fn library_version() -> String {
  "0.1.0-dev"
}

///|
pub fn problem_media_type() -> String {
  "application/problem+json"
}