///|
pub fn render_value_as_xml(value : Value) -> String {
  render_value_as_xml_with_options(value, "root", "  ", "1.0")
}

///|
pub fn render_value_as_xml_with_options(
  value : Value,
  root_element_name : String,
  indent_text : String,
  xml_version : String,
) -> String {
  let buf = StringBuilder::new()
  buf.write_string("\n")
  render_xml_named_element(root_element_name, value, 0, indent_text, buf)
  buf.write_char('\n')
  buf.to_string()
}

///|
fn xml_render_document_error(
  value : Value,
  root_element_name : String,
  xml_version : String,
) -> String? {
  match xml_name_error(root_element_name, xml_version, "element") {
    Some(message) => return Some(message)
    None => xml_render_value_error(value, xml_version)
  }
}

///|
fn xml_render_value_error(value : Value, xml_version : String) -> String? {
  xml_render_nested_value_error(value, xml_version, true)
}

///|
pub fn render_value_as_xml_fragment(
  value : Value,
  indent_text : String,
) -> String {
  let buf = StringBuilder::new()
  render_xml_fragment_value(value, 0, indent_text, buf)
  buf.to_string()
}

///|
fn render_xml_named_element(
  name : String,
  value : Value,
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  if value is NullValue {
    return
  }
  match value {
    // Force before deciding whether this is a regular named node or an XML
    // helper object. Treating a thunk as inline content discards both the
    // requested element name and the resolved value's structural markers.
    ThunkValue(_) =>
      render_xml_named_element(
        name,
        force_eval_thunk(value),
        level,
        indent_text,
        buf,
      )
    ObjectValue(members) =>
      match xml_object_kind(members) {
        Some("Inline") =>
          match lookup_member(members, "value") {
            Some(inner) =>
              render_xml_inline_or_children(inner, level, indent_text, buf)
            None => ()
          }
        Some("CData") =>
          render_xml_regular_named_element(name, value, level, indent_text, buf)
        Some("Element") =>
          render_xml_element_object(members, level, indent_text, buf)
        Some("Comment") =>
          render_xml_comment_object(members, level, indent_text, buf)
        _ =>
          render_xml_regular_named_element(name, value, level, indent_text, buf)
      }
    _ => render_xml_regular_named_element(name, value, level, indent_text, buf)
  }
}

