///|
fn[T] pdf_print_tree_root(
  output : Array[String],
  indent : String,
  value : T,
  get_name : (T) -> String,
  get_children : (T) -> Array[T],
) -> Unit {
  output.push(get_name(value))
  output.push("\n")
  let children = get_children(value)
  for index in 0.. String,
  get_children : (T) -> Array[T],
) -> Unit {
  output.write_string_utf16le(get_name(value))
  output.write_string_utf16le("\n")
  let children = get_children(value)
  for index in 0.. String,
  get_children : (T) -> Array[T],
  line_prefix? : String = "",
) -> Unit {
  output.write_string_utf16le(line_prefix)
  pdf_print_tree_root_to_buffer(
    output, line_prefix, value, get_name, get_children,
  )
}

///|
/// Module-spelled compatibility wrapper for cpdfprinttree's `to_buffer`.
pub fn[T] pdf_printtree_to_buffer(
  output : @buffer.Buffer,
  value : T,
  get_name : (T) -> String,
  get_children : (T) -> Array[T],
  line_prefix? : String = "",
) -> Unit {
  pdf_print_tree_to_buffer(output, value, get_name, get_children, line_prefix~)
}

///|
/// Render a tree to a string using cpdf's line-drawing layout.
///
/// `get_name` returns the label for a node, and `get_children` returns its
/// children in display order. `line_prefix` is prepended to every rendered line.
pub fn[T] pdf_print_tree_to_string(
  value : T,
  get_name : (T) -> String,
  get_children : (T) -> Array[T],
  line_prefix? : String = "",
) -> String {
  let output : Array[String] = [line_prefix]
  pdf_print_tree_root(output, line_prefix, value, get_name, get_children)
  output.join("")
}

///|
/// Module-spelled compatibility wrapper for cpdfprinttree's `to_string`.
pub fn[T] pdf_printtree_to_string(
  value : T,
  get_name : (T) -> String,
  get_children : (T) -> Array[T],
  line_prefix? : String = "",
) -> String {
  pdf_print_tree_to_string(value, get_name, get_children, line_prefix~)
}