Skip to main content

necessist_backends/
lib.rs

1use anyhow::Result;
2use clap::ValueEnum;
3use heck::ToKebabCase;
4use necessist_core::{
5    __Rewriter as Rewriter, LightContext, SourceFile, Span,
6    framework::{
7        Applicable, AsParse, AsRun, Interface, Parse as ParseHigh, Postprocess, Run as RunHigh,
8        ToImplementation,
9    },
10};
11use std::{cell::RefCell, path::Path, rc::Rc};
12use strum_macros::EnumIter;
13use subprocess::Exec;
14
15// Framework modules
16
17mod anchor;
18use anchor::Anchor;
19
20mod foundry;
21use foundry::Foundry;
22
23mod go;
24use go::Go;
25
26mod hardhat;
27use hardhat::Hardhat;
28
29mod php;
30use php::Php;
31
32mod python;
33use python::Python;
34
35mod rust;
36use rust::Rust;
37
38mod vitest;
39use vitest::Vitest;
40
41// Other modules
42
43mod directives;
44use directives::DirectiveSyntax;
45
46mod parsing;
47use parsing::{AbstractTypes, MaybeNamed, Named, ParseAdapter, ParseLow, Spanned, WalkDirResult};
48
49mod generic_visitor;
50use generic_visitor::GenericVisitor;
51
52mod running;
53use running::{ProcessLines, RunAdapter, RunLow};
54
55mod tree_sitter_utils;
56
57mod ts;
58
59mod utils;
60use utils::{OutputAccessors, OutputStrippedOfAnsiScapes};
61
62#[derive(Debug, Clone, Copy, EnumIter, Eq, Ord, PartialEq, PartialOrd, ValueEnum)]
63#[non_exhaustive]
64#[remain::sorted]
65pub enum Identifier {
66    Anchor,
67    Foundry,
68    Go,
69    Hardhat,
70    Php,
71    Python,
72    Rust,
73    Vitest,
74}
75
76impl Applicable for Identifier {
77    fn applicable(&self, context: &LightContext) -> Result<bool> {
78        match *self {
79            Self::Anchor => Anchor::applicable(context),
80            Self::Foundry => Foundry::applicable(context),
81            Self::Go => Go::applicable(context),
82            Self::Hardhat => Hardhat::applicable(context),
83            Self::Php => Php::applicable(context),
84            Self::Python => Python::applicable(context),
85            Self::Rust => Rust::applicable(context),
86            Self::Vitest => Vitest::applicable(context),
87        }
88    }
89}
90
91impl ToImplementation for Identifier {
92    // smoelius: `Anchor`, `Hardhat`, and `Vitest` implement the `ParseLow` interface indirectly
93    // through `ts::Mocha`. They implement the high-level `Run` interface directly.
94    fn to_implementation(&self, context: &LightContext) -> Result<Option<Box<dyn Interface>>> {
95        match *self {
96            Self::Anchor => {
97                let anchor = Anchor::new(context)?;
98                Ok(Some(Box::new(anchor)))
99            }
100
101            Self::Foundry => Ok(Some(implementation_as_interface(ParseRunAdapter::new)(
102                Foundry::new(),
103            ))),
104
105            Self::Go => Ok(Some(implementation_as_interface(ParseRunAdapter::new)(
106                Go::new(),
107            ))),
108
109            Self::Hardhat => Ok(Some(Box::new(Hardhat::new()))),
110
111            Self::Php => Ok(Some(implementation_as_interface(ParseRunAdapter::new)(
112                Php::new(),
113            ))),
114
115            Self::Python => {
116                let python = Python::new(context)?;
117                Ok(Some(implementation_as_interface(ParseRunAdapter::new)(
118                    python,
119                )))
120            }
121
122            Self::Rust => Ok(Some(implementation_as_interface(ParseRunAdapter::new)(
123                Rust::new(),
124            ))),
125
126            Self::Vitest => Ok(Some(Box::new(Vitest::new()))),
127        }
128    }
129}
130
131impl std::fmt::Display for Identifier {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        write!(f, "{}", format!("{self:?}").to_kebab_case())
134    }
135}
136
137/// Utility function
138fn implementation_as_interface<T, U: Interface + 'static>(
139    adapter: impl Fn(T) -> U,
140) -> impl Fn(T) -> Box<dyn Interface> {
141    move |implementation| Box::new(adapter(implementation)) as Box<dyn Interface>
142}
143
144impl<T: RunHigh> RunHigh for ParseAdapter<T> {
145    fn dry_run(&self, context: &LightContext, source_file: &Path) -> Result<()> {
146        self.0.dry_run(context, source_file)
147    }
148    fn instrument_source_file(
149        &self,
150        context: &LightContext,
151        rewriter: &mut Rewriter,
152        source_file: &SourceFile,
153        n_instrumentable_statements: usize,
154    ) -> Result<()> {
155        self.0
156            .instrument_source_file(context, rewriter, source_file, n_instrumentable_statements)
157    }
158    fn statement_is_instrumentable(&self, span: &Span) -> bool {
159        self.0.statement_is_instrumentable(span)
160    }
161    fn statement_prefix_and_suffix(&self, span: &Span) -> Result<(String, String)> {
162        self.0.statement_prefix_and_suffix(span)
163    }
164    fn statement_replacement(&self, span: &Span) -> &'static str {
165        self.0.statement_replacement(span)
166    }
167    fn build_source_file(&self, context: &LightContext, source_file: &Path) -> Result<()> {
168        self.0.build_source_file(context, source_file)
169    }
170    fn exec(
171        &self,
172        context: &LightContext,
173        test_name: &str,
174        span: &Span,
175    ) -> Result<Result<(Exec, Option<Box<Postprocess>>)>> {
176        self.0.exec(context, test_name, span)
177    }
178}
179
180impl<T: ParseLow + RunHigh> Interface for ParseAdapter<T> {}
181
182struct ParseRunAdapter<T> {
183    parse: ParseAdapter<Rc<RefCell<T>>>,
184    run: RunAdapter<Rc<RefCell<T>>>,
185}
186
187impl<T> ParseRunAdapter<T> {
188    fn new(value: T) -> Self {
189        let rc = Rc::new(RefCell::new(value));
190        Self {
191            parse: ParseAdapter(rc.clone()),
192            run: RunAdapter(rc),
193        }
194    }
195}
196
197impl<T: ParseLow> AsParse for ParseRunAdapter<T> {
198    fn as_parse(&self) -> &dyn ParseHigh {
199        &self.parse
200    }
201    fn as_parse_mut(&mut self) -> &mut dyn ParseHigh {
202        &mut self.parse
203    }
204}
205
206impl<T: RunLow> AsRun for ParseRunAdapter<T> {
207    fn as_run(&self) -> &dyn RunHigh {
208        &self.run
209    }
210}
211
212impl<T: ParseLow + RunLow> Interface for ParseRunAdapter<T> {}