adze_parser_feature_profile_core/
lib.rs1#![forbid(unsafe_op_in_unsafe_fn)]
4#![deny(missing_docs)]
5#![cfg_attr(feature = "strict_api", deny(unreachable_pub))]
6#![cfg_attr(not(feature = "strict_api"), warn(unreachable_pub))]
7#![cfg_attr(feature = "strict_docs", deny(missing_docs))]
8#![cfg_attr(not(feature = "strict_docs"), allow(missing_docs))]
9
10use core::fmt::{self, Display, Formatter};
11
12pub use adze_parser_backend_core::ParserBackend;
14pub use adze_parser_backend_core::ParserBackendSelection;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub struct ParserFeatureProfile {
19 pub pure_rust: bool,
21 pub tree_sitter_standard: bool,
23 pub tree_sitter_c2rust: bool,
25 pub glr: bool,
27}
28
29impl ParserFeatureProfile {
30 #[must_use]
32 pub const fn current() -> Self {
33 Self {
34 pure_rust: cfg!(feature = "pure-rust"),
35 tree_sitter_standard: cfg!(feature = "tree-sitter-standard"),
36 tree_sitter_c2rust: cfg!(feature = "tree-sitter-c2rust"),
37 glr: cfg!(feature = "glr"),
38 }
39 }
40
41 #[must_use]
43 pub const fn resolve_backend(self, has_conflicts: bool) -> ParserBackend {
44 match Self::backend_selection_contract(self, has_conflicts) {
45 ParserBackendSelection::Backend(backend) => backend,
46 ParserBackendSelection::ConflictsRequireGlr => panic!(
47 "Grammar has conflicts but GLR feature is not enabled. Enable the 'glr' feature in Cargo.toml or use the tree-sitter C runtime."
48 ),
49 }
50 }
51
52 #[must_use]
57 pub const fn backend_selection_contract(self, has_conflicts: bool) -> ParserBackendSelection {
58 if self.glr {
59 ParserBackendSelection::Backend(ParserBackend::GLR)
60 } else if self.pure_rust {
61 if has_conflicts {
62 ParserBackendSelection::ConflictsRequireGlr
63 } else {
64 ParserBackendSelection::Backend(ParserBackend::PureRust)
65 }
66 } else {
67 ParserBackendSelection::Backend(ParserBackend::TreeSitter)
68 }
69 }
70
71 #[must_use]
73 pub const fn has_pure_rust(self) -> bool {
74 self.pure_rust
75 }
76
77 #[must_use]
79 pub const fn has_glr(self) -> bool {
80 self.glr
81 }
82
83 #[must_use]
85 pub const fn has_tree_sitter(self) -> bool {
86 self.tree_sitter_standard || self.tree_sitter_c2rust
87 }
88}
89
90impl Display for ParserFeatureProfile {
91 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
92 let mut active = 0usize;
93
94 if self.pure_rust {
95 write!(f, "pure-rust")?;
96 active += 1;
97 }
98 if self.tree_sitter_standard {
99 if active > 0 {
100 write!(f, ", ")?;
101 }
102 write!(f, "tree-sitter-standard")?;
103 active += 1;
104 }
105 if self.tree_sitter_c2rust {
106 if active > 0 {
107 write!(f, ", ")?;
108 }
109 write!(f, "tree-sitter-c2rust")?;
110 active += 1;
111 }
112 if self.glr {
113 if active > 0 {
114 write!(f, ", ")?;
115 }
116 write!(f, "glr")?;
117 active += 1;
118 }
119
120 if active == 0 {
121 write!(f, "none")
122 } else {
123 Ok(())
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn profile_matches_cfg() {
134 let profile = ParserFeatureProfile::current();
135 assert_eq!(profile.pure_rust, cfg!(feature = "pure-rust"));
136 assert_eq!(
137 profile.tree_sitter_standard,
138 cfg!(feature = "tree-sitter-standard")
139 );
140 assert_eq!(
141 profile.tree_sitter_c2rust,
142 cfg!(feature = "tree-sitter-c2rust")
143 );
144 assert_eq!(profile.glr, cfg!(feature = "glr"));
145 }
146
147 #[test]
148 fn resolve_backend_glr_takes_priority() {
149 let profile = ParserFeatureProfile {
150 pure_rust: true,
151 tree_sitter_standard: true,
152 tree_sitter_c2rust: true,
153 glr: true,
154 };
155 assert_eq!(profile.resolve_backend(false), ParserBackend::GLR);
156 assert_eq!(profile.resolve_backend(true), ParserBackend::GLR);
157 }
158
159 #[test]
160 fn resolve_backend_pure_rust_without_conflicts() {
161 let profile = ParserFeatureProfile {
162 pure_rust: true,
163 tree_sitter_standard: false,
164 tree_sitter_c2rust: false,
165 glr: false,
166 };
167 assert_eq!(profile.resolve_backend(false), ParserBackend::PureRust);
168 }
169
170 #[test]
171 #[should_panic(expected = "GLR feature is not enabled")]
172 fn resolve_backend_pure_rust_with_conflicts_panics() {
173 let profile = ParserFeatureProfile {
174 pure_rust: true,
175 tree_sitter_standard: false,
176 tree_sitter_c2rust: false,
177 glr: false,
178 };
179 let _ = profile.resolve_backend(true);
180 }
181
182 #[test]
183 fn display_none_when_empty() {
184 let profile = ParserFeatureProfile {
185 pure_rust: false,
186 tree_sitter_standard: false,
187 tree_sitter_c2rust: false,
188 glr: false,
189 };
190 assert_eq!(profile.to_string(), "none");
191 }
192}