use normcore::EvaluateInput;
use normcore::JsonValue;
use normcore::coerce_grounds_input;
use normcore::evaluate;
use normcore::parse_conversation;
use normcore::parse_json;
use normcore::to_pretty_json;
fn print_help() {
println!("NormCore CLI.");
println!("\nUsage:");
println!(
" normcore [--version] [--log-level LEVEL] [-v|-vv] evaluate [--agent-output TEXT] [--conversation JSON] [--grounds JSON]"
);
}
fn main() {
std::process::exit(run(std::env::args().collect()));
}
fn run(argv: Vec<String>) -> i32 {
let args = if argv.is_empty() {
Vec::new()
} else {
argv[1..].to_vec()
};
if args.is_empty() {
print_help();
return 0;
}
if args.iter().any(|a| a == "--version") {
println!("{}", env!("CARGO_PKG_VERSION"));
return 0;
}
let mut i = 0;
let mut command = String::new();
let mut agent_output: Option<String> = None;
let mut conversation_json: Option<String> = None;
let mut grounds_json: Option<String> = None;
while i < args.len() {
match args[i].as_str() {
"--log-level" => {
i += 1;
}
"-v" | "-vv" => {}
"evaluate" => {
command = "evaluate".to_string();
}
"--agent-output" => {
i += 1;
if let Some(v) = args.get(i) {
agent_output = Some(v.clone());
} else {
eprintln!("error: --agent-output requires value");
return 2;
}
}
"--conversation" => {
i += 1;
if let Some(v) = args.get(i) {
conversation_json = Some(v.clone());
} else {
eprintln!("error: --conversation requires value");
return 2;
}
}
"--grounds" => {
i += 1;
if let Some(v) = args.get(i) {
grounds_json = Some(v.clone());
} else {
eprintln!("error: --grounds requires value");
return 2;
}
}
_ => {}
}
i += 1;
}
if command != "evaluate" {
print_help();
return 0;
}
let conversation = match conversation_json {
Some(raw) => match parse_json(&raw) {
Ok(JsonValue::Array(arr)) => match parse_conversation(&arr) {
Ok(v) => Some(v),
Err(err) => {
eprintln!("error: invalid --conversation: {err:?}");
return 2;
}
},
Ok(_) => {
eprintln!("error: --conversation must be JSON array");
return 2;
}
Err(err) => {
eprintln!(
"error: Failed to parse --conversation JSON: {}",
err.message
);
return 2;
}
},
None => None,
};
let grounds = match grounds_json {
Some(raw) => match parse_json(&raw) {
Ok(JsonValue::Array(arr)) => Some(coerce_grounds_input(Some(&arr), None, None)),
Ok(_) => {
eprintln!("error: --grounds must be JSON array");
return 2;
}
Err(err) => {
eprintln!("error: Failed to parse --grounds JSON: {}", err.message);
return 2;
}
},
None => None,
};
match evaluate(EvaluateInput {
agent_output,
conversation,
grounds,
}) {
Ok(judgment) => {
println!("{}", to_pretty_json(&judgment.to_json_value()));
0
}
Err(err) => {
eprintln!("error: {err:?}");
2
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn help_runs() {
assert_eq!(run(vec!["normcore".to_string()]), 0);
}
#[test]
fn version_runs() {
assert_eq!(
run(vec!["normcore".to_string(), "--version".to_string()]),
0
);
}
#[test]
fn evaluate_runs() {
assert_eq!(
run(vec![
"normcore".to_string(),
"evaluate".to_string(),
"--agent-output".to_string(),
"The deployment is blocked.".to_string(),
]),
0
);
}
#[test]
fn evaluate_invalid_conversation_json_returns_error() {
assert_eq!(
run(vec![
"normcore".to_string(),
"evaluate".to_string(),
"--conversation".to_string(),
"{bad json}".to_string(),
]),
2
);
}
#[test]
fn evaluate_invalid_grounds_json_returns_error() {
assert_eq!(
run(vec![
"normcore".to_string(),
"evaluate".to_string(),
"--agent-output".to_string(),
"Use source".to_string(),
"--grounds".to_string(),
"{bad json}".to_string(),
]),
2
);
}
#[test]
fn evaluate_missing_agent_output_value_returns_error() {
assert_eq!(
run(vec![
"normcore".to_string(),
"evaluate".to_string(),
"--agent-output".to_string(),
]),
2
);
}
#[test]
fn evaluate_missing_conversation_value_returns_error() {
assert_eq!(
run(vec![
"normcore".to_string(),
"evaluate".to_string(),
"--conversation".to_string(),
]),
2
);
}
#[test]
fn evaluate_missing_grounds_value_returns_error() {
assert_eq!(
run(vec![
"normcore".to_string(),
"evaluate".to_string(),
"--grounds".to_string(),
]),
2
);
}
#[test]
fn options_without_evaluate_command_do_not_force_execution() {
assert_eq!(
run(vec![
"normcore".to_string(),
"--agent-output".to_string(),
"We should deploy now.".to_string(),
]),
0
);
assert_eq!(
run(vec![
"normcore".to_string(),
"--conversation".to_string(),
"[]".to_string(),
]),
0
);
assert_eq!(
run(vec![
"normcore".to_string(),
"--grounds".to_string(),
"[]".to_string(),
]),
0
);
}
#[test]
fn log_level_consumes_its_value_even_when_it_looks_like_an_option() {
assert_eq!(
run(vec![
"normcore".to_string(),
"evaluate".to_string(),
"--log-level".to_string(),
"--agent-output".to_string(),
"--agent-output".to_string(),
"We should deploy now.".to_string(),
]),
0
);
}
#[test]
fn log_level_value_does_not_become_command() {
assert_eq!(
run(vec![
"normcore".to_string(),
"--log-level".to_string(),
"evaluate".to_string(),
]),
0
);
}
}