///|
/// 查询参数构造助手。
///
/// 六个域子模块都要拼 `?page=1&per_page=20` 这类查询串:可选参数不传时
/// 必须整个键都不出现(传 `null` 会被服务端的 `strconv` 解析成空串或报错),
/// 所以这里收「键 + 可选值」的列表,只把 `Some` 的写进 JSON 对象。
///
/// 交给 moonhttp 的默认序列化器拼 URL:数字会写成 `1` 而不是 `1.0`,
/// 字符串会做百分号编码(路径里带中文、空格都由它负责)。

///|
/// 把 `键 -> 可选值` 列表变成查询参数对象。
///
/// ```moonbit nocheck
/// let query = @core.query_json([
///   ("page", Some(Json::number(1))),
///   ("path", Some(Json::string("/docs"))),
///   ("refresh", None),
/// ])
/// // => {"page": 1, "path": "/docs"}
/// ```
pub fn query_json(pairs : Array[(String, Json?)]) -> Json {
  let fields : Map[String, Json] = Map([])
  for pair in pairs {
    let (key, value) = pair
    match value {
      Some(value) => fields.set(key, value)
      None => ()
    }
  }
  Json::object(fields)
}

///|
/// 整数查询参数的便捷写法(`Json::number` 收的是 `Double`)。
pub fn query_int(value : Int) -> Json {
  Json::number(value.to_double())
}

///|
/// 字符串查询参数的便捷写法。
pub fn query_string(value : String) -> Json {
  Json::string(value)
}

///|
/// 分页参数的键值对:`page` 与 `per_page`,未提供的不会出现。
///
/// 服务端对分页参数很宽容(`page < 1` 视作 1、`per_page < 1` 视作不限),
/// 所以调用方不传就是「用服务端默认行为」。
///
/// ```moonbit nocheck
/// let query = @core.query_json(@core.page_pairs(page=1))
/// // => {"page": 1}
/// ```
pub fn page_pairs(page? : Int, per_page? : Int) -> Array[(String, Json?)] {
  let pairs : Array[(String, Json?)] = []
  match page {
    Some(value) => pairs.push(("page", Some(query_int(value))))
    None => ()
  }
  match per_page {
    Some(value) => pairs.push(("per_page", Some(query_int(value))))
    None => ()
  }
  pairs
}