1pub mod substrate_builder;
17
18use anyhow::Result;
19use serde::{Serialize, de::DeserializeOwned};
20use tokmd_envelope::SensorReport;
21use tokmd_substrate::RepoSubstrate;
22
23pub trait EffortlessSensor {
34 type Settings: Serialize + DeserializeOwned;
36
37 fn name(&self) -> &str;
39
40 fn version(&self) -> &str;
42
43 fn run(&self, settings: &Self::Settings, substrate: &RepoSubstrate) -> Result<SensorReport>;
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use std::collections::BTreeMap;
51 use tokmd_envelope::{SensorReport, ToolMeta, Verdict};
52 use tokmd_substrate::{LangSummary, RepoSubstrate, SubstrateFile};
53
54 struct DummySensor;
56
57 #[derive(serde::Serialize, serde::Deserialize)]
58 struct DummySettings {
59 threshold: usize,
60 }
61
62 impl EffortlessSensor for DummySensor {
63 type Settings = DummySettings;
64
65 fn name(&self) -> &str {
66 "dummy"
67 }
68
69 fn version(&self) -> &str {
70 "0.1.0"
71 }
72
73 fn run(&self, settings: &DummySettings, substrate: &RepoSubstrate) -> Result<SensorReport> {
74 let verdict = if substrate.total_code_lines > settings.threshold {
75 Verdict::Warn
76 } else {
77 Verdict::Pass
78 };
79 Ok(SensorReport::new(
80 ToolMeta::new(self.name(), self.version(), "check"),
81 "2024-01-01T00:00:00Z".to_string(),
82 verdict,
83 format!(
84 "{} code lines (threshold: {})",
85 substrate.total_code_lines, settings.threshold
86 ),
87 ))
88 }
89 }
90
91 fn sample_substrate() -> RepoSubstrate {
92 RepoSubstrate {
93 repo_root: ".".to_string(),
94 files: vec![SubstrateFile {
95 path: "src/lib.rs".to_string(),
96 lang: "Rust".to_string(),
97 code: 100,
98 lines: 120,
99 bytes: 3000,
100 tokens: 750,
101 module: "src".to_string(),
102 in_diff: false,
103 }],
104 lang_summary: BTreeMap::from([(
105 "Rust".to_string(),
106 LangSummary {
107 files: 1,
108 code: 100,
109 lines: 120,
110 bytes: 3000,
111 tokens: 750,
112 },
113 )]),
114 diff_range: None,
115 total_tokens: 750,
116 total_bytes: 3000,
117 total_code_lines: 100,
118 }
119 }
120
121 #[test]
122 fn dummy_sensor_pass() {
123 let sensor = DummySensor;
124 let settings = DummySettings { threshold: 200 };
125 let substrate = sample_substrate();
126 let report = sensor.run(&settings, &substrate).unwrap();
127 assert_eq!(report.verdict, Verdict::Pass);
128 }
129
130 #[test]
131 fn dummy_sensor_warn() {
132 let sensor = DummySensor;
133 let settings = DummySettings { threshold: 50 };
134 let substrate = sample_substrate();
135 let report = sensor.run(&settings, &substrate).unwrap();
136 assert_eq!(report.verdict, Verdict::Warn);
137 }
138
139 #[test]
140 fn sensor_name_and_version() {
141 let sensor = DummySensor;
142 assert_eq!(sensor.name(), "dummy");
143 assert_eq!(sensor.version(), "0.1.0");
144 }
145}