///|
fn render_xml_regular_named_element(
  name : String,
  value : Value,
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  write_xml_indent(buf, level, indent_text)
  if xml_value_is_block(value) {
    if xml_value_is_empty_block(value) {
      buf.write_char('<')
      write_xml_name(name, buf)
      buf.write_string("/>")
      return
    }
    let starts_inline = xml_value_starts_with_inline_child(value)
    let ends_inline = xml_value_ends_with_inline_child(value)
    buf.write_char('<')
    write_xml_name(name, buf)
    buf.write_char('>')
    if !starts_inline {
      buf.write_char('\n')
    }
    render_xml_children(value, level + 1, indent_text, buf)
    if !ends_inline {
      buf.write_char('\n')
      write_xml_indent(buf, level, indent_text)
    }
    buf.write_string("')
  } else {
    buf.write_char('<')
    write_xml_name(name, buf)
    buf.write_char('>')
    render_xml_inline_content(value, buf)
    buf.write_string("')
  }
}

///|
fn render_xml_element_object(
  members : Array[ValueMember],
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  let element_name = match lookup_member(members, "name") {
    Some(StringValue(s)) => s
    _ => "element"
  }
  let block_format = xml_element_is_block_format(members)
  let attributes = xml_element_attributes(members)
  let body = xml_element_body_members(members)
  if block_format {
    write_xml_indent(buf, level, indent_text)
  }
  if body.length() == 0 {
    buf.write_char('<')
    write_xml_name(element_name, buf)
    write_xml_attributes(attributes, buf)
    buf.write_string(">')
    return
  }
  let body_value = ObjectValue(body)
  if xml_value_is_block(body_value) {
    let starts_inline = xml_value_starts_with_inline_child(body_value)
    let ends_inline = xml_value_ends_with_inline_child(body_value)
    buf.write_char('<')
    write_xml_name(element_name, buf)
    write_xml_attributes(attributes, buf)
    buf.write_char('>')
    if !starts_inline {
      buf.write_char('\n')
    }
    render_xml_children(body_value, level + 1, indent_text, buf)
    if !ends_inline {
      buf.write_char('\n')
      if block_format {
        write_xml_indent(buf, level, indent_text)
      }
    }
    buf.write_string("')
  } else {
    buf.write_char('<')
    write_xml_name(element_name, buf)
    write_xml_attributes(attributes, buf)
    buf.write_char('>')
    render_xml_inline_content(body_value, buf)
    buf.write_string("')
  }
}

///|
fn render_xml_comment_object(
  members : Array[ValueMember],
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  let text = match lookup_member(members, "text") {
    Some(StringValue(s)) => s
    _ => ""
  }
  write_xml_indent(buf, level, indent_text)
  buf.write_string("")
}

///|
fn render_xml_children(
  value : Value,
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  let mut first = true
  let mut previous_inline = false
  match value {
    ObjectValue(members) =>
      for field in visible_members(members) {
        if field.value is NullValue {
          continue
        }
        let mut rendered = render_xml_member_to_string(
          field, level, indent_text,
        )
        let current_inline = xml_rendered_member_is_inline(
          rendered, level, indent_text,
        )
        if (first || previous_inline) && current_inline {
          rendered = xml_strip_leading_newline(rendered)
        }
        if !first {
          if !(previous_inline && current_inline) {
            buf.write_char('\n')
          }
        }
        buf.write_string(rendered)
        previous_inline = current_inline
        first = false
      }
    MappingValue(entries)
    | DefaultedMappingValue(_, entries, _)
    | MapValue(entries) =>
      for entry in entries {
        if entry.value is NullValue {
          continue
        }
        if !first {
          buf.write_char('\n')
        }
        render_xml_named_element(
          xml_coerce_key(entry.key),
          entry.value,
          level,
          indent_text,
          buf,
        )
        first = false
      }
    ListingValue(elements)
    | DefaultedListingValue(_, elements, _)
    | ListValue(elements)
    | SetValue(elements) =>
      for element in elements {
        if element is NullValue {
          continue
        }
        let current_inline = xml_fragment_renders_inline(element)
        if !first {
          if !(previous_inline && current_inline) {
            buf.write_char('\n')
          }
        }
        render_xml_fragment_value(element, level, indent_text, buf)
        previous_inline = current_inline
        first = false
      }
    _ => render_xml_fragment_value(value, level, indent_text, buf)
  }
}

///|
fn render_xml_member(
  field : ValueMember,
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  if field.name.has_prefix("@element$") {
    match xml_member_source_constructor(field.source) {
      Some("CData") =>
        match field.value {
          ObjectValue(members) =>
            match lookup_member(members, "text") {
              Some(StringValue(text)) => {
                write_xml_cdata(text, buf)
                return
              }
              _ => ()
            }
          _ => ()
        }
      Some("Comment") =>
        match field.value {
          ObjectValue(members) => {
            render_xml_comment_object(members, level, indent_text, buf)
            return
          }
          _ => ()
        }
      _ => ()
    }
    if xml_member_source_function_name(field.source) == Some("comment") {
      match field.value {
        ObjectValue(members) => {
          render_xml_comment_object(members, level, indent_text, buf)
          return
        }
        _ => ()
      }
    }
    match field.value {
      ObjectValue(members) => {
        let visible = visible_members(members)
        if visible.length() == 1 && visible[0].name == "text" {
          render_xml_comment_object(members, level, indent_text, buf)
          return
        }
      }
      _ => ()
    }
    render_xml_fragment_value(field.value, level, indent_text, buf)
    return
  }
  match xml_member_source_constructor(field.source) {
    Some("CData") => {
      let name = xml_member_element_name(field)
      write_xml_indent(buf, level, indent_text)
      buf.write_char('<')
      write_xml_name(name, buf)
      buf.write_char('>')
      match field.value {
        ObjectValue(members) =>
          match lookup_member(members, "text") {
            Some(StringValue(text)) => write_xml_cdata(text, buf)
            _ => ()
          }
        _ => render_xml_inline_content(field.value, buf)
      }
      buf.write_string("')
      return
    }
    Some("Comment") => {
      match field.value {
        ObjectValue(members) =>
          render_xml_comment_object(members, level, indent_text, buf)
        _ => render_xml_inline_content(field.value, buf)
      }
      return
    }
    _ => ()
  }
  if field.name.has_prefix("@subscript$") {
    match field.value {
      ObjectValue(pair_members) =>
        match
          (
            lookup_member(pair_members, "@key"),
            lookup_member(pair_members, "@value"),
          ) {
          (Some(key), Some(value)) =>
            render_xml_named_element(
              xml_coerce_key(key),
              value,
              level,
              indent_text,
              buf,
            )
          _ => ()
        }
      _ => ()
    }
    return
  }
  let name = xml_member_element_name(field)
  render_xml_named_element(name, field.value, level, indent_text, buf)
}

///|
fn render_xml_member_to_string(
  field : ValueMember,
  level : Int,
  indent_text : String,
) -> String {
  let buf = StringBuilder::new()
  render_xml_member(field, level, indent_text, buf)
  buf.to_string()
}

///|
fn xml_rendered_member_is_inline(
  rendered : String,
  level : Int,
  indent_text : String,
) -> Bool {
  if level == 0 {
    return xml_rendered_member_has_inline_prefix(rendered)
  }
  let prefix = xml_indent_string(level, indent_text)
  !rendered.has_prefix(prefix)
}

///|
fn xml_rendered_member_has_inline_prefix(rendered : String) -> Bool {
  !(rendered.has_prefix("<") || rendered.has_prefix(" "))
}

///|
fn xml_strip_leading_newline(rendered : String) -> String {
  if rendered.has_prefix("\n") {
    String::unsafe_substring(rendered, start=1, end=rendered.length())
  } else {
    rendered
  }
}

///|
fn xml_indent_string(level : Int, indent_text : String) -> String {
  let buf = StringBuilder::new()
  write_xml_indent(buf, level, indent_text)
  buf.to_string()
}

///|
fn xml_member_source_constructor(source : Expr?) -> String? {
  match source {
    Some(CallExpr(MemberAccess(Identifier(_), name), _)) =>
      match name {
        "CData" | "Comment" | "Inline" => Some(name)
        _ => None
      }
    _ => None
  }
}

///|
fn xml_member_source_function_name(source : Expr?) -> String? {
  match source {
    Some(CallExpr(Identifier(name), _)) => Some(name)
    _ => None
  }
}

///|
fn render_xml_fragment_value(
  value : Value,
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  // PKL-153f: directive short-circuit — `renderValue(new
  // RenderDirective {...})` flows the directive's `text` into the
  // fragment verbatim, ahead of the bare-`text`-member ↦ Comment
  // shortcut that would otherwise wrap it in ``.
  match render_directive_text(value) {
    Some(text) => {
      buf.write_string(text)
      return
    }
    None => ()
  }
  match value {
    ObjectValue(members) => {
      let visible = visible_members(members)
      if visible.length() == 1 && visible[0].name == "text" {
        render_xml_comment_object(members, level, indent_text, buf)
        return
      }
      match xml_object_kind(members) {
        Some("Inline") =>
          match lookup_member(members, "value") {
            Some(inner) =>
              render_xml_inline_or_children(inner, level, indent_text, buf)
            None => ()
          }
        Some("Element") =>
          render_xml_element_object(members, level, indent_text, buf)
        Some("Comment") =>
          render_xml_comment_object(members, level, indent_text, buf)
        _ =>
          if xml_value_is_block(value) {
            render_xml_children(value, level, indent_text, buf)
          } else {
            render_xml_inline_content(value, buf)
          }
      }
    }
    MappingValue(_) | DefaultedMappingValue(_, _, _) | MapValue(_) =>
      render_xml_children(value, level, indent_text, buf)
    ListingValue(elements)
    | DefaultedListingValue(_, elements, _)
    | ListValue(elements)
    | SetValue(elements) =>
      render_xml_fragment_elements(elements, level, indent_text, buf)
    _ => render_xml_inline_content(value, buf)
  }
}

///|
fn render_xml_fragment_elements(
  elements : Array[Value],
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  let mut first = true
  let mut previous_inline = false
  for element in elements {
    if element is NullValue {
      continue
    }
    let current_inline = xml_fragment_renders_inline(element)
    if !first && !(previous_inline && current_inline) {
      buf.write_char('\n')
    }
    render_xml_fragment_value(element, level, indent_text, buf)
    previous_inline = current_inline
    first = false
  }
}

///|
fn render_xml_inline_or_children(
  value : Value,
  level : Int,
  indent_text : String,
  buf : StringBuilder,
) -> Unit {
  if xml_value_is_block(value) || xml_value_is_xml_special(value) {
    render_xml_fragment_value(value, level, indent_text, buf)
  } else {
    render_xml_inline_content(value, buf)
  }
}

///|
fn render_xml_inline_content(value : Value, buf : StringBuilder) -> Unit {
  match render_directive_text(value) {
    Some(text) => {
      buf.write_string(text)
      return
    }
    None => ()
  }
  match value {
    ThunkValue(_) => render_xml_inline_content(force_eval_thunk(value), buf)
    IntValue(n) => buf.write_string("\{n}")
    FloatValue(d) => buf.write_string(render_float_text(d))
    BoolValue(true) => buf.write_string("true")
    BoolValue(false) => buf.write_string("false")
    StringValue(s) => write_xml_escaped_text(s, buf)
    NullValue => ()
    DeferredImportValue(_) => ()
    DurationValue(n, unit) => {
      buf.write_string("\{n} ")
      buf.write_string(unit)
    }
    DataSizeValue(n, unit) => {
      buf.write_string("\{n} ")
      buf.write_string(unit)
    }
    RegexValue(pattern) => write_xml_escaped_text(pattern, buf)
    BytesValue(bytes) => buf.write_string(@base64.encode(bytes[:]))
    ObjectValue(members) =>
      match xml_object_kind(members) {
        Some("CData") =>
          match lookup_member(members, "text") {
            Some(StringValue(text)) => write_xml_cdata(text, buf)
            _ => ()
          }
        Some("Inline") =>
          match lookup_member(members, "value") {
            Some(inner) => render_xml_inline_content(inner, buf)
            None => ()
          }
        _ =>
          for field in visible_members(members) {
            if field.value is NullValue {
              continue
            }
            render_xml_inline_content(field.value, buf)
          }
      }
    ListingValue(elements)
    | DefaultedListingValue(_, elements, _)
    | ListValue(elements)
    | SetValue(elements) =>
      for element in elements {
        render_xml_inline_content(element, buf)
      }
    MappingValue(entries)
    | DefaultedMappingValue(_, entries, _)
    | MapValue(entries) =>
      for entry in entries {
        render_xml_inline_content(entry.value, buf)
      }
    FunctionValue(_, _, _, _, _) => ()
    PairValue(first, second) => {
      render_xml_inline_content(first, buf)
      render_xml_inline_content(second, buf)
    }
    IntSeqValue(start, end_v, step) =>
      for element in intseq_materialize(start, end_v, step) {
        render_xml_inline_content(element, buf)
      }
  }
}

///|
fn xml_value_is_block(value : Value) -> Bool {
  if render_directive_text(value) is Some(_) {
    return false
  }
  match value {
    ObjectValue(members) =>
      match xml_object_kind(members) {
        Some("CData") | Some("Inline") | Some("Comment") => false
        Some("Element") =>
          xml_element_is_block_format(members) &&
          xml_element_body_members(members).length() > 0
        _ => {
          for field in visible_members(members) {
            if field.value is NullValue {
              continue
            }
            if field.name.has_prefix("@element$") {
              if xml_value_is_block(field.value) ||
                xml_value_is_xml_special(field.value) {
                return true
              }
              continue
            }
            return true
          }
          false
        }
      }
    MappingValue(entries)
    | DefaultedMappingValue(_, entries, _)
    | MapValue(entries) => entries.length() > 0
    ListingValue(elements)
    | DefaultedListingValue(_, elements, _)
    | ListValue(elements)
    | SetValue(elements) => {
      for element in elements {
        if xml_value_is_block(element) || xml_value_is_xml_special(element) {
          return true
        }
      }
      false
    }
    _ => false
  }
}

///|
fn xml_value_starts_with_inline_child(value : Value) -> Bool {
  match value {
    ObjectValue(members) =>
      for field in visible_members(members) {
        if field.value is NullValue {
          continue
        }
        return xml_member_renders_inline(field)
      }
    ListingValue(elements)
    | DefaultedListingValue(_, elements, _)
    | ListValue(elements)
    | SetValue(elements) =>
      for element in elements {
        if element is NullValue {
          continue
        }
        return xml_fragment_renders_inline(element)
      }
    _ => ()
  }
  false
}

///|
fn xml_value_ends_with_inline_child(value : Value) -> Bool {
  let mut found = false
  let mut inline = false
  match value {
    ObjectValue(members) =>
      for field in visible_members(members) {
        if field.value is NullValue {
          continue
        }
        found = true
        inline = xml_member_renders_inline(field)
      }
    ListingValue(elements)
    | DefaultedListingValue(_, elements, _)
    | ListValue(elements)
    | SetValue(elements) =>
      for element in elements {
        if element is NullValue {
          continue
        }
        found = true
        inline = xml_fragment_renders_inline(element)
      }
    _ => ()
  }
  found && inline
}

///|
fn xml_member_renders_inline(field : ValueMember) -> Bool {
  if xml_member_source_constructor(field.source) is Some("Inline") {
    return xml_fragment_renders_inline(field.value)
  }
  if field.name.has_prefix("@element$") {
    return xml_fragment_renders_inline(field.value)
  }
  match field.value {
    ObjectValue(members) =>
      match xml_object_kind(members) {
        Some("Inline") => xml_fragment_renders_inline(field.value)
        _ => false
      }
    _ => false
  }
}

///|
fn xml_fragment_renders_inline(value : Value) -> Bool {
  if render_directive_text(value) is Some(_) {
    return true
  }
  match value {
    ObjectValue(members) =>
      match xml_object_kind(members) {
        Some("Inline") =>
          match lookup_member(members, "value") {
            Some(inner) => xml_fragment_renders_inline(inner)
            None => true
          }
        Some("Element") => !xml_element_is_block_format(members)
        Some("CData") => true
        Some("Comment") => false
        _ => !xml_value_is_block(value)
      }
    _ => !xml_value_is_block(value)
  }
}

///|
fn xml_element_is_block_format(members : Array[ValueMember]) -> Bool {
  match lookup_member(members, "isBlockFormat") {
    Some(BoolValue(false)) => false
    _ => true
  }
}

///|
fn xml_value_is_empty_block(value : Value) -> Bool {
  match value {
    ObjectValue(members) => {
      for field in visible_members(members) {
        if field.value is NullValue {
          continue
        }
        return false
      }
      true
    }
    MappingValue(entries)
    | DefaultedMappingValue(_, entries, _)
    | MapValue(entries) => entries.length() == 0
    _ => false
  }
}

///|
fn xml_value_is_xml_special(value : Value) -> Bool {
  match value {
    ObjectValue(members) => xml_object_kind(members) is Some(_)
    _ => false
  }
}

///|
fn xml_object_kind(members : Array[ValueMember]) -> String? {
  match lookup_member(members, "__xmlKind") {
    Some(StringValue(kind)) => return Some(kind)
    _ => ()
  }
  match lookup_member(members, "__class") {
    Some(StringValue(class_name)) =>
      match xml_kind_from_class_name(class_name) {
        Some(kind) => return Some(kind)
        None => ()
      }
    _ => ()
  }
  match find_object_class_tag(members) {
    Some(class_name) =>
      match xml_kind_from_class_name(class_name) {
        Some(kind) => return Some(kind)
        None => ()
      }
    None => ()
  }
  let visible = visible_members(members)
  if visible.length() == 1 && visible[0].name == "value" {
    return Some("Inline")
  }
  let mut has_name = false
  let mut element_shaped = true
  for field in visible {
    if field.name == "name" {
      has_name = true
    } else if field.name == "isBlockFormat" ||
      field.name.has_prefix("@element$") ||
      field.name.has_prefix("@subscript$") {
      ()
    } else {
      element_shaped = false
    }
  }
  if has_name && element_shaped {
    return Some("Element")
  }
  None
}

///|
fn xml_kind_from_class_name(class_name : String) -> String? {
  if class_name.has_suffix("Element") {
    Some("Element")
  } else if class_name.has_suffix("Inline") {
    Some("Inline")
  } else if class_name.has_suffix("CData") {
    Some("CData")
  } else if class_name.has_suffix("Comment") {
    Some("Comment")
  } else {
    None
  }
}

///|
fn xml_element_body_members(members : Array[ValueMember]) -> Array[ValueMember] {
  let body : Array[ValueMember] = []
  for field in visible_members(members) {
    if xml_member_is_element_metadata(field.name) {
      continue
    }
    body.push(field)
  }
  body
}

///|
fn xml_member_is_element_metadata(name : String) -> Bool {
  name == "name" || name == "isBlockFormat" || name == "attributes"
}

///|
fn xml_member_element_name(field : ValueMember) -> String {
  for annotation in field.annotations {
    if annotation.class_name.has_suffix(".Property") ||
      annotation.class_name == "Property" {
      match xml_annotation_string_field(annotation.body_text, "name") {
        Some(name) => return name
        None => ()
      }
    }
  }
  field.name
}

///|
fn xml_annotation_string_field(body : String, field_name : String) -> String? {
  let needle = field_name + " = \""
  match body.find(needle) {
    Some(start) => {
      let value_start = start + needle.length()
      let rest = String::unsafe_substring(
        body,
        start=value_start,
        end=body.length(),
      )
      match rest.find("\"") {
        Some(end_rel) =>
          Some(String::unsafe_substring(rest, start=0, end=end_rel))
        None => None
      }
    }
    None => None
  }
}

///|
fn xml_coerce_key(value : Value) -> String {
  // PKL-153f: a RenderDirective key uses its `text` verbatim — Apple
  // Pkl accepts an arbitrary string (even emoji) as the element name
  // when it came from a directive.
  match render_directive_text(value) {
    Some(text) => return text
    None => ()
  }
  match value {
    StringValue(s) => s
    IntValue(n) => "\{n}"
    BoolValue(true) => "true"
    BoolValue(false) => "false"
    NullValue => "null"
    _ => {
      let buf = StringBuilder::new()
      render_pcf_inline(value, 0, false, buf)
      buf.to_string()
    }
  }
}

///|
fn xml_element_attributes(members : Array[ValueMember]) -> Array[ValueEntry] {
  let attributes : Array[ValueEntry] = []
  match lookup_member(members, "attributes") {
    Some(value) => append_xml_attribute_entries(attributes, value)
    None => ()
  }
  attributes
}

///|
fn append_xml_attribute_entries(out : Array[ValueEntry], value : Value) -> Unit {
  match value {
    MappingValue(entries)
    | DefaultedMappingValue(_, entries, _)
    | MapValue(entries) =>
      for entry in entries {
        out.push(entry)
      }
    ObjectValue(members) =>
      for field in visible_members(members) {
        if field.value is NullValue {
          continue
        }
        if field.name.has_prefix("@subscript$") {
          match field.value {
            ObjectValue(pair_members) =>
              match
                (
                  lookup_member(pair_members, "@key"),
                  lookup_member(pair_members, "@value"),
                ) {
                (Some(key), Some(attr_value)) =>
                  out.push({ key, value: attr_value })
                _ => ()
              }
            _ => ()
          }
        } else {
          out.push({ key: StringValue(field.name), value: field.value })
        }
      }
    _ => ()
  }
}

///|
fn write_xml_attributes(
  attributes : Array[ValueEntry],
  buf : StringBuilder,
) -> Unit {
  for entry in attributes {
    buf.write_char(' ')
    write_xml_name(xml_coerce_key(entry.key), buf)
    buf.write_string("=\"")
    write_xml_escaped_attr(xml_attribute_value_text(entry.value), buf)
    buf.write_char('"')
  }
}

///|
fn xml_attribute_value_text(value : Value) -> String {
  match render_directive_text(value) {
    Some(text) => return text
    None => ()
  }
  match value {
    StringValue(s) => s
    IntValue(n) => n.to_string()
    FloatValue(d) => render_float_text(d)
    BoolValue(true) => "true"
    BoolValue(false) => "false"
    _ => render_pcf_value_inline(value)
  }
}

///|
fn xml_render_nested_value_error(
  value : Value,
  xml_version : String,
  top_level : Bool,
) -> String? {
  match xml_unsupported_value_error(value, top_level) {
    Some(message) => return Some(message)
    None => ()
  }
  match value {
    ObjectValue(members) =>
      match xml_object_kind(members) {
        Some("Element") => xml_element_render_error(members, xml_version)
        Some("Inline") =>
          match lookup_member(members, "value") {
            Some(inner) =>
              match xml_nested_inline_special_kind(inner) {
                Some(kind) => Some(xml_unsupported_here_message(kind, inner))
                None => xml_render_nested_value_error(inner, xml_version, false)
              }
            None => None
          }
        Some("CData") | Some("Comment") => None
        _ => xml_object_render_error(members, xml_version)
      }
    MappingValue(entries)
    | DefaultedMappingValue(_, entries, _)
    | MapValue(entries) => xml_entries_render_error(entries, xml_version)
    ListingValue(elements)
    | DefaultedListingValue(_, elements, _)
    | ListValue(elements)
    | SetValue(elements) => xml_elements_render_error(elements, xml_version)
    _ => None
  }
}

///|
fn xml_nested_inline_special_kind(value : Value) -> String? {
  match value {
    ObjectValue(members) =>
      match xml_object_kind(members) {
        Some("Element") => Some("Element")
        Some("Inline") => Some("Inline")
        _ => None
      }
    _ => None
  }
}

///|
fn xml_unsupported_here_message(kind : String, value : Value) -> String {
  "`xml.\{kind}` is not supported here. Value: \{xml_unsupported_here_value_text(kind, value)}"
}

///|
fn xml_unsupported_here_value_text(kind : String, value : Value) -> String {
  match (kind, value) {
    ("Element", ObjectValue(members)) => {
      let name = match lookup_member(members, "name") {
        Some(StringValue(s)) => s
        _ => "element"
      }
      "new Dynamic { _isXmlElement = true; name = \"\{name}\"; attributes {}; isBlockF..."
    }
    ("Inline", ObjectValue(members)) =>
      match lookup_member(members, "value") {
        Some(inner) =>
          "new Inline { value \{xml_placeholder_value_text(inner)} }"
        None => "new Inline {}"
      }
    _ => render_pcf_value_inline(value)
  }
}

///|
fn xml_placeholder_value_text(value : Value) -> String {
  match value {
    ObjectValue(members) => {
      let buf = StringBuilder::new()
      buf.write_string("{ ")
      let mut first = true
      for field in visible_members(members) {
        if field.value is NullValue {
          continue
        }
        if !first {
          buf.write_string("; ")
        }
        buf.write_string(field.name)
        buf.write_string(" = ?")
        first = false
      }
      buf.write_string(" }")
      buf.to_string()
    }
    _ => render_pcf_value_inline(value)
  }
}

///|
fn xml_unsupported_value_error(value : Value, top_level : Bool) -> String? {
  match value {
    NullValue =>
      if top_level {
        Some(xml_unsupported_value_message(value))
      } else {
        None
      }
    DurationValue(_, _)
    | DataSizeValue(_, _)
    | FunctionValue(_, _, _, _, _)
    | PairValue(_, _)
    | IntSeqValue(_, _, _) => Some(xml_unsupported_value_message(value))
    ObjectValue(members) =>
      match reflect_kind(members) {
        Some("Class") | Some("TypeAlias") =>
          Some(xml_unsupported_value_message(value))
        _ => None
      }
    _ => None
  }
}

///|
fn xml_unsupported_value_message(value : Value) -> String {
  "Cannot render value of type `\{xml_unsupported_type_name(value)}` as XML. Value: \{xml_unsupported_value_text(value)}"
}

///|
fn xml_unsupported_type_name(value : Value) -> String {
  match value {
    ObjectValue(members) =>
      match reflect_kind(members) {
        Some(kind) => kind
        None => eval_value_type_name(value)
      }
    FunctionValue(parameters, _, _, _, _) => "Function\{parameters.length()}"
    _ => eval_value_type_name(value)
  }
}

///|
fn xml_unsupported_value_text(value : Value) -> String {
  match value {
    ObjectValue(members) =>
      match reflect_kind(members) {
        Some("Class") | Some("TypeAlias") =>
          match lookup_member(members, "__qualified_name") {
            Some(StringValue(name)) => name
            _ =>
              match lookup_member(members, "name") {
                Some(StringValue(name)) => name
                _ => render_pcf_value_inline(value)
              }
          }
        _ => render_pcf_value_inline(value)
      }
    FunctionValue(parameters, _, _, _, _) =>
      "new Function\{parameters.length()} {}"
    _ => render_pcf_value_inline(value)
  }
}

///|
fn xml_element_render_error(
  members : Array[ValueMember],
  xml_version : String,
) -> String? {
  let element_name = match lookup_member(members, "name") {
    Some(StringValue(s)) => s
    _ => "element"
  }
  match xml_name_error(element_name, xml_version, "element") {
    Some(message) => return Some(message)
    None => ()
  }
  let attributes = xml_element_attributes(members)
  for entry in attributes {
    let name = xml_coerce_key(entry.key)
    match xml_name_error(name, xml_version, "attribute") {
      Some(message) => return Some(message)
      None => ()
    }
    match xml_render_nested_value_error(entry.value, xml_version, false) {
      Some(message) => return Some(message)
      None => ()
    }
  }
  xml_object_render_error(xml_element_body_members(members), xml_version)
}

///|
fn xml_object_render_error(
  members : Array[ValueMember],
  xml_version : String,
) -> String? {
  for field in visible_members(members) {
    if field.value is NullValue {
      continue
    }
    if field.name.has_prefix("@element$") {
      match xml_render_nested_value_error(field.value, xml_version, false) {
        Some(message) => return Some(message)
        None => ()
      }
      continue
    }
    if field.name.has_prefix("@subscript$") {
      match field.value {
        ObjectValue(pair_members) =>
          match
            (
              lookup_member(pair_members, "@key"),
              lookup_member(pair_members, "@value"),
            ) {
            (Some(key), Some(entry_value)) => {
              match
                xml_name_error(xml_coerce_key(key), xml_version, "element") {
                Some(message) => return Some(message)
                None => ()
              }
              match
                xml_render_nested_value_error(entry_value, xml_version, false) {
                Some(message) => return Some(message)
                None => ()
              }
            }
            _ => ()
          }
        _ => ()
      }
      continue
    }
    let name = xml_member_element_name(field)
    match xml_name_error(name, xml_version, "element") {
      Some(message) => return Some(message)
      None => ()
    }
    match xml_render_nested_value_error(field.value, xml_version, false) {
      Some(message) => return Some(message)
      None => ()
    }
  }
  None
}

///|
fn xml_entries_render_error(
  entries : Array[ValueEntry],
  xml_version : String,
) -> String? {
  for entry in entries {
    if entry.value is NullValue {
      continue
    }
    // PKL-153f: skip the name-validity check for keys that came from
    // a RenderDirective — Apple Pkl emits the directive's text raw
    // even when the resulting element name isn't a valid XML 1.0
    // identifier.
    if render_directive_text(entry.key) is None {
      match xml_name_error(xml_coerce_key(entry.key), xml_version, "element") {
        Some(message) => return Some(message)
        None => ()
      }
    }
    match xml_render_nested_value_error(entry.value, xml_version, false) {
      Some(message) => return Some(message)
      None => ()
    }
  }
  None
}

///|
fn xml_elements_render_error(
  elements : Array[Value],
  xml_version : String,
) -> String? {
  for element in elements {
    if element is NullValue {
      continue
    }
    match xml_render_nested_value_error(element, xml_version, false) {
      Some(message) => return Some(message)
      None => ()
    }
  }
  None
}

///|
fn xml_name_error(
  name : String,
  xml_version : String,
  kind : String,
) -> String? {
  if xml_name_is_valid(name, xml_version) {
    None
  } else {
    Some("Invalid XML \{xml_version} \{kind} name: `\{name}`")
  }
}

///|
fn xml_name_is_valid(name : String, xml_version : String) -> Bool {
  if name.length() == 0 {
    return false
  }
  for c in name {
    if xml_version == "1.0" {
      if !xml_name_char_is_ascii(c) {
        return false
      }
    } else if !xml_name_char_is_xml11(c) {
      return false
    }
  }
  true
}

///|
fn xml_name_char_is_ascii(c : Char) -> Bool {
  (c >= 'A' && c <= 'Z') ||
  (c >= 'a' && c <= 'z') ||
  (c >= '0' && c <= '9') ||
  c == '_' ||
  c == '-' ||
  c == '.' ||
  c == ':'
}

///|
fn xml_name_char_is_xml11(c : Char) -> Bool {
  if c <= ' ' {
    return false
  }
  c != '<' && c != '>' && c != '&' && c != '"'
}

///|
fn write_xml_indent(
  buf : StringBuilder,
  level : Int,
  indent_text : String,
) -> Unit {
  for _i = 0; _i < level; _i = _i + 1 {
    buf.write_string(indent_text)
  }
}

///|
fn write_xml_name(name : String, buf : StringBuilder) -> Unit {
  buf.write_string(name)
}

///|
fn write_xml_escaped_text(value : String, buf : StringBuilder) -> Unit {
  for c in value {
    match c {
      '&' => buf.write_string("&")
      '<' => buf.write_string("<")
      '>' => buf.write_string(">")
      '"' => buf.write_string(""")
      _ => buf.write_char(c)
    }
  }
}

///|
fn write_xml_escaped_attr(value : String, buf : StringBuilder) -> Unit {
  for c in value {
    match c {
      '&' => buf.write_string("&")
      '<' => buf.write_string("<")
      '>' => buf.write_string(">")
      '"' => buf.write_string(""")
      _ => buf.write_char(c)
    }
  }
}

///|
fn write_xml_cdata(value : String, buf : StringBuilder) -> Unit {
  buf.write_string("") {
      Some(idx) => {
        let end = start + idx + 2
        buf.write_string(String::unsafe_substring(value, start~, end~))
        buf.write_string("]]> {
        buf.write_string(rest)
        start = value.length()
      }
    }
  }
  buf.write_string("]]>")
}