Skip to content
Arxo Arxo

Rust API Reference

This guide covers using the closed-source arxo-engine from Rust via the arxo-loader crate. The loader dynamically loads the engine library and exposes four operations: load, load from path, version, and analyze. Analysis returns a single JSON string that you parse to get results and policy violations.

[dependencies]
arxo-types = "0.1" # Config and shared types (MIT)
arxo-loader = "0.1" # Dynamic engine loader
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" # Parse result JSON
anyhow = "1.0" # Error handling
ItemDescription
EngineLibraryHandle to the loaded engine (from arxo_loader)
EngineLibrary::load()Load engine from default locations (env, same dir, cache, CDN)
EngineLibrary::load_from_path(path)Load engine from a specific library path
engine.version()Engine version string
engine.analyze(path, config, license)Run analysis; returns Result<AnalysisResult, LoadError>
AnalysisResult{ json: String } — raw JSON from the engine
LoadErrorLoad or analysis failure (NotFound, LoadFailed, InvalidSymbol, AnalyzeFailed, DownloadFailed)

Configuration uses arxo_types::config::schema::Config. You build or deserialize Config, then pass the project path and config to analyze. Optional license can be None (engine may use env or default).

use arxo_loader::EngineLibrary;
use std::path::Path;
fn main() -> Result<(), arxo_loader::LoadError> {
// Load from default locations (ARCH0_ENGINE_PATH, same dir as binary, cache, CDN)
let engine = EngineLibrary::load()?;
// Or load from a specific path
let engine = EngineLibrary::load_from_path(Path::new("/path/to/libarxo_engine.so"))?;
println!("Engine version: {}", engine.version());
Ok(())
}

You pass a path (project root), a config (from arxo-types), and an optional license string. The result is an AnalysisResult with a single field json: a string containing the full result and violations as JSON.

use arxo_loader::{EngineLibrary, LoadError};
use arxo_types::config::schema::Config;
use std::path::Path;
fn analyze_project() -> Result<(), LoadError> {
let engine = EngineLibrary::load()?;
let path = Path::new("./my-project");
let config = Config {
metric_preset: Some("quick".to_string()),
data: None,
source: None,
architecture: None,
metrics: None,
policy: None,
report: None,
run_options: None,
};
let result = engine.analyze(path, &config, None)?;
println!("Result JSON: {}", result.json);
Ok(())
}

The engine returns a JSON object with at least results (array of metric results) and violations (array of policy violations). Parse result.json with serde_json to inspect them.

use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct AnalysisOutput {
results: Option<Vec<MetricResultItem>>,
violations: Option<Vec<ViolationItem>>,
}
#[derive(Debug, Deserialize)]
struct MetricResultItem {
id: Option<String>,
// ... other fields depend on metric
}
#[derive(Debug, Deserialize)]
struct ViolationItem {
metric: Option<String>,
message: Option<String>,
// ... other fields
}
fn use_result(result: &arxo_loader::AnalysisResult) -> anyhow::Result<()> {
let out: AnalysisOutput = serde_json::from_str(&result.json)?;
if let Some(violations) = &out.violations {
for v in violations {
eprintln!("Violation: {:?}", v.message);
}
}
if let Some(results) = &out.results {
for r in results {
println!("Metric {}: {:?}", r.id.as_deref().unwrap_or("?"), r);
}
}
Ok(())
}

Exact shape of results and violations depends on your config (metrics enabled, policy rules). Use the same schema as the engine’s JSON report or inspect a sample output.

Build a Config from arxo_types::config::schema (or deserialize from YAML/JSON). Key fields include:

  • metric_preset — e.g. "quick", "ci", "full" (replaces explicit metrics list when set)
  • data — language, import_graph (exclude, group_by, group_depth), call_graph, git_history, etc.
  • policy — rules to evaluate (metric, operator, threshold)
  • report — format and file (engine uses this for internal reporting; your programmatic output is still the JSON string)
  • run_options — disable_cache, incremental, quiet

See Configuration for the full schema. Minimal programmatic example (or load from YAML/JSON and deserialize):

use arxo_types::config::schema::Config;
let config = Config {
source: None,
data: None,
architecture: None,
metrics: None,
metric_preset: Some("quick".to_string()),
policy: None,
report: None,
run_options: None,
};
// For data options (language, exclude, etc.) build DataConfig and set config.data, or load from file.

arxo_loader uses LoadError for load and analysis failures:

use arxo_loader::{EngineLibrary, LoadError};
fn run() -> Result<(), LoadError> {
use arxo_types::config::schema::Config;
let engine = EngineLibrary::load()?;
let config = Config {
source: None,
data: None,
architecture: None,
metrics: None,
metric_preset: Some("quick".to_string()),
policy: None,
report: None,
run_options: None,
};
let result = engine.analyze(std::path::Path::new("."), &config, None)?;
println!("{}", result.json);
Ok(())
}
fn main() {
if let Err(e) = run() {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}

Variants: NotFound, LoadFailed, InvalidSymbol, AnalyzeFailed, DownloadFailed. Use thiserror display or match on the variant as needed.

If your deployment requires a license, pass it as the third argument to analyze or set the ARCH0_LICENSE_KEY environment variable. Pass None to use env/default.

let license = std::env::var("ARCH0_LICENSE_KEY").ok();
let result = engine.analyze(path, &config, license.as_deref())?;

arxo_engine_lib_name() and platform_triple() are re-exported from arxo_loader for use when resolving the engine path (e.g. for custom download or UI):

use arxo_loader::{arxo_engine_lib_name, platform_triple};
println!("Library name: {}", arxo_engine_lib_name());
println!("Platform: {}", platform_triple());
  1. Reuse the engine — Call EngineLibrary::load() once and run multiple analyze calls with different paths/configs.
  2. Cache config — Build or load Config once and reuse; only change what you need per run.
  3. Parse JSON once — Deserialize result.json into your own types and work with typed data.
  4. Handle LoadError — Surface load/analysis failures to the user or CI (e.g. exit code, log, retry).
  5. Use presets — Set metric_preset (e.g. "quick") instead of building a full metrics list unless you need fine-grained control.