///|
let old_checkout : String =
#|contract Checkout 1.0.0
#|type Order closed
#|field id string required minlen=1
#|field total int required min=0 max=100000
#|field status enum:pending|paid|failed required
#|field note string optional maxlen=200
#|end
///|
let new_checkout : String =
#|contract Checkout 2.0.0
#|type Order closed
#|field id string required minlen=8
#|field total int required min=1 max=100000
#|field status enum:pending|paid required
#|field note string required maxlen=80
#|field currency string required minlen=3 maxlen=3
#|end
///|
fn parse_mode(value : String) -> @evo.CompatibilityMode? {
match value {
"backward" => Some(@evo.Backward)
"forward" => Some(@evo.Forward)
"full" => Some(@evo.Full)
_ => None
}
}
///|
fn print_report(report : @evo.AnalysisReport, format : String) -> Unit {
match format {
"json" => println(report.to_json())
"sarif" => println(report.to_sarif())
"witness-json" => println(report.to_witness_json())
"witness-jsonl" => println(report.to_witness_jsonl())
"witness-md" => println(report.to_witness_markdown())
"score-json" => println(report.score_witness_evidence().to_json())
"score-md" => println(report.score_witness_evidence().to_markdown())
"replay-json" => println(report.to_replay_json())
"replay-md" => println(report.to_replay_markdown())
"replay-checklist" => println(report.to_replay_checklist())
_ => println(report.to_markdown())
}
}
///|
fn run_demo(format : String) -> Unit {
match @evo.analyze_text(old_checkout, new_checkout, mode=@evo.Full) {
Ok(report) => print_report(report, format)
Err(error) => println("error: " + error.render())
}
}
///|
fn print_help() -> Unit {
let help_text =
#|EvoWitness — explain versioned payload compatibility with minimal counterexamples
#|
#|Usage:
#| evowitness demo [markdown|json|sarif|witness-json|witness-jsonl|witness-md|score-json|score-md|replay-json|replay-md|replay-checklist]
#| evowitness compare [markdown|json|sarif|witness-json|witness-jsonl|witness-md|score-json|score-md|replay-json|replay-md|replay-checklist]
#|
#|For compare, replace line breaks in each contract with semicolons.
#|Example contract: contract C 1;type Item closed;field id string required;end
println(help_text)
}
///|
fn main {
let argv = @env.args()
let args = if argv.length() > 0 { argv[1:] } else { argv[:] }
if args.is_empty() {
run_demo("markdown")
return
}
match args[0] {
"demo" => run_demo(if args.length() >= 2 { args[1] } else { "markdown" })
"compare" => {
if args.length() < 4 {
print_help()
return
}
guard parse_mode(args[1]) is Some(mode) else {
println("error: mode must be backward, forward, or full")
return
}
let old_source = args[2].replace_all(old=";", new="\n")
let new_source = args[3].replace_all(old=";", new="\n")
let format = if args.length() >= 5 { args[4] } else { "markdown" }
match @evo.analyze_text(old_source, new_source, mode~) {
Ok(report) => print_report(report, format)
Err(error) => println("error: " + error.render())
}
}
"help" | "--help" | "-h" => print_help()
_ => print_help()
}
}