use std::path::{Path, PathBuf};
use crate::{CliError, CliResult};
const RUST_CHEAT_SHEET: &str = "\
# Aristo annotation syntax — Rust
## Attribute form (preferred for fn / struct / impl / trait / mod / type / field / variant)
#[aristo::intent(\"text here\", verify = \"test\", id = \"snake_case_id\", parent = \"other_id\")]
fn the_thing() { ... }
## Function-like form (sub-item: before a statement / loop / block)
aristo::intent_stmt!(\"text here\", verify = \"test\");
for item in items { ... }
## Assume (no verify field; states external invariants you rely on)
#[aristo::assume(\"OS guarantee or library invariant\")]
fn the_thing() { ... }
## Parent linkage (singular or list)
parent = \"balance_no_duplicate_cells\"
parent = [\"a\", \"b\"]
## Verify levels
false | documentation only; no check ever runs
\"neural\" | AI-reasoned property check
\"test\" | mined assertions + existing test suite
\"full\" | server formal proof attempt (paid tier)
true | resolves to project default in aristo.toml [verify] default_method
## Namespace prefix
`aristos:` and `kanon:` prefixes are applied by `aristo canon accept`
when you accept a pending canon match. NEVER write them manually.
## Cargo features (in your `Cargo.toml`)
aristo_verify | injects mined assertions during `aristo verify --filter ...`
aristo_check | compile-time per-annotation validation
aristo_doc | rustdoc integration via include_str!
";
const C_CHEAT_SHEET: &str = "\
# Aristo annotation syntax — C
## Directive form (a `//` line comment directly above the item)
// @aristo intent(\"text here\", verify = \"test\", id = \"snake_case_id\", parent = \"other_id\")
int the_thing(void) { ... }
Attaches to the function / struct / union / enum on the next line, or — inside
a function body — to the statement on the next line.
## Assume (no verify field; states external invariants you rely on)
// @aristo assume(\"OS guarantee or library invariant\")
int the_thing(void) { ... }
## Explicit target (site = \"name\" as the FIRST argument)
// @aristo intent(site = \"do_read\", \"text here\", verify = \"test\")
SYSCALL_DEFINE3(read, ...) { ... }
Reaches a target adjacency can't: a macro-defined function, or one held off
from its directive by a doc-comment block. Resolves by name anywhere in the file.
## Parent linkage (singular or list)
parent = \"balance_no_duplicate_cells\"
parent = [\"a\", \"b\"]
## Verify levels
false | documentation only; no check ever runs
\"neural\" | AI-reasoned property check
\"test\" | mined assertions + existing test suite
\"full\" | server formal proof attempt (paid tier)
true | resolves to project default in aristo.toml [verify] default_method
## Namespace prefix
`aristos:` and `kanon:` prefixes are applied by `aristo canon accept`
when you accept a pending canon match. NEVER write them manually.
## Notes
Only `//` line comments are directives (not `/* ... */`). Keep the text on one
line; whitespace in the text is normalized when hashed.
";
#[aristo::intent(
"Each language's cheat sheet MUST match what that language's front-end \
actually recognizes — the Rust cheat sheet the macros `aristo-macros` \
export, the C cheat sheet the C directive extractor accepts. Adding, \
renaming, or removing a form requires updating the matching cheat sheet \
in the same change — agents are instructed to trust this output over \
their training data.",
verify = "test",
id = "lang_cheat_sheet_matches_macros"
)]
pub(crate) fn run(file: Option<PathBuf>) -> CliResult<()> {
let cwd = std::env::current_dir()?;
let lang = match file {
Some(path) => detect_for_file(&path)?,
None => detect_for_dir(&cwd)?,
};
emit_cheat_sheet(lang);
Ok(())
}
enum Lang {
Rust { manifest: PathBuf },
C { detected_via: String },
}
const C_MANIFESTS: &[&str] = &[
"CMakeLists.txt",
"Makefile",
"GNUmakefile",
"meson.build",
"configure.ac",
"compile_commands.json",
];
fn detect_for_dir(cwd: &Path) -> CliResult<Lang> {
let cargo_toml = cwd.join("Cargo.toml");
if cargo_toml.is_file() {
return Ok(Lang::Rust {
manifest: cargo_toml,
});
}
if let Some(signal) = detect_c_dir_signal(cwd) {
return Ok(Lang::C {
detected_via: signal,
});
}
Err(unsupported_error())
}
fn detect_c_dir_signal(cwd: &Path) -> Option<String> {
for name in C_MANIFESTS {
if cwd.join(name).is_file() {
return Some((*name).to_string());
}
}
let mut c_files: Vec<String> = std::fs::read_dir(cwd)
.ok()?
.flatten()
.filter(|e| {
matches!(
e.path().extension().and_then(|s| s.to_str()),
Some("c") | Some("h")
)
})
.filter_map(|e| e.file_name().to_str().map(str::to_string))
.collect();
c_files.sort(); c_files.into_iter().next()
}
fn detect_for_file(path: &Path) -> CliResult<Lang> {
match path.extension().and_then(|s| s.to_str()) {
Some("rs") => Ok(Lang::Rust {
manifest: path.to_path_buf(),
}),
Some("c") | Some("h") => Ok(Lang::C {
detected_via: path.display().to_string(),
}),
_ => Err(unsupported_error()),
}
}
fn unsupported_error() -> CliError {
CliError::Other {
message: "Cannot detect a supported language in this repository.\n \
Aristo supports: Rust, C.\n \
Planned: Python, Go, TypeScript."
.to_string(),
exit_code: 2,
}
}
fn emit_cheat_sheet(lang: Lang) {
match lang {
Lang::Rust { manifest } => {
println!(
"Detected language: Rust (from Cargo.toml at {})",
manifest.display()
);
println!();
print!("{RUST_CHEAT_SHEET}");
}
Lang::C { detected_via } => {
println!("Detected language: C (from {detected_via})");
println!();
print!("{C_CHEAT_SHEET}");
}
}
}