Skip to main content

cli/
preset_pack.rs

1//! Terminal adapter and explicit output write for deterministic Preset bundles.
2
3use crate::commands::PresetReportFormat;
4use anyhow::Result;
5use shine_core::runtime::PresetPackReportV1;
6use std::path::{Path, PathBuf};
7
8pub async fn handle_pack(
9    path: &Path,
10    output: &Path,
11    force: bool,
12    format: PresetReportFormat,
13) -> Result<bool> {
14    let cwd = std::env::current_dir().unwrap_or_else(|_| Path::new(".").to_path_buf());
15    let mut artifact =
16        shine_core::runtime::pack_preset_path(&shine_core::runtime::RealHost, &cwd, path).await;
17    if artifact.report.valid {
18        let category_input = if path.ends_with("shine.toml") {
19            path.parent().unwrap_or(path)
20        } else {
21            path
22        };
23        let category = absolute(&cwd, category_input);
24        let output = absolute(&cwd, output);
25        if output.starts_with(&category) {
26            invalidate(&mut artifact.report, "output_inside_category");
27        } else if output.exists() && !force {
28            invalidate(&mut artifact.report, "output_exists");
29        } else if shine_core::persist::atomic_write(&output, &artifact.bytes)
30            .await
31            .is_err()
32        {
33            invalidate(&mut artifact.report, "output_write_failed");
34        }
35    }
36    match format {
37        PresetReportFormat::Text => print_text_report(&artifact.report),
38        PresetReportFormat::Json => {
39            println!("{}", serde_json::to_string_pretty(&artifact.report)?)
40        }
41    }
42    Ok(artifact.report.valid)
43}
44
45fn absolute(cwd: &Path, path: &Path) -> PathBuf {
46    if path.is_absolute() {
47        path.to_path_buf()
48    } else {
49        cwd.join(path)
50    }
51}
52
53fn invalidate(report: &mut PresetPackReportV1, code: &str) {
54    report.valid = false;
55    report.files = 0;
56    report.archive_bytes = 0;
57    report.bundle_sha256 = None;
58    report.diagnostics.push(code.to_string());
59}
60
61fn print_text_report(report: &PresetPackReportV1) {
62    println!(
63        "Preset pack: {}",
64        if report.valid { "created" } else { "blocked" }
65    );
66    if let Some(target) = &report.target {
67        println!("  Target: {target}");
68    }
69    for diagnostic in &report.diagnostics {
70        println!("  error[{diagnostic}]");
71    }
72    if report.valid {
73        println!("  Files: {}", report.files);
74        println!("  Archive bytes: {}", report.archive_bytes);
75        println!(
76            "  SHA-256: {}",
77            report.bundle_sha256.as_deref().unwrap_or_default()
78        );
79    }
80}