Skip to main content

relay_knowledge/domain/operations/software/
lifecycle.rs

1use serde::{Deserialize, Serialize};
2
3use super::super::{DomainError, GraphVersion, RepositoryCodeRange, error::required_text};
4use super::validation::{normalize_optional, stable_software_id, validate_confidence};
5
6/// Build target, script, profile, or generator entry projected from indexed repository evidence.
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub struct SoftwareBuildTarget {
9    pub target_id: String,
10    pub repository_id: String,
11    pub source_scope: String,
12    pub ecosystem: String,
13    pub language_id: String,
14    pub name: String,
15    pub kind: String,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub command: Option<String>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub output_hint: Option<String>,
20    pub source_kind: String,
21    pub evidence_path: String,
22    pub evidence_line_range: RepositoryCodeRange,
23    pub confidence_basis_points: u16,
24    pub created_graph_version: GraphVersion,
25}
26
27impl SoftwareBuildTarget {
28    /// Creates a validated build target projection row.
29    pub fn new(input: SoftwareBuildTargetInput) -> Result<Self, DomainError> {
30        let source_scope = required_text("source_scope", input.source_scope)?;
31        let ecosystem = required_text("ecosystem", input.ecosystem)?;
32        let language_id = required_text("language_id", input.language_id)?;
33        let name = required_text("build_target_name", input.name)?;
34        let kind = required_text("build_target_kind", input.kind)?;
35        let command = normalize_optional("command", input.command)?;
36        let output_hint = normalize_optional("output_hint", input.output_hint)?;
37        let source_kind = required_text("source_kind", input.source_kind)?;
38        let evidence_path = required_text("evidence_path", input.evidence_path)?;
39        let line_start = input.evidence_line_range.start.to_string();
40
41        Ok(Self {
42            target_id: stable_software_id(
43                "build_target",
44                [
45                    source_scope.as_str(),
46                    ecosystem.as_str(),
47                    language_id.as_str(),
48                    name.as_str(),
49                    kind.as_str(),
50                    source_kind.as_str(),
51                    evidence_path.as_str(),
52                    line_start.as_str(),
53                ],
54            ),
55            repository_id: required_text("repository_id", input.repository_id)?,
56            source_scope,
57            ecosystem,
58            language_id,
59            name,
60            kind,
61            command,
62            output_hint,
63            source_kind,
64            evidence_path,
65            evidence_line_range: input.evidence_line_range,
66            confidence_basis_points: validate_confidence(input.confidence_basis_points)?,
67            created_graph_version: input.created_graph_version,
68        })
69    }
70}
71
72/// Constructor input for `SoftwareBuildTarget`.
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct SoftwareBuildTargetInput {
75    pub repository_id: String,
76    pub source_scope: String,
77    pub ecosystem: String,
78    pub language_id: String,
79    pub name: String,
80    pub kind: String,
81    pub command: Option<String>,
82    pub output_hint: Option<String>,
83    pub source_kind: String,
84    pub evidence_path: String,
85    pub evidence_line_range: RepositoryCodeRange,
86    pub confidence_basis_points: u16,
87    pub created_graph_version: GraphVersion,
88}
89
90/// Infrastructure, deployment, or service-operation resource projected from indexed IaC evidence.
91#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
92pub struct SoftwareIacResource {
93    pub resource_id: String,
94    pub repository_id: String,
95    pub source_scope: String,
96    pub language_id: String,
97    pub provider: String,
98    pub resource_kind: String,
99    pub name: String,
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub scope_hint: Option<String>,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub target_hint: Option<String>,
104    pub resolution_state: String,
105    pub source_kind: String,
106    pub evidence_path: String,
107    pub evidence_line_range: RepositoryCodeRange,
108    pub confidence_basis_points: u16,
109    pub created_graph_version: GraphVersion,
110}
111
112impl SoftwareIacResource {
113    /// Creates a validated IaC/deployment resource projection row.
114    pub fn new(input: SoftwareIacResourceInput) -> Result<Self, DomainError> {
115        let source_scope = required_text("source_scope", input.source_scope)?;
116        let language_id = required_text("language_id", input.language_id)?;
117        let provider = required_text("iac_provider", input.provider)?;
118        let resource_kind = required_text("iac_resource_kind", input.resource_kind)?;
119        let name = required_text("iac_resource_name", input.name)?;
120        let scope_hint = normalize_optional("scope_hint", input.scope_hint)?;
121        let target_hint = normalize_optional("target_hint", input.target_hint)?;
122        let resolution_state = required_text("resolution_state", input.resolution_state)?;
123        let source_kind = required_text("source_kind", input.source_kind)?;
124        let evidence_path = required_text("evidence_path", input.evidence_path)?;
125        let line_start = input.evidence_line_range.start.to_string();
126
127        Ok(Self {
128            resource_id: stable_software_id(
129                "iac_resource",
130                [
131                    source_scope.as_str(),
132                    language_id.as_str(),
133                    provider.as_str(),
134                    resource_kind.as_str(),
135                    name.as_str(),
136                    source_kind.as_str(),
137                    evidence_path.as_str(),
138                    line_start.as_str(),
139                ],
140            ),
141            repository_id: required_text("repository_id", input.repository_id)?,
142            source_scope,
143            language_id,
144            provider,
145            resource_kind,
146            name,
147            scope_hint,
148            target_hint,
149            resolution_state,
150            source_kind,
151            evidence_path,
152            evidence_line_range: input.evidence_line_range,
153            confidence_basis_points: validate_confidence(input.confidence_basis_points)?,
154            created_graph_version: input.created_graph_version,
155        })
156    }
157}
158
159/// Constructor input for `SoftwareIacResource`.
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct SoftwareIacResourceInput {
162    pub repository_id: String,
163    pub source_scope: String,
164    pub language_id: String,
165    pub provider: String,
166    pub resource_kind: String,
167    pub name: String,
168    pub scope_hint: Option<String>,
169    pub target_hint: Option<String>,
170    pub resolution_state: String,
171    pub source_kind: String,
172    pub evidence_path: String,
173    pub evidence_line_range: RepositoryCodeRange,
174    pub confidence_basis_points: u16,
175    pub created_graph_version: GraphVersion,
176}
177
178/// Design, architecture, module, component, interface, or capability evidence.
179#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
180pub struct SoftwareDesignElement {
181    pub element_id: String,
182    pub repository_id: String,
183    pub source_scope: String,
184    pub language_id: String,
185    pub element_kind: String,
186    pub name: String,
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub parent: Option<String>,
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub summary: Option<String>,
191    pub source_kind: String,
192    pub evidence_path: String,
193    pub evidence_line_range: RepositoryCodeRange,
194    pub confidence_basis_points: u16,
195    pub created_graph_version: GraphVersion,
196}
197
198impl SoftwareDesignElement {
199    /// Creates a validated design projection row.
200    pub fn new(input: SoftwareDesignElementInput) -> Result<Self, DomainError> {
201        let source_scope = required_text("source_scope", input.source_scope)?;
202        let language_id = required_text("language_id", input.language_id)?;
203        let element_kind = required_text("design_element_kind", input.element_kind)?;
204        let name = required_text("design_element_name", input.name)?;
205        let parent = normalize_optional("parent", input.parent)?;
206        let summary = normalize_optional("summary", input.summary)?;
207        let source_kind = required_text("source_kind", input.source_kind)?;
208        let evidence_path = required_text("evidence_path", input.evidence_path)?;
209        let line_start = input.evidence_line_range.start.to_string();
210
211        Ok(Self {
212            element_id: stable_software_id(
213                "design_element",
214                [
215                    source_scope.as_str(),
216                    language_id.as_str(),
217                    element_kind.as_str(),
218                    name.as_str(),
219                    source_kind.as_str(),
220                    evidence_path.as_str(),
221                    line_start.as_str(),
222                ],
223            ),
224            repository_id: required_text("repository_id", input.repository_id)?,
225            source_scope,
226            language_id,
227            element_kind,
228            name,
229            parent,
230            summary,
231            source_kind,
232            evidence_path,
233            evidence_line_range: input.evidence_line_range,
234            confidence_basis_points: validate_confidence(input.confidence_basis_points)?,
235            created_graph_version: input.created_graph_version,
236        })
237    }
238}
239
240/// Constructor input for `SoftwareDesignElement`.
241#[derive(Debug, Clone, PartialEq, Eq)]
242pub struct SoftwareDesignElementInput {
243    pub repository_id: String,
244    pub source_scope: String,
245    pub language_id: String,
246    pub element_kind: String,
247    pub name: String,
248    pub parent: Option<String>,
249    pub summary: Option<String>,
250    pub source_kind: String,
251    pub evidence_path: String,
252    pub evidence_line_range: RepositoryCodeRange,
253    pub confidence_basis_points: u16,
254    pub created_graph_version: GraphVersion,
255}
256
257#[cfg(test)]
258#[path = "lifecycle_tests.rs"]
259mod tests;