// Identifier and anchor rules for the JSON Schema specifications
// (Python's `referencing.jsonschema` private helpers).

///|
fn string_member(contents : Json, key : String) -> String? {
  match contents {
    Object(obj) =>
      match obj.get(key) {
        Some(String(s)) => Some(s)
        _ => None
      }
    _ => None
  }
}

///|
/// `$id` (draft 2019-09 and later): booleans have no ID.
///
/// Python's `_dollar_id`.
pub fn dollar_id(contents : Json) -> String? {
  string_member(contents, "$id")
}

///|
/// `$id` in drafts 6 and 7: ignored next to `$ref` (whose siblings are
/// ignored in those drafts), and plain-name fragments (`#foo`) are anchors,
/// not IDs.
///
/// Python's `_legacy_dollar_id`.
pub fn legacy_dollar_id(contents : Json) -> String? {
  guard contents is Object(obj) && !obj.contains("$ref") else { return None }
  match string_member(contents, "$id") {
    Some(id) if !id.has_prefix("#") => Some(id)
    _ => None
  }
}

///|
/// `id` in drafts 3 and 4: ignored next to `$ref`, and plain-name fragments
/// (`#foo`) are anchors, not IDs.
///
/// Python's `_legacy_id`.
pub fn legacy_id(contents : Json) -> String? {
  guard contents is Object(obj) && !obj.contains("$ref") else { return None }
  match string_member(contents, "id") {
    Some(id) if !id.has_prefix("#") => Some(id)
    _ => None
  }
}

///|
/// `$anchor` and `$dynamicAnchor` (draft 2020-12).
///
/// Python's `_anchor`.
pub fn anchors_2020(
  specification : Specification,
  contents : Json,
) -> Array[Anchor] {
  let anchors = []
  if string_member(contents, "$anchor") is Some(name) {
    anchors.push(
      Anchor::new(name~, resource=specification.create_resource(contents)),
    )
  }
  if string_member(contents, "$dynamicAnchor") is Some(name) {
    anchors.push(
      dynamic_anchor(name~, resource=specification.create_resource(contents)),
    )
  }
  anchors
}

///|
/// `$anchor` (draft 2019-09).
///
/// Python's `_anchor_2019`.
pub fn anchors_2019(
  specification : Specification,
  contents : Json,
) -> Array[Anchor] {
  match string_member(contents, "$anchor") {
    Some(name) =>
      [Anchor::new(name~, resource=specification.create_resource(contents))]
    None => []
  }
}

///|
fn legacy_anchor_in(
  key : String,
  specification : Specification,
  contents : Json,
) -> Array[Anchor] {
  match string_member(contents, key) {
    Some(id) if id.has_prefix("#") =>
      [
        Anchor::new(
          name=id.view(start_offset=1).to_owned(),
          resource=specification.create_resource(contents),
        ),
      ]
    _ => []
  }
}

///|
/// Plain-name fragments in `$id` (`{"$id": "#foo"}`), drafts 6 and 7.
///
/// Python's `_legacy_anchor_in_dollar_id`.
pub fn legacy_anchor_in_dollar_id(
  specification : Specification,
  contents : Json,
) -> Array[Anchor] {
  legacy_anchor_in("$id", specification, contents)
}

///|
/// Plain-name fragments in `id` (`{"id": "#foo"}`), drafts 3 and 4.
///
/// Python's `_legacy_anchor_in_id`.
pub fn legacy_anchor_in_id(
  specification : Specification,
  contents : Json,
) -> Array[Anchor] {
  legacy_anchor_in("id", specification, contents)
}