ruast/
lib.rs

1#[cfg(feature = "tokenize")]
2mod conversion;
3mod expr;
4mod stmt;
5mod token;
6mod ty;
7
8use std::fmt;
9use std::fs::File;
10use std::io::Write;
11use std::path::Path as Pt;
12
13pub use expr::*;
14pub use stmt::*;
15pub use token::*;
16pub use ty::*;
17
18pub mod traits {
19    pub use crate::{
20        Accessible, AddVisibility, Assignable, Awaitable, BinaryOperable, Callable, Castable,
21        EmptyItem, HasItem, Ident, Indexable, IntoConst, IntoTokens, IntoTryBlock, IntoUnsafe,
22        MaybeIdent, MethodCallable, Returnable, Semicolon, Tryable, UnaryOperable, Yieldable,
23    };
24}
25
26macro_rules! impl_obvious_conversion {
27    ($Enum: ident; $($Variant: ident $(,)?)*) => {
28        $(
29            impl From<$Variant> for $Enum {
30                fn from(item: $Variant) -> Self {
31                    Self::$Variant(item)
32                }
33            }
34        )*
35        impl From<$Enum> for $crate::TokenStream {
36            fn from(item: $Enum) -> Self {
37                match item {
38                    $($Enum::$Variant(v) => v.into(),)*
39                }
40            }
41        }
42    };
43}
44pub(crate) use impl_obvious_conversion;
45
46macro_rules! impl_display_for_enum {
47    ($Enum: ident; $($Variant: ident $(,)?)*) => {
48        impl std::fmt::Display for $Enum {
49            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50                match self {
51                    $(
52                        Self::$Variant(item) => write!(f, "{item}"),
53                    )*
54                }
55            }
56        }
57    };
58}
59pub(crate) use impl_display_for_enum;
60
61macro_rules! impl_hasitem_methods {
62    ($Ty: ident) => {
63        impl $Ty {
64            pub fn with_item(self, item: impl Into<Item>) -> Self {
65                HasItem::with_item(self, item)
66            }
67
68            pub fn add_item(&mut self, item: impl Into<Item>) -> $crate::ItemIndex {
69                HasItem::add_item(self, item)
70            }
71
72            pub fn try_remove_item(&mut self, index: usize) -> Option<Item> {
73                HasItem::try_remove_item(self, index)
74            }
75
76            pub fn remove_item(&mut self, index: $crate::ItemIndex) -> Item {
77                HasItem::remove_item(self, index)
78            }
79
80            pub fn try_remove_item_by_id(&mut self, ident: &str) -> Option<Item> {
81                HasItem::try_remove_item_by_id(self, ident)
82            }
83
84            pub fn get_item(&self, index: usize) -> Option<&Item> {
85                HasItem::get_item(self, index)
86            }
87
88            pub fn get_item_by_id(&self, ident: &str) -> Option<&Item> {
89                HasItem::get_item_by_id(self, ident)
90            }
91        }
92        impl std::ops::Deref for $Ty {
93            type Target = [Item];
94
95            fn deref(&self) -> &Self::Target {
96                self.items()
97            }
98        }
99        impl std::ops::DerefMut for $Ty {
100            fn deref_mut(&mut self) -> &mut Self::Target {
101                self.items_mut()
102            }
103        }
104        impl std::ops::Index<$crate::ItemIndex> for $Ty {
105            type Output = Item;
106
107            fn index(&self, index: $crate::ItemIndex) -> &Self::Output {
108                self.get_item(index.0).expect("index out of bounds")
109            }
110        }
111        impl std::ops::IndexMut<$crate::ItemIndex> for $Ty {
112            fn index_mut(&mut self, index: $crate::ItemIndex) -> &mut Self::Output {
113                self.get_item_mut(index.0).expect("index out of bounds")
114            }
115        }
116    };
117    ($Ty: ident, $Item: ident) => {
118        impl $Ty {
119            pub fn with_item(self, item: impl Into<$Item>) -> Self {
120                HasItem::with_item(self, item)
121            }
122
123            pub fn add_item(&mut self, item: impl Into<$Item>) -> $crate::ItemIndex {
124                HasItem::add_item(self, item)
125            }
126
127            pub fn try_remove_item(&mut self, index: usize) -> Option<$Item> {
128                HasItem::try_remove_item(self, index)
129            }
130
131            pub fn remove_item(&mut self, index: $crate::ItemIndex) -> $Item {
132                HasItem::remove_item(self, index)
133            }
134
135            pub fn try_remove_item_by_id(&mut self, ident: &str) -> Option<$Item> {
136                HasItem::try_remove_item_by_id(self, ident)
137            }
138
139            pub fn get_item(&self, index: usize) -> Option<&$Item> {
140                HasItem::get_item(self, index)
141            }
142
143            pub fn get_item_by_id(&self, ident: &str) -> Option<&$Item> {
144                HasItem::get_item_by_id(self, ident)
145            }
146        }
147        impl std::ops::Deref for $Ty {
148            type Target = [$Item];
149
150            fn deref(&self) -> &Self::Target {
151                self.items()
152            }
153        }
154        impl std::ops::DerefMut for $Ty {
155            fn deref_mut(&mut self) -> &mut Self::Target {
156                self.items_mut()
157            }
158        }
159        impl std::ops::Index<$crate::ItemIndex> for $Ty {
160            type Output = $Item;
161
162            fn index(&self, index: $crate::ItemIndex) -> &Self::Output {
163                self.get_item(index.0).expect("index out of bounds")
164            }
165        }
166        impl std::ops::IndexMut<$crate::ItemIndex> for $Ty {
167            fn index_mut(&mut self, index: $crate::ItemIndex) -> &mut Self::Output {
168                self.get_item_mut(index.0).expect("index out of bounds")
169            }
170        }
171    };
172    ($Ty: ident, $Item: ident, Deref) => {
173        impl std::ops::Deref for $Ty {
174            type Target = [$Item];
175
176            fn deref(&self) -> &Self::Target {
177                self.items()
178            }
179        }
180        impl std::ops::DerefMut for $Ty {
181            fn deref_mut(&mut self) -> &mut Self::Target {
182                self.items_mut()
183            }
184        }
185    };
186}
187pub(crate) use impl_hasitem_methods;
188
189#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
190#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
191pub struct Crate {
192    pub attrs: Vec<Attribute>,
193    pub items: Vec<Item>,
194}
195
196impl EmptyItem for Crate {
197    type Input = ();
198    fn empty(_: impl Into<()>) -> Self {
199        Self::new()
200    }
201}
202
203impl HasItem for Crate {
204    fn items(&self) -> &[Item] {
205        &self.items
206    }
207    fn items_mut(&mut self) -> &mut Vec<Item> {
208        &mut self.items
209    }
210}
211
212impl_hasitem_methods!(Crate);
213
214impl fmt::Display for Crate {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        for attr in self.attrs.iter() {
217            writeln!(f, "{attr}")?;
218        }
219        writeln!(f)?;
220        for item in self.items.iter() {
221            writeln!(f, "{item}")?;
222        }
223        Ok(())
224    }
225}
226
227impl From<Crate> for TokenStream {
228    fn from(value: Crate) -> Self {
229        let mut ts = TokenStream::new();
230        for attr in value.attrs {
231            ts.extend(TokenStream::from(attr));
232        }
233        for item in value.items {
234            ts.extend(TokenStream::from(item));
235        }
236        ts
237    }
238}
239
240#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
241pub struct CompileOptions {
242    pub allow: Option<String>,
243    pub cap_lints: Option<String>,
244    pub cfg: Option<String>,
245    pub check_cfg: Option<String>,
246    pub crate_name: Option<String>,
247    pub crate_type: Option<String>,
248    pub deny: Option<String>,
249    pub edition: Option<String>,
250    pub emit: Option<String>,
251    pub explain: Option<String>,
252    pub forbid: Option<String>,
253    pub force_warn: Option<String>,
254    pub g: bool,
255    pub o: bool,
256    pub print: Option<String>,
257    pub target: Option<String>,
258    pub test: bool,
259    pub out_dir: Option<String>,
260    pub verbose: bool,
261    pub warn: Option<String>,
262}
263
264impl Crate {
265    pub fn new() -> Self {
266        Self {
267            attrs: Vec::new(),
268            items: Vec::new(),
269        }
270    }
271
272    pub fn dump(self, path: impl AsRef<Pt>) -> Result<(), std::io::Error> {
273        let mut file = File::create(path)?;
274        write!(file, "{self}")?;
275        Ok(())
276    }
277
278    pub fn compile(
279        self,
280        rs_path: impl AsRef<Pt>,
281        options: CompileOptions,
282    ) -> Result<(), std::io::Error> {
283        let rs_path = rs_path.as_ref();
284        let mut file = File::create(rs_path)?;
285        write!(file, "{self}")?;
286        drop(file);
287        let mut cmd = std::process::Command::new("rustc");
288        if let Some(allow) = options.allow {
289            cmd.arg("--allow").arg(allow);
290        }
291        if let Some(cap_lints) = options.cap_lints {
292            cmd.arg("--cap-lints").arg(cap_lints);
293        }
294        if let Some(cfg) = options.cfg {
295            cmd.arg("--cfg").arg(cfg);
296        }
297        if let Some(check_cfg) = options.check_cfg {
298            cmd.arg("--check-cfg").arg(check_cfg);
299        }
300        if let Some(crate_name) = options.crate_name {
301            cmd.arg("--crate-name").arg(crate_name);
302        }
303        if let Some(crate_type) = options.crate_type {
304            cmd.arg("--crate-type").arg(crate_type);
305        }
306        if let Some(deny) = options.deny {
307            cmd.arg("--deny").arg(deny);
308        }
309        if let Some(edition) = options.edition {
310            cmd.arg("--edition").arg(edition);
311        }
312        if let Some(emit) = options.emit {
313            cmd.arg("--emit").arg(emit);
314        }
315        if let Some(explain) = options.explain {
316            cmd.arg("--explain").arg(explain);
317        }
318        if let Some(forbid) = options.forbid {
319            cmd.arg("--forbid").arg(forbid);
320        }
321        if let Some(force_warn) = options.force_warn {
322            cmd.arg("--force-warn").arg(force_warn);
323        }
324        if options.g {
325            cmd.arg("-g");
326        }
327        if options.o {
328            cmd.arg("-O");
329        }
330        if let Some(print) = options.print {
331            cmd.arg("--print").arg(print);
332        }
333        if let Some(target) = options.target {
334            cmd.arg("--target").arg(target);
335        }
336        if options.test {
337            cmd.arg("--test");
338        }
339        if let Some(out_dir) = options.out_dir {
340            cmd.arg("--out-dir").arg(out_dir);
341        }
342        if options.verbose {
343            cmd.arg("--verbose");
344        }
345        if let Some(warn) = options.warn {
346            cmd.arg("--warn").arg(warn);
347        }
348        cmd.arg(rs_path).output()?;
349        Ok(())
350    }
351}
352
353#[cfg(feature = "tokenize")]
354impl_to_tokens!(Crate,);