Examples
Examples
Section titled “Examples”This page shows concrete integration patterns: loading the engine, running analysis, enforcing policies in CI, and handling multiple projects.
Rust: Using the Loader (FFI Path)
Section titled “Rust: Using the Loader (FFI Path)”When you use arxo-loader, the engine is loaded as a dynamic library and analysis returns raw JSON. You can parse the JSON to read metrics and violations.
use arxo_loader::EngineLibrary;use arxo_types::config::{default_config, schema::Config};use std::path::Path;
fn main() -> anyhow::Result<()> { let engine = EngineLibrary::load()?; println!("Engine version: {}", engine.version());
let config = default_config(); // Optional: customize config (e.g. metric_preset, data.language, report.format) let path = Path::new("./my-project"); let license = std::env::var("ARCH0_LICENSE_KEY").ok().as_deref();
let result = engine.analyze(path, &config, license)?; let data: serde_json::Value = serde_json::from_str(&result.json)?;
// Example: read metric results or violations from data if let Some(violations) = data.get("violations").and_then(|v| v.as_array()) { println!("Policy violations: {}", violations.len()); } if let Some(metrics) = data.get("results").and_then(|r| r.as_array()) { println!("Metrics computed: {}", metrics.len()); }
Ok(())}Rust: Using the Orchestrator (Direct Engine)
Section titled “Rust: Using the Orchestrator (Direct Engine)”If your build links against arxo-engine directly (e.g. in-tree or private build), you use the orchestrator and get structured results.
use arxo_engine::{Orchestrator, core::run_options::RunOptions};use arxo_types::config::{load_config, default_config};use std::path::PathBuf;
#[tokio::main]async fn main() -> anyhow::Result<()> { let orchestrator = Orchestrator::new(); let project_path = PathBuf::from("./my-project"); let config = load_config("arxo.yml").unwrap_or_else(|_| default_config()); let options = RunOptions::default();
let result = orchestrator.run(project_path, config, options).await?;
println!("Metrics: {}", result.results.len()); println!("Violations: {}", result.violations.len()); println!("Import graph nodes: {}", result.import_graph.node_count());
if !result.violations.is_empty() { for v in &result.violations { eprintln!(" {}: {}", v.metric, v.message); } }
result.report_with_options(options.quiet)?; Ok(())}CI/CD: Fail on Policy Violations
Section titled “CI/CD: Fail on Policy Violations”Run analysis and exit with a non-zero code when any policy invariant fails. Works with either the loader (parse JSON) or the orchestrator (use result.violations).
With orchestrator (Rust):
use arxo_engine::Orchestrator;use arxo_types::config::{load_config, default_config};use arxo_engine::core::run_options::RunOptions;use std::path::PathBuf;
#[tokio::main]async fn main() -> anyhow::Result<()> { let config = load_config("arxo.yml").unwrap_or_else(|_| default_config()); // Ensure policy is set in config (policy.invariants) let result = Orchestrator::new() .run( PathBuf::from("."), config, RunOptions { fail_fast: true, ..Default::default() }, ) .await?;
if result.violations.is_empty() { println!("All policies passed."); return Ok(()); }
eprintln!("{} policy violation(s):", result.violations.len()); for v in &result.violations { eprintln!(" - {}: {}", v.metric, v.message); } std::process::exit(1);}With loader (Rust, JSON):
Parse result.json and check for a violations array; if non-empty, exit with code 1.
Policy Gate Configuration
Section titled “Policy Gate Configuration”Define invariants in config so the engine evaluates them after metrics run. Example arxo.yml:
data: language: typescript import_graph: group_by: folder group_depth: 2 exclude: ["node_modules", "dist"]
metric_preset: quick
policy: invariants: - metric: scc.cycle_count op: "<=" value: 0 - metric: propagation_cost.system.ratio op: "<=" value: 0.15
report: format: json file: report.jsonThen in code, check result.violations (or the violations field in the JSON result) and fail the build or gate the merge accordingly.
Multi-Project Analysis
Section titled “Multi-Project Analysis”Reuse a single engine instance to analyze several directories. With the loader:
use arxo_loader::EngineLibrary;use arxo_types::config::default_config;use std::path::Path;
fn main() -> anyhow::Result<()> { let engine = EngineLibrary::load()?; let config = default_config(); let license = std::env::var("ARCH0_LICENSE_KEY").ok().as_deref();
let projects = ["./service-a", "./service-b", "./libs/shared"]; for project in &projects { let path = Path::new(project); if !path.exists() { continue; } match engine.analyze(path, &config, license) { Ok(result) => println!("{}: OK ({} bytes)", project, result.json.len()), Err(e) => eprintln!("{}: {}", project, e), } }
Ok(())}With the orchestrator, call orchestrator.run(project_path, config.clone(), options).await in a loop for each project path.
Writing Results to a File
Section titled “Writing Results to a File”Orchestrator: Set report.file in config and call result.report_with_options(quiet); the engine writes the report (e.g. JSON or HTML) to that path.
Loader: The result is only JSON in memory. Write it yourself:
let result = engine.analyze(path, &config, license)?;std::fs::write("analysis-output.json", &result.json)?;Next Steps
Section titled “Next Steps”- Configuration — Full config schema
- Rust API — More Rust patterns
- FFI API — C, Go, Zig usage
- Troubleshooting — Common issues