///|
pub(all) enum DiagnosticLevel {
Info
Warning
Error
} derive(Debug, ToJson)
///|
pub(all) struct Diagnostic {
level : DiagnosticLevel
path : String
message : String
} derive(Debug, ToJson)
///|
pub fn Diagnostic::new(
level~ : DiagnosticLevel,
path~ : String,
message~ : String,
) -> Diagnostic {
{ level, path, message }
}
///|
pub fn DebugDocument::validate(self : DebugDocument) -> Array[Diagnostic] {
let diagnostics : Array[Diagnostic] = []
if !self.image.valid() {
diagnostics.push(
Diagnostic::new(
level=Error,
path="image",
message="image dimensions must be positive",
),
)
}
if self.layers.is_empty() {
diagnostics.push(
Diagnostic::new(
level=Warning,
path="layers",
message="document has no layers",
),
)
}
for layer_index, layer in self.layers {
validate_layer(diagnostics, layer, layer_index)
}
diagnostics
}
///|
pub fn DebugDocument::is_valid(self : DebugDocument) -> Bool {
self.validate().all(diag => !(diag.level is Error))
}
///|
fn validate_layer(
out : Array[Diagnostic],
layer : Layer,
layer_index : Int,
) -> Unit {
let path = "layers[\{layer_index}]"
if layer.id == "" {
out.push(Diagnostic::new(level=Warning, path~, message="layer id is empty"))
}
if layer.opacity < 0.0 || layer.opacity > 1.0 {
out.push(
Diagnostic::new(
level=Error,
path~,
message="layer opacity is outside 0..1",
),
)
}
if layer.overlays.is_empty() {
out.push(
Diagnostic::new(level=Info, path~, message="layer has no overlays"),
)
}
for overlay_index, overlay in layer.overlays {
validate_overlay(out, overlay, "\{path}.overlays[\{overlay_index}]")
}
}
///|
fn validate_overlay(
out : Array[Diagnostic],
overlay : Overlay,
path : String,
) -> Unit {
match overlay {
BBox(rect~, ..) => validate_rect(out, rect, path)
Mask(polygons~, ..) =>
if polygons.is_empty() {
out.push(
Diagnostic::new(level=Warning, path~, message="mask has no polygons"),
)
} else {
for index, polygon in polygons {
if polygon.length() < 3 {
out.push(
Diagnostic::new(
level=Warning,
path="\{path}.polygons[\{index}]",
message="polygon has fewer than three points",
),
)
}
}
}
Keypoints(points~, ..) =>
if points.is_empty() {
out.push(
Diagnostic::new(
level=Warning,
path~,
message="keypoint overlay has no points",
),
)
}
Trajectory(path=trajectory, ..) =>
if trajectory.points.length() < 2 {
out.push(
Diagnostic::new(
level=Warning,
path~,
message="trajectory has fewer than two points",
),
)
}
Heatmap(cells~, ..) =>
if cells.is_empty() {
out.push(
Diagnostic::new(level=Warning, path~, message="heatmap has no cells"),
)
} else {
for index, cell in cells {
validate_rect(out, cell.rect, "\{path}.cells[\{index}]")
if cell.value < 0.0 || cell.value > 1.0 {
out.push(
Diagnostic::new(
level=Warning,
path="\{path}.cells[\{index}].value",
message="heat value is outside 0..1 and will be clamped by renderers",
),
)
}
}
}
ErrorRegion(rect~, severity~, ..) => {
validate_rect(out, rect, path)
if severity < 0.0 || severity > 1.0 {
out.push(
Diagnostic::new(
level=Warning,
path="\{path}.severity",
message="severity is outside 0..1 and will be clamped by renderers",
),
)
}
}
}
}
///|
fn validate_rect(out : Array[Diagnostic], rect : Rect, path : String) -> Unit {
if rect.width <= 0.0 || rect.height <= 0.0 {
out.push(
Diagnostic::new(
level=Error,
path~,
message="rectangle size must be positive",
),
)
}
}