///|

///|
/// Helper functions

///|

///|
fn cc_parse_directive(
  directive : String,
  cc : CacheControl,
) -> Result[CacheControl, CacheError] {
  let eq_pos = cc_find_char(directive, 61)
  match eq_pos {
    Some(pos) => {
      let name = cc_to_lower_case(
        cc_trim_string(cc_string_slice(directive, 0, pos)),
      )
      let value = cc_trim_string(cc_string_slice_from(directive, pos + 1))
      let value_unquoted = if value.length() >= 2 &&
        cc_char_at(value, 0) == 34 &&
        cc_char_at(value, value.length() - 1) == 34 {
        cc_string_slice(value, 1, value.length() - 1)
      } else {
        value
      }
      match name {
        "max-age" =>
          match cc_parse_u64(value_unquoted) {
            Some(secs) => Ok(cc_set_max_age(cc, secs))
            None => Err(InvalidNumber)
          }
        "s-maxage" =>
          match cc_parse_u64(value_unquoted) {
            Some(secs) => Ok(cc_set_s_maxage(cc, secs))
            None => Err(InvalidNumber)
          }
        "max-stale" =>
          match cc_parse_u64(value_unquoted) {
            Some(secs) => Ok(cc_set_max_stale(cc, secs))
            None => Err(InvalidNumber)
          }
        "min-fresh" =>
          match cc_parse_u64(value_unquoted) {
            Some(secs) => Ok(cc_set_min_fresh(cc, secs))
            None => Err(InvalidNumber)
          }
        "stale-while-revalidate" =>
          match cc_parse_u64(value_unquoted) {
            Some(secs) => Ok(cc_set_stale_while_revalidate(cc, secs))
            None => Err(InvalidNumber)
          }
        "stale-if-error" =>
          match cc_parse_u64(value_unquoted) {
            Some(secs) => Ok(cc_set_stale_if_error(cc, secs))
            None => Err(InvalidNumber)
          }
        _ => Ok(cc)
      }
    }
    None => {
      let name = cc_to_lower_case(directive)
      match name {
        "no-cache" => Ok(cc_set_no_cache(cc))
        "no-store" => Ok(cc_set_no_store(cc))
        "no-transform" => Ok(cc_set_no_transform(cc))
        "only-if-cached" => Ok(cc_set_only_if_cached(cc))
        "must-revalidate" => Ok(cc_set_must_revalidate(cc))
        "proxy-revalidate" => Ok(cc_set_proxy_revalidate(cc))
        "must-understand" => Ok(cc_set_must_understand(cc))
        "public" => Ok(cc_set_public(cc))
        "private" => Ok(cc_set_private(cc))
        "immutable" => Ok(cc_set_immutable(cc))
        "max-stale" => Ok(cc_set_max_stale_unlimited(cc))
        _ => Ok(cc)
      }
    }
  }
}

///|
fn cc_set_max_age(cc : CacheControl, secs : Int) -> CacheControl {
  {
    max_age: Some(secs),
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_s_maxage(cc : CacheControl, secs : Int) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: Some(secs),
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_max_stale(cc : CacheControl, secs : Int) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: Some(secs),
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_min_fresh(cc : CacheControl, secs : Int) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: Some(secs),
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_stale_while_revalidate(cc : CacheControl, secs : Int) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: Some(secs),
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_stale_if_error(cc : CacheControl, secs : Int) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: Some(secs),
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_no_cache(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: true,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_no_store(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: true,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_no_transform(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: true,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_only_if_cached(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: true,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_must_revalidate(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: true,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_proxy_revalidate(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: true,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_must_understand(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: true,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_public(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: true,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_private(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: true,
    immutable: cc.immutable,
  }
}

///|
fn cc_set_immutable(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: cc.max_stale,
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: true,
  }
}

///|
fn cc_set_max_stale_unlimited(cc : CacheControl) -> CacheControl {
  {
    max_age: cc.max_age,
    s_maxage: cc.s_maxage,
    max_stale: Some(2147483647),
    min_fresh: cc.min_fresh,
    stale_while_revalidate: cc.stale_while_revalidate,
    stale_if_error: cc.stale_if_error,
    no_cache: cc.no_cache,
    no_store: cc.no_store,
    no_transform: cc.no_transform,
    only_if_cached: cc.only_if_cached,
    must_revalidate: cc.must_revalidate,
    proxy_revalidate: cc.proxy_revalidate,
    must_understand: cc.must_understand,
    public: cc.public,
    private: cc.private,
    immutable: cc.immutable,
  }
}

///|
fn cc_trim_string(s : String) -> String {
  str_trim_string(s)
}

///|
fn cc_char_at(s : String, idx : Int) -> Int {
  str_char_at(s, idx)
}

///|
fn cc_string_slice(s : String, start : Int, end : Int) -> String {
  str_string_slice(s, start, end)
}

///|
fn cc_string_slice_from(s : String, start : Int) -> String {
  str_string_slice_from(s, start)
}

///|
fn cc_find_char(s : String, target : Int) -> Int? {
  let mut idx = 0
  while idx < s.length() {
    if cc_char_at(s, idx) == target {
      return Some(idx)
    }
    idx = idx + 1
  }
  None
}

///|
fn cc_split_string(s : String, sep : String) -> Array[String] {
  str_split_string(s, sep)
}

///|
fn cc_to_lower_case(s : String) -> String {
  str_to_lower_case(s)
}

///|
fn cc_parse_u64(s : String) -> Int? {
  let mut result = 0
  let mut idx = 0
  let len = s.length()
  if len == 0 {
    return None
  }
  while idx < len {
    let ci = cc_char_at(s, idx)
    if ci >= 48 && ci <= 57 {
      result = result * 10 + (ci - 48)
      if result < 0 {
        return None
      }
    } else {
      return None
    }
    idx = idx + 1
  }
  Some(result)
}

///|
fn cc_join_strings(strs : Array[String], sep : String) -> String {
  let mut result = ""
  let mut idx = 0
  while idx < strs.length() {
    if idx > 0 {
      result = result + sep
    }
    result = result + strs[idx]
    idx = idx + 1
  }
  result
}