spec_driven_docs/commands/
payload.rs1use crate::cli::payload::PayloadArgs;
9use crate::context::AppContext;
10use crate::domain::paths::UserEnv;
11use crate::error::AppError;
12use crate::output;
13use crate::release::crates_io::CratesIoResolver;
14use crate::release::embedded::EmbeddedReleaseBundle;
15use crate::release::{Provenance, ReleaseBundle, ReleaseManifest, ReleaseResolver, Role, Selector};
16
17pub fn run(_ctx: &AppContext, args: &PayloadArgs) -> Result<(), AppError> {
24 let manifest = match selector(args.release.as_deref())? {
25 Selector::Embedded => EmbeddedReleaseBundle::new().manifest()?,
26 chosen => {
27 let cache = UserEnv::from_process()
28 .user_paths()
29 .ok_or_else(|| AppError::Usage("no cache root resolves".to_string()))?
30 .bundle_cache
31 .path;
32 let resolver = CratesIoResolver::new(&cache).offline(args.offline);
33 resolver.resolve(&chosen)?.bundle.manifest()?
34 }
35 };
36 if args.json {
37 return output::json(&manifest);
38 }
39 render(&manifest);
40 Ok(())
41}
42
43fn selector(release: Option<&str>) -> Result<Selector, AppError> {
45 match release {
46 None | Some("embedded") => Ok(Selector::Embedded),
47 Some("latest") => Ok(Selector::Latest),
48 Some(version) => version.parse().map(Selector::Exact).map_err(|_| {
49 AppError::Usage(format!(
50 "--release takes embedded, latest, or a semantic version; {version} is none of those"
51 ))
52 }),
53 }
54}
55
56fn render(manifest: &ReleaseManifest) {
57 let provenance = match manifest.provenance {
58 Provenance::Native => "native".to_string(),
59 Provenance::LegacyAdapted => manifest.descriptor_sha256.as_ref().map_or_else(
60 || "legacy-adapted".to_string(),
61 |digest| format!("legacy-adapted from descriptor {digest}"),
62 ),
63 };
64 output::line(format!("spec-driven-docs {}", manifest.version));
65 output::line(format!("payload schema: {}", manifest.payload_schema));
66 output::line(format!("provenance: {provenance}"));
67 output::line(format!("payload digest: {}", manifest.payload_sha256));
68 let payload = manifest
69 .artifacts
70 .iter()
71 .filter(|artifact| artifact.role == Role::Payload)
72 .count();
73 let metadata = manifest.artifacts.len() - payload;
74 output::line(format!("artifacts: {payload} payload, {metadata} metadata"));
75}