code_passenger 0.3.0

Scans a Rust workspace with klepto and does the writing: feature gates across every crate from one features document, proven by cargo check, Cargo.toml edits, file headers from the plan, and the history of it.
Documentation
//! `passenger init | branch | checkout | checkpoint | log`, on the store of
//! the workspace `--root` belongs to.

use crate::error::{PassengerError, Result};
use crate::lines::{CodePassenger, PassengerRooted};
use crate::passenger::{CheckpointOptions, PassengerStore};
use std::path::Path;

fn os(v: &Option<std::ffi::OsString>) -> Option<String> {
    v.as_ref().map(|s| s.to_string_lossy().into_owned())
}

/// Runs the line; what it says goes into `out`; returns the exit status.
pub fn run(l: &PassengerRooted<CodePassenger>, root: &Path, out: &mut String) -> Result<i32> {
    let l = l.leaf();
    let name = os(&l.name);
    match l.action.to_string_lossy().as_ref() {
        "init" => {
            let s = PassengerStore::init(root)?;
            out.push_str(&format!("initialized {}\n", s.root.join(crate::passenger::store::FOLDER).display()));
        }
        "branch" => {
            let s = PassengerStore::open(root)?;
            let name = name.ok_or_else(|| PassengerError::Line { detail: "passenger branch NAME".into() })?;
            s.create_branch(&name, os(&l.from).as_deref())?;
            out.push_str(&format!("created branch {name}\n"));
        }
        "checkout" => {
            let s = PassengerStore::open(root)?;
            let name = name.ok_or_else(|| PassengerError::Line { detail: "passenger checkout BRANCH|SNAPSHOT".into() })?;
            let r = s.checkout(&name, l.force)?;
            out.push_str(&format!("checked out {name} ({}): {} file(s) written", r.commit.as_deref().unwrap_or("no commits yet"), r.written.len()));
            for f in &r.written {
                out.push_str(&format!("\n  wrote {f}"));
            }
            for f in &r.left {
                out.push_str(&format!("\n  left {f} (not in the snapshot)"));
            }
            out.push('\n');
        }
        "checkpoint" => {
            let s = PassengerStore::open(root)?;
            let ctx = crate::engine::RunContext { root: s.root.clone(), src_rel: String::new(), manifest_path: s.root.join("Cargo.toml"), lang: "rust".into() };
            let (scan, unparsed) = crate::engine::run_scan_skipping(&ctx)?;
            let analysis = serde_json::to_value(crate::analysis::run(&scan))?;
            let c = s.checkpoint(Some(&scan), Some(&analysis), CheckpointOptions { note: os(&l.note), branch: os(&l.branch), include_artifacts: !l.no_artifacts, unparsed })?;
            out.push_str(&format!("checkpoint {} on {}\n", c.id, c.branch));
            for (f, e) in c.manifest.files.iter().filter_map(|(f, e)| e.unparsed.as_ref().map(|why| (f, why))) {
                out.push_str(&format!("note: klepto could not parse {f}; kept by digest, marked unparsed, left out of the scan: {e}\n"));
            }
        }
        "log" => {
            let s = PassengerStore::open(root)?;
            let n: usize = os(&l.n).map(|v| v.parse().map_err(|_| PassengerError::Line { detail: format!("--n takes a number, not `{v}`") })).transpose()?.unwrap_or(25);
            let head = s.resolve_head()?;
            let mut cur = head.head_commit;
            out.push_str(&format!("== log ({}) ==\n", head.branch));
            for _ in 0..n {
                let Some(id) = cur.take() else { break };
                let c = s.read_commit(&head.passenger_version, &id)?;
                let when = chrono::DateTime::<chrono::Utc>::from_timestamp_millis(c.ts_ms).map(|t| t.to_rfc3339()).unwrap_or_default();
                out.push_str(&format!("{}  {}  changed_files={} +{} -{}  {}\n", c.id, when, c.stats.changed_files, c.stats.added_lines, c.stats.removed_lines, c.note.clone().unwrap_or_default()));
                cur = c.parents.first().cloned();
            }
        }
        other => return Err(PassengerError::Line { detail: format!("passenger {other}") }),
    }
    Ok(0)
}