// Port of sqlglot/time.py

///|
/// Converts a time string given a mapping (Python `format_time`). Returns
/// `None` for an empty string.
pub fn format_time_opt(
  string : String,
  mapping : Map[String, String],
  trie? : Trie,
) -> String? {
  if string.is_empty() {
    return None
  }
  let s = string.to_array()
  let size = s.length()
  let trie = match trie {
    Some(t) if !t.children.is_empty() || t.terminal => t
    _ => trie_of_keys(mapping)
  }
  let mut start = 0
  let mut end = 1
  let mut current = trie
  let chunks : Array[String] = []
  let mut sym : String? = None
  while end <= size {
    let mut chars = String::from_array(s[start:end])
    let (result, next) = in_trie_chars(current, [s[end - 1]])
    current = next
    if result == Failed {
      match sym {
        Some(sy) => {
          end -= 1
          chars = sy
          sym = None
        }
        None => {
          chars = s[start].to_string()
          end = start + 1
        }
      }
      start += py_len(chars)
      chunks.push(chars)
      current = trie
    } else if result == Exists {
      sym = Some(chars)
    }
    end += 1
    if result != Failed && end > size {
      chunks.push(chars)
    }
  }
  let sb = StringBuilder()
  for c in chunks {
    sb.write_string(
      match mapping.get(c) {
        Some(m) => m
        None => c
      },
    )
  }
  Some(sb.to_string())
}

///|
/// `format_time` returning Python's `str(None)` for empty input, which is what
/// `exp.Literal.string(format_time(...))` produces.
pub fn format_time(
  string : String,
  mapping : Map[String, String],
  trie : Trie,
) -> String {
  match format_time_opt(string, mapping, trie~) {
    Some(s) => s
    None => "None"
  }
}