///|
/// 诊断系统。
///
/// GPX 的很多约束(元素顺序、必填属性、取值区间)都不会让 XML 解析器报错,
/// 只会静默产出一个**不合规但看起来正常**的文档。本模块把这些约束变成
/// 可枚举、可定位的诊断记录。
///
/// 每条诊断都带 **1-based 的行列号**,直接对应源文件中的位置。
///|
/// 诊断级别。
///
/// - `Error`:文档违反 schema,不合格
/// - `Warning`:不违反 schema,但明显可疑
pub(all) enum Severity {
Error
Warning
} derive(Eq, Debug)
///|
pub fn Severity::to_string(self : Severity) -> String {
match self {
Error => "error"
Warning => "warning"
}
}
///|
/// 一条诊断记录。
pub struct Diagnostic {
severity : Severity
/// 稳定的诊断代码,便于测试断言与用户检索。
code : String
message : String
/// 1-based 行号。
line : Int
/// 1-based 列号。
column : Int
} derive(Eq, Debug)
///|
pub fn Diagnostic::error(
code : String,
message : String,
line : Int,
column : Int,
) -> Diagnostic {
{ severity: Error, code, message, line, column, }
}
///|
pub fn Diagnostic::warning(
code : String,
message : String,
line : Int,
column : Int,
) -> Diagnostic {
{ severity: Warning, code, message, line, column, }
}
///|
/// 人类可读形式:`行:列 级别 代码 说明`
pub fn Diagnostic::to_string(self : Diagnostic) -> String {
"\{self.line}:\{self.column} \{self.severity.to_string()} \{self.code} \{self.message}"
}
///|
/// 诊断列表中是否存在错误级问题。
pub fn has_errors(diags : Array[Diagnostic]) -> Bool {
for d in diags {
if d.severity == Error {
return true
}
}
false
}
///|
/// 把诊断列表格式化成多行文本,每条一行。
pub fn format_diagnostics(diags : Array[Diagnostic]) -> String {
let lines : Array[String] = []
for d in diags {
lines.push(d.to_string())
}
lines.join("\n")
}
///|
/// 诊断代码常量。
///
/// 集中在这里,避免各处写散落的魔法字符串,也方便测试与文档对照。
pub let code_root_not_gpx : String = "E1001"
///|
pub let code_version_missing : String = "E1002"
///|
pub let code_version_not_1_1 : String = "E1003"
///|
pub let code_creator_missing : String = "E1004"
///|
pub let code_root_order : String = "E1005"
///|
pub let code_metadata_order : String = "E1006"
///|
pub let code_metadata_duplicate : String = "E1007"
///|
pub let code_unexpected_attribute : String = "E1008"
///|
pub let code_missing_required_attribute : String = "E1009"
///|
pub let code_invalid_datetime : String = "E1010"
///|
pub let code_invalid_decimal : String = "E1011"
///|
pub let code_wpt_order : String = "E1013"
///|
pub let code_rte_order : String = "E1014"
///|
pub let code_trk_order : String = "E1015"
///|
pub let code_invalid_fix : String = "E1016"
///|
/// `version` 不是 `"1.0"`。`parse_gpx_1_0` 专用——
/// 与 `E1003`(不是 1.1)分开,报错信息能直接说清是哪个入口在要求什么。
pub let code_version_not_1_0 : String = "E1017"
// ── 警告级(不违反 schema,但语义可疑)──
//
// 判定标准:**错误** = 文档在校验器下不合格;**警告** = 合法但值得人看一眼。
// 警告绝不会被 `parse_gpx` 当成失败原因,只在 `validate_gpx` 里出现。
///|
/// `bounds` 的 `min` 大于 `max`。
pub let code_bounds_order : String = "W1001"
///|
/// 同一 `trkseg` 内后一个点的时间早于前一个点。
pub let code_time_not_monotonic : String = "W1002"
///|
/// 相邻两个轨迹点的 `lat` / `lon` / `ele` 完全相同。
pub let code_duplicate_point : String = "W1003"
///|
/// `fix` 与 `sat` 自相矛盾(如 `fix=none` 却 `sat=9`,或 `fix=3d` 却 `sat=0`)。
pub let code_fix_sat_conflict : String = "W1004"
///|
/// `ele` 超出地球表面的合理范围(约 -11000 ~ 9000 m)。
pub let code_elevation_implausible : String = "W1005"
///|
/// `trk` / `trkseg` / `rte` 没有任何点。
pub let code_empty_segment : String = "W1006"
///|
/// 点位落在 `metadata/bounds` 声明的范围之外。
pub let code_out_of_bounds : String = "W1007"
// ── XML 层 ──
///|
pub let code_xml_parse_error : String = "E1000"