Skip to main content

aft/inspect/oxc_engine/
types.rs

1use std::collections::BTreeSet;
2use std::fmt;
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7pub const OXC_PROVENANCE: &str = "oxc";
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
10pub struct FileId(pub usize);
11
12#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
13#[serde(tag = "kind", content = "name", rename_all = "snake_case")]
14pub enum ExportName {
15    Named(String),
16    Default,
17}
18
19impl ExportName {
20    pub fn matches_str(&self, name: &str) -> bool {
21        match self {
22            Self::Named(value) => value == name,
23            Self::Default => name == "default",
24        }
25    }
26
27    pub fn as_symbol(&self) -> String {
28        match self {
29            Self::Named(value) => value.clone(),
30            Self::Default => "default".to_string(),
31        }
32    }
33}
34
35impl fmt::Display for ExportName {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            Self::Named(name) => f.write_str(name),
39            Self::Default => f.write_str("default"),
40        }
41    }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(rename_all = "snake_case")]
46pub enum ImportKind {
47    Named,
48    Default,
49    Namespace,
50    SideEffect,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(rename_all = "snake_case")]
55pub enum ReExportKind {
56    Named,
57    Star,
58    Namespace,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub struct DecoratorFact {
63    pub name: String,
64    pub segments: Vec<String>,
65    pub line: u32,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct ExportFact {
70    pub name: ExportName,
71    pub local_name: Option<String>,
72    pub kind: String,
73    pub is_type_only: bool,
74    pub line: u32,
75    pub declared: bool,
76    #[serde(default, skip_serializing_if = "Vec::is_empty")]
77    pub decorators: Vec<DecoratorFact>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct ImportFact {
82    pub source: String,
83    pub kind: ImportKind,
84    pub imported_name: Option<String>,
85    pub local_name: Option<String>,
86    pub is_type_only: bool,
87    pub line: u32,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct ReExportFact {
92    pub source: String,
93    pub kind: ReExportKind,
94    pub imported_name: Option<String>,
95    pub exported_name: Option<String>,
96    pub is_type_only: bool,
97    pub line: u32,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct DynamicImportFact {
102    pub source: Option<String>,
103    pub is_literal: bool,
104    pub line: u32,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct FileFacts {
109    pub file_id: FileId,
110    pub path: PathBuf,
111    pub content_hash: String,
112    pub exports: Vec<ExportFact>,
113    pub imports: Vec<ImportFact>,
114    pub re_exports: Vec<ReExportFact>,
115    pub dynamic_imports: Vec<DynamicImportFact>,
116    pub same_file_value_references: BTreeSet<String>,
117    pub used_import_bindings: BTreeSet<String>,
118    pub type_referenced_import_bindings: BTreeSet<String>,
119    pub value_referenced_import_bindings: BTreeSet<String>,
120    pub parse_error: Option<String>,
121}
122
123impl FileFacts {
124    pub fn empty(
125        file_id: FileId,
126        path: PathBuf,
127        content_hash: String,
128        parse_error: String,
129    ) -> Self {
130        Self {
131            file_id,
132            path,
133            content_hash,
134            exports: Vec::new(),
135            imports: Vec::new(),
136            re_exports: Vec::new(),
137            dynamic_imports: Vec::new(),
138            same_file_value_references: BTreeSet::new(),
139            used_import_bindings: BTreeSet::new(),
140            type_referenced_import_bindings: BTreeSet::new(),
141            value_referenced_import_bindings: BTreeSet::new(),
142            parse_error: Some(parse_error),
143        }
144    }
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
148#[serde(rename_all = "snake_case")]
149pub enum LivenessVerdict {
150    Used,
151    Unused,
152    Uncertain,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
156pub struct OxcReExportContext {
157    pub file: String,
158    pub line: u32,
159    pub exported_name: String,
160}
161
162fn is_false(value: &bool) -> bool {
163    !*value
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct OxcExportVerdict {
168    pub symbol: String,
169    pub kind: String,
170    pub line: u32,
171    pub verdict: LivenessVerdict,
172    pub reason: String,
173    pub provenance: String,
174    #[serde(default, skip_serializing_if = "is_false")]
175    pub has_references: bool,
176    #[serde(default, skip_serializing_if = "Vec::is_empty")]
177    pub test_only_reference_files: Vec<String>,
178    #[serde(default, skip_serializing_if = "Vec::is_empty")]
179    pub also_reexported: Vec<OxcReExportContext>,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct OxcFileVerdicts {
184    pub file: PathBuf,
185    pub relative_file: String,
186    pub exports: Vec<OxcExportVerdict>,
187}
188
189impl OxcFileVerdicts {
190    pub fn contribution_payload(&self) -> serde_json::Value {
191        serde_json::json!({
192            "file": self.relative_file,
193            "exports": self.exports,
194            "provenance": OXC_PROVENANCE,
195        })
196    }
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct ResolverConfigInput {
201    pub path: PathBuf,
202    pub content_hash: String,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct OxcResolvedEdge {
207    pub from_file: PathBuf,
208    pub specifier: String,
209    pub resolved_file: Option<PathBuf>,
210    pub kind: String,
211    pub line: u32,
212    pub is_type_only: bool,
213}
214
215#[derive(Debug, Clone, Default, Serialize, Deserialize)]
216pub struct OxcEngineStats {
217    pub files: usize,
218    pub cache_hits: usize,
219    pub cache_misses: usize,
220    pub resolved_edges: usize,
221    pub unresolved_edges: usize,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct OxcEngineError {
226    pub file: PathBuf,
227    pub message: String,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct OxcEngineResult {
232    pub files: Vec<OxcFileVerdicts>,
233    #[serde(default)]
234    pub facts: Vec<FileFacts>,
235    pub resolver_config_inputs: Vec<ResolverConfigInput>,
236    pub resolver_config_fingerprint: String,
237    pub edges: Vec<OxcResolvedEdge>,
238    pub stats: OxcEngineStats,
239    pub errors: Vec<OxcEngineError>,
240    #[serde(default)]
241    pub skipped_outside_root: Vec<PathBuf>,
242}
243
244impl OxcEngineResult {
245    pub fn resolver_config_fingerprint(&self) -> &str {
246        &self.resolver_config_fingerprint
247    }
248}