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, Popen};
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, Popen) -> 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(
94 &self,
95 context: &LightContext,
96 test_name: &str,
97 span: &Span,
98 ) -> Result<Option<(Exec, Option<Box<Postprocess>>)>>;
99}
100
101pub trait AsParse {
102 fn as_parse(&self) -> &dyn Parse;
103 fn as_parse_mut(&mut self) -> &mut dyn Parse;
104}
105
106pub trait AsRun {
107 fn as_run(&self) -> &dyn Run;
108}
109
110impl<T: AsParse> Parse for T {
111 #[cfg_attr(dylint_lib = "general", allow(non_local_effect_before_error_return))]
112 fn parse(
113 &mut self,
114 context: &LightContext,
115 config: &config::Toml,
116 source_files: &[&Path],
117 ) -> Result<(usize, SourceFileSpanTestMap)> {
118 self.as_parse_mut().parse(context, config, source_files)
119 }
120}
121
122impl<T: AsRun> Run for T {
123 fn dry_run(&self, context: &LightContext, source_file: &Path) -> Result<()> {
124 self.as_run().dry_run(context, source_file)
125 }
126 fn instrument_source_file(
127 &self,
128 context: &LightContext,
129 rewriter: &mut Rewriter,
130 source_file: &SourceFile,
131 n_instrumentable_statements: usize,
132 ) -> Result<()> {
133 self.as_run().instrument_source_file(
134 context,
135 rewriter,
136 source_file,
137 n_instrumentable_statements,
138 )
139 }
140 fn statement_prefix_and_suffix(&self, span: &Span) -> Result<(String, String)> {
141 self.as_run().statement_prefix_and_suffix(span)
142 }
143 fn build_source_file(&self, context: &LightContext, source_file: &Path) -> Result<()> {
144 self.as_run().build_source_file(context, source_file)
145 }
146 fn exec(
147 &self,
148 context: &LightContext,
149 test_name: &str,
150 span: &Span,
151 ) -> Result<Option<(Exec, Option<Box<Postprocess>>)>> {
152 self.as_run().exec(context, test_name, span)
153 }
154}