necessist_core/framework/
mod.rs1use crate::{LightContext, SourceFile, Span, config, rewriter::Rewriter};
2use anyhow::Result;
3use indexmap::IndexSet;
4use std::{
5 collections::{BTreeMap, BTreeSet},
6 path::Path,
7};
8use subprocess::{Exec, Job};
9
10mod auto;
11pub use auto::Auto;
12
13mod empty;
14pub use empty::Empty;
15
16mod union;
17pub use union::Union;
18
19#[allow(dead_code)]
20type AutoUnion<T, U> = Auto<Union<T, U>>;
21
22pub trait Applicable {
23 fn applicable(&self, context: &LightContext) -> Result<bool>;
24}
25
26pub trait ToImplementation {
27 fn to_implementation(&self, context: &LightContext) -> Result<Option<Box<dyn Interface>>>;
28}
29
30pub trait Interface: Parse + Run {}
31
32pub type TestSet = BTreeSet<String>;
33
34pub type SourceFileSpanTestMap = BTreeMap<SourceFile, SpanTestMaps>;
35
36#[derive(Default)]
37pub struct SpanTestMaps {
38 pub statement: SpanTestMap,
39 pub method_call: SpanTestMap,
40}
41
42impl SpanTestMaps {
43 pub fn iter(&self) -> impl Iterator<Item = (&Span, SpanKind, &IndexSet<String>)> {
44 self.statement
45 .iter()
46 .map(|(span, test_names)| (span, SpanKind::Statement, test_names))
47 .chain(
48 self.method_call
49 .iter()
50 .map(|(span, test_names)| (span, SpanKind::MethodCall, test_names)),
51 )
52 }
53}
54
55pub type SpanTestMap = BTreeMap<Span, IndexSet<String>>;
59
60#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61pub enum SpanKind {
62 Statement,
63 MethodCall,
64}
65
66pub trait Parse {
67 fn parse(
68 &mut self,
69 context: &LightContext,
70 config: &config::Toml,
71 source_files: &[&Path],
72 ) -> Result<(usize, SourceFileSpanTestMap)>;
73}
74
75pub type Postprocess = dyn Fn(&LightContext, Job) -> Result<bool>;
76
77pub trait Run {
78 fn dry_run(&self, context: &LightContext, source_file: &Path) -> Result<()>;
79 fn instrument_source_file(
80 &self,
81 context: &LightContext,
82 rewriter: &mut Rewriter,
83 source_file: &SourceFile,
84 n_instrumentable_statements: usize,
85 ) -> Result<()>;
86 fn statement_prefix_and_suffix(&self, span: &Span) -> Result<(String, String)>;
87 fn build_source_file(&self, context: &LightContext, source_file: &Path) -> Result<()>;
88 fn exec(
98 &self,
99 context: &LightContext,
100 test_name: &str,
101 span: &Span,
102 ) -> Result<Result<(Exec, Option<Box<Postprocess>>)>>;
103}
104
105pub trait AsParse {
106 fn as_parse(&self) -> &dyn Parse;
107 fn as_parse_mut(&mut self) -> &mut dyn Parse;
108}
109
110pub trait AsRun {
111 fn as_run(&self) -> &dyn Run;
112}
113
114impl<T: AsParse> Parse for T {
115 #[cfg_attr(dylint_lib = "general", allow(non_local_effect_before_error_return))]
116 fn parse(
117 &mut self,
118 context: &LightContext,
119 config: &config::Toml,
120 source_files: &[&Path],
121 ) -> Result<(usize, SourceFileSpanTestMap)> {
122 self.as_parse_mut().parse(context, config, source_files)
123 }
124}
125
126impl<T: AsRun> Run for T {
127 fn dry_run(&self, context: &LightContext, source_file: &Path) -> Result<()> {
128 self.as_run().dry_run(context, source_file)
129 }
130 fn instrument_source_file(
131 &self,
132 context: &LightContext,
133 rewriter: &mut Rewriter,
134 source_file: &SourceFile,
135 n_instrumentable_statements: usize,
136 ) -> Result<()> {
137 self.as_run().instrument_source_file(
138 context,
139 rewriter,
140 source_file,
141 n_instrumentable_statements,
142 )
143 }
144 fn statement_prefix_and_suffix(&self, span: &Span) -> Result<(String, String)> {
145 self.as_run().statement_prefix_and_suffix(span)
146 }
147 fn build_source_file(&self, context: &LightContext, source_file: &Path) -> Result<()> {
148 self.as_run().build_source_file(context, source_file)
149 }
150 fn exec(
151 &self,
152 context: &LightContext,
153 test_name: &str,
154 span: &Span,
155 ) -> Result<Result<(Exec, Option<Box<Postprocess>>)>> {
156 self.as_run().exec(context, test_name, span)
157 }
158}