packc/cli/
inspect_lock.rs1#![forbid(unsafe_code)]
2
3use std::path::{Path, PathBuf};
4
5use anyhow::{Context, Result};
6use clap::Args;
7use greentic_pack::pack_lock::read_pack_lock;
8use serde::Serialize;
9use serde_json::Value;
10
11#[derive(Debug, Args)]
12pub struct InspectLockArgs {
13 #[arg(long = "in", value_name = "DIR", default_value = ".")]
15 pub input: PathBuf,
16
17 #[arg(long = "lock", value_name = "FILE")]
19 pub lock: Option<PathBuf>,
20}
21
22pub fn handle(args: InspectLockArgs) -> Result<()> {
23 let pack_dir = args
24 .input
25 .canonicalize()
26 .with_context(|| format!("failed to resolve pack dir {}", args.input.display()))?;
27 let lock_path = resolve_lock_path(&pack_dir, args.lock.as_deref());
28 let lock = read_pack_lock(&lock_path)
29 .with_context(|| format!("failed to read {}", lock_path.display()))?;
30
31 let json = to_sorted_json(&lock)?;
32 println!("{json}");
33 Ok(())
34}
35
36fn resolve_lock_path(pack_dir: &Path, override_path: Option<&Path>) -> PathBuf {
37 match override_path {
38 Some(path) if path.is_absolute() => path.to_path_buf(),
39 Some(path) => pack_dir.join(path),
40 None => pack_dir.join("pack.lock.cbor"),
41 }
42}
43
44fn to_sorted_json<T: Serialize>(value: &T) -> Result<String> {
45 let value = serde_json::to_value(value).context("encode lock to json")?;
46 let sorted = sort_json(value);
47 serde_json::to_string_pretty(&sorted).context("serialize json")
48}
49
50fn sort_json(value: Value) -> Value {
51 match value {
52 Value::Object(map) => {
53 let mut entries: Vec<(String, Value)> = map.into_iter().collect();
54 entries.sort_by(|a, b| a.0.cmp(&b.0));
55 let mut sorted = serde_json::Map::new();
56 for (key, value) in entries {
57 sorted.insert(key, sort_json(value));
58 }
59 Value::Object(sorted)
60 }
61 Value::Array(values) => Value::Array(values.into_iter().map(sort_json).collect()),
62 other => other,
63 }
64}