bash_loadable/ffi/
word.rs1use std::{ffi::{CStr, c_char, c_int}, mem::transmute, ptr::NonNull};
2use paste::paste;
4
5#[repr(transparent)]
38#[derive(Clone, Copy, PartialEq, Eq, Hash)]
39pub struct FFIWordFlags(c_int);
40
41impl FFIWordFlags {
42 pub const NONE: Self = Self(0);
43
44 #[must_use]
45 #[inline(always)]
46 pub const fn has_flags(self, flags: Self) -> bool {
47 self.0 & flags.0 == flags.0
48 }
49
50 #[inline(always)]
51 pub const fn add_flags(&mut self, flags: Self) {
52 self.0 |= flags.0;
53 }
54
55 #[inline(always)]
56 pub const fn remove_flags(&mut self, flags: Self) {
57 self.0 &= !flags.0;
58 }
59
60 #[inline(always)]
61 pub const fn toggle_flags(&mut self, flags: Self) {
62 self.0 ^= flags.0;
63 }
64
65 #[must_use]
66 #[inline(always)]
67 pub const fn with_flags(mut self, flags: Self) -> Self {
68 self.add_flags(flags);
69 self
70 }
71
72 #[must_use]
73 #[inline(always)]
74 pub const fn without_flags(mut self, flags: Self) -> Self {
75 self.remove_flags(flags);
76 self
77 }
78
79 #[must_use]
80 #[inline]
81 pub const fn count_ones(self) -> u32 {
82 self.0.count_ones()
83 }
84
85 #[must_use]
86 #[inline]
87 pub const fn pop_bottom_index(&mut self) -> Option<u32> {
88 if self.0 == 0 {
89 return None;
90 }
91 let mut mask = self.0.cast_unsigned();
92 let next_bit = mask.trailing_zeros();
93 mask ^= 1 << next_bit;
94 self.0 = mask.cast_signed();
95 Some(next_bit)
96 }
97
98 #[must_use]
99 #[inline]
100 pub fn get_flags(self) -> Box<[(Self, &'static str)]> {
101 self.collect()
102 }
103}
104
105impl Iterator for FFIWordFlags {
106 type Item = (FFIWordFlags, &'static str);
107 fn size_hint(&self) -> (usize, Option<usize>) {
108 (self.count_ones() as usize, Some(self.count_ones() as usize))
109 }
110
111 fn next(&mut self) -> Option<Self::Item> {
112 let index = self.pop_bottom_index()?;
113 let index = index as usize;
114 Some((Self::ALL_FLAGS[index], Self::FLAG_NAMES[index]))
115 }
116}
117
118macro_rules! define_word_flags {
119 (
120 $(
121 #[$doc_meta:meta]
122 $const_name:ident $func_name:ident = $value:expr
123 ),*
124 $(,)
125 ?) => {
126 paste!{
127 impl FFIWordFlags {
128 pub const ALL: Self = {
129 let mut builder = Self::NONE;
130 $(
131 builder.add_flags(Self::$const_name);
132 )*
133 builder
134 };
135 pub const ALL_FLAGS: &'static [Self] = &[
136 $(
137 Self::$const_name,
138 )*
139 ];
140 pub const FLAG_NAMES: &'static [&'static str] = &[
141 $(
142 stringify!($func_name),
143 )*
144 ];
145 $(
146 #[$doc_meta]
147 pub const $const_name: Self = Self($value);
148
149 #[must_use]
150 #[inline(always)]
151 pub const fn [<get_ $func_name>](self) -> bool {
152 self.has_flags(Self::$const_name)
153 }
154
155 #[inline(always)]
156 pub const fn [<add_ $func_name>](&mut self) {
157 self.add_flags(Self::$const_name);
158 }
159
160 #[inline(always)]
161 pub const fn [<remove_ $func_name>](&mut self) {
162 self.remove_flags(Self::$const_name);
163 }
164
165 #[inline]
166 pub const fn [<set_ $func_name>](&mut self, on: bool) {
167 if on {
168 self.add_flags(Self::$const_name);
169 } else {
170 self.remove_flags(Self::$const_name);
171 }
172 }
173
174 #[inline(always)]
175 pub const fn [<toggle_ $func_name>](&mut self) {
176 self.0 ^= Self::$const_name.0;
177 }
178
179 #[must_use]
180 #[inline(always)]
181 pub const fn [<with_ $func_name>](self) -> Self {
182 self.with_flags(Self::$const_name)
183 }
184
185 #[must_use]
186 #[inline(always)]
187 pub const fn [<without_ $func_name>](self) -> Self {
188 self.without_flags(Self::$const_name)
189 }
190 )*
191 }
192 }
193 };
194}
195
196define_word_flags!(
197 HAS_DOLLAR
199 has_dollar = 1 << 0,
200 QUOTED
202 quoted = 1 << 1,
203 ASSIGNMENT
205 assignment = 1 << 2,
206 SPLIT_SPACE
208 split_space = 1 << 3,
209 NO_SPLIT
211 no_split = 1 << 4,
212 NO_GLOB
214 no_glob = 1 << 5,
215 NO_SPLIT2
217 no_split2 = 1 << 6,
218 TILDE_EXP
220 tilde_exp = 1 << 7,
221 DOLLAR_AT
223 dollar_at = 1 << 8,
224 ARRAY_REF
226 array_ref = 1 << 9,
227 NO_COMMAND_SUBSTITUTION
229 no_command_substitution = 1 << 10,
230 ASSIGN_RHS
232 assign_rhs = 1 << 11,
233 NO_TILDE
235 no_tilde = 1 << 12,
236 NO_ASSIGN_TILDE
238 no_assign_tilde = 1 << 13,
239 EXPAND_RHS
241 expand_rhs = 1 << 14,
242 COMPOUND_ASSIGNMENT
244 compound_assignment = 1 << 15,
245 ASSIGN_BUILTIN
247 assign_builtin = 1 << 16,
248 ASSIGN_ARG
250 assign_arg = 1 << 17,
251 HAS_QUOTED_NULL
253 has_quoted_null = 1 << 18,
254 DOUBLE_QUOTE
256 double_quote = 1 << 19,
257 NO_PROCESS_SUBSTITUTION
259 no_process_substitution = 1 << 20,
260 SAW_QUOTED_NULL
262 saw_quoted_null = 1 << 21,
263 ASSIGN_ASSOC
265 assign_assoc = 1 << 22,
266 ASSIGN_ARRAY
268 assign_array = 1 << 23,
269 ARRAY_INDEX
271 array_index = 1 << 24,
272 ASSIGN_GLOBAL
274 assign_global = 1 << 25,
275 NO_BRACE
277 no_brace = 1 << 26,
278 COMPLETION
280 completion = 1 << 27,
281 CHECK_LOCAL
283 check_local = 1 << 28,
284 FORCE_LOCAL
286 force_local = 1 << 29
287);
288
289#[repr(C)]
290#[derive(Clone, Copy)]
291pub struct FFIWord {
292 pub word: *const c_char,
293 pub flags: FFIWordFlags,
294}
295
296impl FFIWord {
297 pub const EMTPY: Self = Self { word: c"".as_ptr(), flags: FFIWordFlags::NONE };
298}
299
300#[repr(transparent)]
301#[derive(Clone, Copy)]
302pub struct WordRef(Option<NonNull<FFIWord>>);
303
304impl WordRef {
305 #[must_use]
306 #[inline(always)]
307 pub const fn get(&self) -> Option<&FFIWord> {
308 unsafe { transmute(self.0) }
309 }
310
311 #[must_use]
312 #[inline]
313 pub fn to_str(&self) -> Option<&str> {
314 if let Some(word) = self.get() {
315 if word.word.is_null() {
316 return None;
317 }
318 let cstr = unsafe { CStr::from_ptr(word.word) };
319 let len = cstr.count_bytes();
320 Some(unsafe { transmute(std::slice::from_raw_parts(word.word.cast::<u8>(), len)) })
321 } else {
322 None
323 }
324 }
325}
326
327impl std::fmt::Display for WordRef {
328 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329 if let Some(s) = self.to_str() {
330 write!(f, "{s}")
331 } else {
332 Ok(())
333 }
334 }
335}
336
337#[repr(C)]
338#[derive(Clone, Copy)]
339pub struct FFIWordList {
340 pub next: WordListRef,
341 pub word: WordRef,
342}
343
344impl FFIWordList {
345 const NULL: Self = Self { next: WordListRef(None), word: WordRef(None) };
346
347 #[must_use]
348 #[inline]
349 pub const fn next(&self) -> Option<&FFIWordList> {
350 self.next.get()
351 }
352
353 #[must_use]
354 #[inline]
355 pub const fn word(&self) -> Option<&FFIWord> {
356 self.word.get()
357 }
358
359 #[must_use]
360 #[inline]
361 pub fn word_str(&self) -> Option<&str> {
362 self.word.to_str()
363 }
364}
365
366#[repr(transparent)]
367#[derive(Clone, Copy)]
368pub struct WordListRef(Option<NonNull<FFIWordList>>);
369
370impl WordListRef {
371 #[must_use]
372 #[inline(always)]
373 pub const fn get(&self) -> Option<&FFIWordList> {
374 unsafe { transmute(self.0) }
375 }
376
377 #[must_use]
378 #[inline]
379 pub const fn as_ref(&self) -> &FFIWordList {
380 if let Some(inner) = self.get() {
381 inner
382 } else {
383 &FFIWordList::NULL
384 }
385 }
386}