bonsol-cli 0.6.1

Cli For Bonsol
use anyhow::Result;
use std::fs;
use std::path::Path;
use tera::{Context, Tera};

pub fn init_project(project_name: Option<String>, dir: Option<String>) -> Result<()> {
    let pwd = std::env::current_dir()?;

    let project_path = if let Some(ref d) = dir {
        let p = Path::new(d);
        if p.is_relative() {
            pwd.join(p)
        } else {
            p.to_path_buf()
        }
    } else if let Some(name) = &project_name {
        pwd.join(name)
    } else {
        pwd.clone()
    };

    let effective_name = if let Some(n) = project_name {
        n
    } else {
        project_path
            .file_name()
            .map(|s| s.to_string_lossy().to_string())
            .or_else(|| pwd.file_name().map(|s| s.to_string_lossy().to_string()))
            .unwrap_or_else(|| "project".to_string())
    };

    if project_path.exists() && project_path != pwd {
        return Err(anyhow::anyhow!("Project already exists"));
    }

    println!("Creating project skeleton...");

    fs::create_dir_all(project_path.join("src"))?;

    let mut tera = Tera::default();
    tera.add_raw_templates(vec![
        ("Cargo.toml", CARGO_TEMPLATE),
        ("src/main.rs", MAIN_TEMPLATE),
        ("README.md", README_TEMPLATE),
    ])?;

    let mut context = Context::new();
    context.insert("project_name", &effective_name);

    for (template_name, file_name) in &[
        ("Cargo.toml", "Cargo.toml"),
        ("src/main.rs", "src/main.rs"),
        ("README.md", "README.md"),
    ] {
        let content = tera.render(template_name, &context)?;
        fs::write(project_path.join(file_name), content)?;
    }

    println!("Updating Cargo.lock...");

    let status = std::process::Command::new("cargo")
        .arg("generate-lockfile")
        .current_dir(&project_path)
        .status()?;
    if !status.success() {
        return Err(anyhow::anyhow!("`cargo generate-lockfile` failed"));
    }

    println!("Project '{}' initialized successfully!", effective_name);
    Ok(())
}

const CARGO_TEMPLATE: &str = r#"
[package]
name = "{{ project_name }}"
version = "0.1.0"
edition = "2024"

[package.metadata.zkprogram]
input_order = ["Public"]

[workspace]

[dependencies]
risc0-zkvm = {version = "2.3.1", default-features = false, features = ["std"]}

[dependencies.sha2]
git = "https://github.com/risc0/RustCrypto-hashes"
tag = "sha2-v0.10.6-risczero.0"
"#;

const MAIN_TEMPLATE: &str = r#"
use risc0_zkvm::{guest::{env, sha::Impl}, sha::{Sha256}};

fn main() {
    let mut input_1 = Vec::new();
    env::read_slice(&mut input_1);
    let digest = Impl::hash_bytes(&input_1);
    env::commit_slice(digest.as_bytes());
}
"#;

const README_TEMPLATE: &str = r#"
# {{ project_name }}

This is a Bonsol zkprogram, built on risc0
"#;