///|
/// 配置对象的 JSON 序列化 / 反序列化,用于 CLI 与脚本持久化水印参数。
///
/// 注意:`seed`(Int64)与 `color`(Int)在 JSON 中表示为 Number;
/// Int64 超过 2^53 时会丢失精度(水印种子常规取值远小于该值,可忽略)。
///|
/// 将字段对序列化为 JSON 字符串(三个配置类型共用)。
fn stringify_cfg(pairs : Array[(String, Json)]) -> String {
Json::stringify(Json::object(Map(pairs)))
}
///|
/// 将 `TextConfig` 序列化为 JSON 字符串。
pub fn text_config_to_json(cfg : TextConfig) -> String {
stringify_cfg([
("text", Json::string(cfg.text)),
("x", Json::number(cfg.x.to_double())),
("y", Json::number(cfg.y.to_double())),
("font_size", Json::number(cfg.font_size.to_double())),
("opacity", Json::number(cfg.opacity)),
("angle", Json::number(cfg.angle)),
("color", Json::number(cfg.color.to_double())),
("font", Json::string(cfg.font)),
("auto_shrink", Json::boolean(cfg.auto_shrink)),
])
}
///|
/// 从 JSON 字符串解析 `TextConfig`;字段缺失或格式错误返回 `None`。
pub fn text_config_from_json(s : String) -> TextConfig? {
text_cfg_from_obj(@json.parse(s)) catch {
_ => None
}
}
///|
fn text_cfg_from_obj(j : Json) -> TextConfig? {
match j {
Object(m) => {
// 向后兼容:旧 JSON 缺 font → "ascii5x7";缺 angle(早期版本无该字段)→ 0.0
let font = match j_str(m, "font") {
Some(f) => f
None => "ascii5x7"
}
let angle = match j_dbl(m, "angle") {
Some(v) => v
None => 0.0
}
let auto_shrink = match j_bool(m, "auto_shrink") {
Some(v) => v
None => false
}
match
(
j_str(m, "text"),
j_int(m, "x"),
j_int(m, "y"),
j_int(m, "font_size"),
j_dbl(m, "opacity"),
j_int(m, "color"),
) {
(
Some(text),
Some(x),
Some(y),
Some(font_size),
Some(opacity),
Some(color),
) =>
Some({
text,
x,
y,
font_size,
opacity,
angle,
color,
font,
auto_shrink,
})
_ => None
}
}
_ => None
}
}
///|
/// 将 `DotConfig` 序列化为 JSON 字符串。
pub fn dot_config_to_json(cfg : DotConfig) -> String {
stringify_cfg([
("density", Json::number(cfg.density)),
("dot_size", Json::number(cfg.dot_size.to_double())),
("seed", Json::number(cfg.seed.to_double())),
("opacity", Json::number(cfg.opacity)),
("grid_offset_x", Json::number(cfg.grid_offset_x.to_double())),
("grid_offset_y", Json::number(cfg.grid_offset_y.to_double())),
])
}
///|
/// 从 JSON 字符串解析 `DotConfig`;字段缺失或格式错误返回 `None`。
pub fn dot_config_from_json(s : String) -> DotConfig? {
dot_cfg_from_obj(@json.parse(s)) catch {
_ => None
}
}
///|
fn dot_cfg_from_obj(j : Json) -> DotConfig? {
match j {
Object(m) =>
match
(
j_dbl(m, "density"),
j_int(m, "dot_size"),
j_i64(m, "seed"),
j_dbl(m, "opacity"),
j_int(m, "grid_offset_x"),
j_int(m, "grid_offset_y"),
) {
(
Some(density),
Some(dot_size),
Some(seed),
Some(opacity),
Some(grid_offset_x),
Some(grid_offset_y),
) =>
Some({
density,
dot_size,
seed,
opacity,
grid_offset_x,
grid_offset_y,
})
_ => None
}
_ => None
}
}
///|
/// 将 `TiledConfig` 序列化为 JSON 字符串。
pub fn tiled_config_to_json(cfg : TiledConfig) -> String {
stringify_cfg([
("text", Json::string(cfg.text)),
("font_size", Json::number(cfg.font_size.to_double())),
("spacing_x", Json::number(cfg.spacing_x.to_double())),
("spacing_y", Json::number(cfg.spacing_y.to_double())),
("opacity", Json::number(cfg.opacity)),
("color", Json::number(cfg.color.to_double())),
("font", Json::string(cfg.font)),
])
}
///|
/// 从 JSON 字符串解析 `TiledConfig`;字段缺失或格式错误返回 `None`。
pub fn tiled_config_from_json(s : String) -> TiledConfig? {
tiled_cfg_from_obj(@json.parse(s)) catch {
_ => None
}
}
///|
fn tiled_cfg_from_obj(j : Json) -> TiledConfig? {
match j {
Object(m) => {
let font = match j_str(m, "font") {
Some(f) => f
None => "ascii5x7" // 向后兼容:旧 JSON 无 font 字段
}
match
(
j_str(m, "text"),
j_int(m, "font_size"),
j_int(m, "spacing_x"),
j_int(m, "spacing_y"),
j_dbl(m, "opacity"),
j_int(m, "color"),
) {
(
Some(text),
Some(font_size),
Some(spacing_x),
Some(spacing_y),
Some(opacity),
Some(color),
) =>
Some({ text, font_size, spacing_x, spacing_y, opacity, color, font, })
_ => None
}
}
_ => None
}
}
///|
fn j_str(m : Map[String, Json], k : String) -> String? {
match m.get(k) {
Some(j) =>
match j {
String(sv) => Some(sv)
_ => None
}
_ => None
}
}
///|
fn j_int(m : Map[String, Json], k : String) -> Int32? {
match m.get(k) {
Some(j) =>
match j {
Number(d, ..) => Some(d.to_int())
_ => None
}
_ => None
}
}
///|
fn j_bool(m : Map[String, Json], k : String) -> Bool? {
match m.get(k) {
Some(True) => Some(true)
Some(False) => Some(false)
_ => None
}
}
///|
fn j_dbl(m : Map[String, Json], k : String) -> Double? {
match m.get(k) {
Some(j) =>
match j {
Number(d, ..) => Some(d)
_ => None
}
_ => None
}
}
///|
fn j_i64(m : Map[String, Json], k : String) -> Int64? {
match m.get(k) {
Some(j) =>
match j {
Number(d, ..) => Some(d.to_int64())
_ => None
}
_ => None
}
}