1use std::path::PathBuf;
11
12use gen_types::{
13 Adapter, AdapterCtx, AdapterError, AdapterResult, ConfirmReport, DiffRef, DiffReport,
14 InvariantBreak, LockOutcome, Plan, PlanIntent, Sbom, SbomFormat,
15};
16
17use crate::build_spec::{self, TargetTuple};
18use crate::interp::{self, EncodeCtx, RealGoBuildEnv};
19
20pub struct GomodAdapter;
21
22impl GomodAdapter {
23 fn tuple_for(ctx: &AdapterCtx) -> TargetTuple {
26 ctx.target
27 .as_deref()
28 .and_then(TargetTuple::from_rust_triple)
29 .unwrap_or_else(TargetTuple::host)
30 }
31
32 fn encode(ctx: &AdapterCtx) -> AdapterResult<build_spec::BuildSpec> {
34 let manifest = ctx.workspace_root.join("go.mod");
35 if !manifest.exists() {
36 return Err(AdapterError::ManifestNotFound(manifest));
37 }
38 let ectx = EncodeCtx {
39 root: ctx.workspace_root.clone(),
40 tuple: Self::tuple_for(ctx),
41 };
42 let env = RealGoBuildEnv::default();
43 interp::apply(&env, &ectx)
44 .map_err(|e| AdapterError::Internal(format!("gomod encode: {e}")))
45 }
46}
47
48impl Adapter for GomodAdapter {
49 fn name(&self) -> &'static str {
50 "gomod"
51 }
52 fn manifest_files(&self) -> &'static [&'static str] {
53 &["go.mod"]
54 }
55
56 fn lock(&self, _ctx: &AdapterCtx) -> AdapterResult<LockOutcome> {
57 Err(AdapterError::Unsupported("gomod lock not implemented yet".into()))
60 }
61
62 fn build(&self, ctx: &AdapterCtx) -> AdapterResult<gen_types::AdapterBuildSpec> {
63 let spec = Self::encode(ctx)?;
64 let data = serde_json::to_value(&spec)
65 .map_err(|e| AdapterError::Internal(format!("serialize gomod build-spec: {e}")))?;
66 Ok(gen_types::AdapterBuildSpec {
67 ecosystem: "gomod".to_string(),
68 schema_version: build_spec::SCHEMA_VERSION,
69 data,
70 })
71 }
72
73 fn plan(&self, _ctx: &AdapterCtx, _intent: &PlanIntent) -> AdapterResult<Plan> {
74 Err(AdapterError::Unsupported("gomod plan not implemented yet".into()))
75 }
76
77 fn confirm(&self, ctx: &AdapterCtx) -> AdapterResult<ConfirmReport> {
78 let spec = Self::encode(ctx)?;
79 let mut held: Vec<String> = Vec::new();
80 let mut broken: Vec<InvariantBreak> = Vec::new();
81
82 let violations = crate::invariants::check(&spec);
83 if violations.is_empty() {
84 held.push("invariants::check".to_string());
85 } else {
86 for v in &violations {
87 let (name, locus) = crate::invariants::violation_locus(v);
88 let message = serde_json::to_string(v).unwrap_or_else(|_| format!("{v:?}"));
89 broken.push(InvariantBreak { name: name.to_string(), message, locus });
90 }
91 }
92
93 Ok(ConfirmReport { invariants_held: held, invariants_broken: broken })
94 }
95
96 fn diff(&self, _ctx: &AdapterCtx, _against: &DiffRef) -> AdapterResult<DiffReport> {
97 Err(AdapterError::Unsupported("gomod diff not implemented yet".into()))
98 }
99
100 fn sbom(&self, _ctx: &AdapterCtx, _format: SbomFormat) -> AdapterResult<Sbom> {
101 Err(AdapterError::Unsupported("gomod sbom not implemented yet".into()))
102 }
103
104 fn quirks_registry(&self) -> Vec<gen_types::AdapterQuirkEntry> {
105 use gen_types::QuirkRegistry;
106 <crate::quirks::GomodQuirks as QuirkRegistry>::registry()
107 .into_iter()
108 .map(|(p, qs)| gen_types::AdapterQuirkEntry {
109 package: p.to_string(),
110 quirks: qs.into_iter().filter_map(|q| serde_json::to_value(&q).ok()).collect(),
111 })
112 .collect()
113 }
114
115 fn dispatcher_reflection(&self) -> Vec<gen_types::DispatcherVariant> {
116 use gen_types::TypedDispatcher;
117 <crate::quirks::GomodQuirk as TypedDispatcher>::variant_fields()
118 .into_iter()
119 .map(|(kind, fields)| gen_types::DispatcherVariant {
120 kind: kind.to_string(),
121 fields: fields.into_iter().map(str::to_string).collect(),
122 })
123 .collect()
124 }
125}
126
127pub fn ctx_for(workspace_root: PathBuf) -> AdapterCtx {
128 AdapterCtx { workspace_root, target: None }
129}
130
131inventory::submit! {
134 gen_types::AdapterRegistration {
135 make: || Box::new(GomodAdapter),
136 name: "gomod",
137 }
138}