1use std::path::PathBuf;
2
3#[cfg(feature = "inference")]
4use callisto_model::CommandRunner;
5use callisto_model::{CommitSha, Package, Severity, Version};
6
7use crate::config::PreMajorInferencePolicy;
8use crate::error::GraphError;
9
10pub trait SeverityInference: Send + Sync {
11 fn infer(
12 &self,
13 pkg: &Package,
14 window: InferenceWindowSpec<'_>,
15 ) -> Result<Option<InferenceOutcome>, GraphError>;
16}
17
18pub struct InferenceWindowSpec<'a> {
19 pub pathspecs: &'a [PathBuf],
20 pub since: Option<CommitSha>,
21 pub current_version: &'a Version,
22 pub has_prior_release: bool,
23 pub policy: PreMajorInferencePolicy,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct InferenceOutcome {
28 pub severity: Severity,
29 pub commit_count: usize,
30 pub remapped: bool,
31 pub commits: Vec<(CommitSha, String)>,
32}
33
34pub struct NoInference;
35
36impl SeverityInference for NoInference {
37 fn infer(
38 &self,
39 _pkg: &Package,
40 _window: InferenceWindowSpec<'_>,
41 ) -> Result<Option<InferenceOutcome>, GraphError> {
42 Ok(None)
43 }
44}
45
46#[cfg(feature = "inference")]
47pub struct CommitInference<'a, R: CommandRunner> {
48 pub runner: &'a R,
49 pub root: PathBuf,
50}
51
52#[cfg(feature = "inference")]
53impl<'a, R: CommandRunner> SeverityInference for CommitInference<'a, R> {
54 fn infer(
55 &self,
56 pkg: &Package,
57 window: InferenceWindowSpec<'_>,
58 ) -> Result<Option<InferenceOutcome>, GraphError> {
59 use callisto_conventional::{infer_severity, InferenceInput, InferenceWindow};
60
61 let inf_window = match window.since {
62 Some(sha) => InferenceWindow::SinceCommit(sha),
63 None => InferenceWindow::FullHistory,
64 };
65
66 let input = InferenceInput {
67 package: &pkg.id,
68 pathspecs: window.pathspecs,
69 window: inf_window,
70 current_version: window.current_version,
71 has_prior_release: window.has_prior_release,
72 };
73
74 let raw = infer_severity(self.runner, &self.root, &input)?;
75 if raw.commit_count == 0 {
76 return Ok(None);
77 }
78
79 let (severity, remapped) = crate::aggregate::apply_pre_major(
80 raw.severity,
81 window.policy,
82 window.current_version,
83 window.has_prior_release,
84 );
85
86 let commits = raw
87 .commits
88 .into_iter()
89 .map(|c| (c.sha().clone(), c.subject().to_string()))
90 .collect();
91
92 Ok(Some(InferenceOutcome {
93 severity,
94 commit_count: raw.commit_count,
95 remapped,
96 commits,
97 }))
98 }
99}