pi_agent_rust 0.3.0

Native AI coding agent CLI - Rust port of Pi Agent
use flate2::{Compression, GzBuilder};
use std::fmt::Write as _;
use std::io::Write;
use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder, RustcBuilder};

struct EmbeddedTextAsset {
    source: &'static str,
    output_name: &'static str,
    metadata_prefix: &'static str,
    checksum_prefix: Option<&'static str>,
}

const EMBEDDED_TEXT_ASSETS: [EmbeddedTextAsset; 4] = [
    EmbeddedTextAsset {
        source: "legacy_pi_mono_code/pi-mono/packages/ai/src/models.generated.ts",
        output_name: "legacy-models-generated.ts.gz",
        metadata_prefix: "LEGACY_MODELS_GENERATED_TS",
        checksum_prefix: Some("LEGACY_MODELS_GENERATED_TS"),
    },
    EmbeddedTextAsset {
        source: "docs/provider-upstream-model-ids-snapshot.json",
        output_name: "provider-upstream-model-ids.json.gz",
        metadata_prefix: "PROVIDER_UPSTREAM_MODEL_IDS_JSON",
        checksum_prefix: Some("PROVIDER_UPSTREAM_MODEL_IDS_JSON"),
    },
    EmbeddedTextAsset {
        source: "docs/extension-artifact-provenance.json",
        output_name: "extension-artifact-provenance.json.gz",
        metadata_prefix: "EXTENSION_ARTIFACT_PROVENANCE_JSON",
        checksum_prefix: None,
    },
    EmbeddedTextAsset {
        source: "CHANGELOG.md",
        output_name: "changelog.md.gz",
        metadata_prefix: "CHANGELOG",
        checksum_prefix: None,
    },
];

fn grouped_decimal(value: impl std::fmt::Display) -> String {
    let digits = value.to_string();
    let mut grouped = String::with_capacity(digits.len() + digits.len() / 3);
    for (index, digit) in digits.chars().enumerate() {
        if index > 0 && (digits.len() - index).is_multiple_of(3) {
            grouped.push('_');
        }
        grouped.push(digit);
    }
    grouped
}

fn compress_embedded_text_assets() -> Result<(), Box<dyn std::error::Error>> {
    let out_dir = std::path::PathBuf::from(
        std::env::var_os("OUT_DIR")
            .ok_or_else(|| std::io::Error::other("OUT_DIR is unset for build script"))?,
    );
    let mut metadata = String::from("// @generated by build.rs; do not edit.\n");
    for asset in EMBEDDED_TEXT_ASSETS {
        let source = asset.source;
        println!("cargo:rerun-if-changed={source}");
        let source_bytes = std::fs::read(source)?;
        std::str::from_utf8(&source_bytes).map_err(|error| {
            std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                format!("embedded text resource {source} is not valid UTF-8: {error}"),
            )
        })?;
        let mut encoder = GzBuilder::new()
            .mtime(0)
            .operating_system(255)
            .write(Vec::new(), Compression::best());
        encoder.write_all(&source_bytes)?;
        std::fs::write(out_dir.join(asset.output_name), encoder.finish()?)?;
        writeln!(
            metadata,
            "const {}_RAW_LEN: usize = {};",
            asset.metadata_prefix,
            grouped_decimal(source_bytes.len())
        )?;
        if let Some(prefix) = asset.checksum_prefix {
            writeln!(
                metadata,
                "const {prefix}_CRC32C: u32 = {};",
                grouped_decimal(crc32c::crc32c(&source_bytes))
            )?;
        }
    }
    std::fs::write(out_dir.join("embedded-text-metadata.rs"), metadata)?;
    Ok(())
}

fn emit_benchmark_build_fingerprint() -> Result<(), Box<dyn std::error::Error>> {
    fn required_env(name: &str) -> Result<String, std::io::Error> {
        std::env::var(name).map_err(|_| {
            std::io::Error::other(format!(
                "Cargo did not provide required build variable {name}"
            ))
        })
    }

    let profile_family = required_env("PROFILE")?;
    let opt_level = required_env("OPT_LEVEL")?;
    let debug = required_env("DEBUG")?;
    let mut features = std::env::vars()
        .filter_map(|(name, _)| {
            name.strip_prefix("CARGO_FEATURE_")
                .map(|feature| feature.to_ascii_lowercase().replace('_', "-"))
        })
        .collect::<Vec<_>>();
    features.sort_unstable();
    features.dedup();

    println!("cargo:rustc-env=PI_BUILD_PROFILE_FAMILY={profile_family}");
    println!("cargo:rustc-env=PI_BUILD_OPT_LEVEL={opt_level}");
    println!("cargo:rustc-env=PI_BUILD_DEBUG={debug}");
    println!("cargo:rustc-env=PI_BUILD_FEATURES={}", features.join(","));
    Ok(())
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    compress_embedded_text_assets()?;
    emit_benchmark_build_fingerprint()?;

    let build = BuildBuilder::default().build_timestamp(true).build()?;
    let cargo = CargoBuilder::default().target_triple(true).build()?;
    let gix = GixBuilder::default().sha(true).dirty(true).build()?;
    let rustc = RustcBuilder::default().semver(true).build()?;

    let mut emitter = Emitter::default();
    // Offloaded builds can temporarily miss git objects and trigger fallback warnings.
    // Keep default env fallbacks, but suppress warning noise in build output.
    emitter
        .quiet()
        .add_instructions(&build)?
        .add_instructions(&cargo)?
        .add_instructions(&gix)?
        .add_instructions(&rustc)?
        .emit()?;

    Ok(())
}