Skip to main content

rbspy_ruby_structs/
lib.rs

1#![allow(deref_nullptr)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(non_upper_case_globals)]
5#![allow(unnecessary_transmutes)]
6pub mod ruby_1_9_1_0;
7pub mod ruby_1_9_2_0;
8pub mod ruby_1_9_3_0;
9pub mod ruby_2_0_0_0;
10pub mod ruby_2_1_0;
11pub mod ruby_2_1_1;
12pub mod ruby_2_1_10;
13pub mod ruby_2_1_2;
14pub mod ruby_2_1_3;
15pub mod ruby_2_1_4;
16pub mod ruby_2_1_5;
17pub mod ruby_2_1_6;
18pub mod ruby_2_1_7;
19pub mod ruby_2_1_8;
20pub mod ruby_2_1_9;
21pub mod ruby_2_2_0;
22pub mod ruby_2_2_1;
23pub mod ruby_2_2_10;
24pub mod ruby_2_2_2;
25pub mod ruby_2_2_3;
26pub mod ruby_2_2_4;
27pub mod ruby_2_2_5;
28pub mod ruby_2_2_6;
29pub mod ruby_2_2_7;
30pub mod ruby_2_2_8;
31pub mod ruby_2_2_9;
32pub mod ruby_2_3_0;
33pub mod ruby_2_3_1;
34pub mod ruby_2_3_2;
35pub mod ruby_2_3_3;
36pub mod ruby_2_3_4;
37pub mod ruby_2_3_5;
38pub mod ruby_2_3_6;
39pub mod ruby_2_3_7;
40pub mod ruby_2_3_8;
41pub mod ruby_2_4_0;
42pub mod ruby_2_4_1;
43pub mod ruby_2_4_10;
44pub mod ruby_2_4_2;
45pub mod ruby_2_4_3;
46pub mod ruby_2_4_4;
47pub mod ruby_2_4_5;
48pub mod ruby_2_4_6;
49pub mod ruby_2_4_7;
50pub mod ruby_2_4_8;
51pub mod ruby_2_4_9;
52pub mod ruby_2_5_0;
53pub mod ruby_2_5_1;
54pub mod ruby_2_5_2;
55pub mod ruby_2_5_3;
56pub mod ruby_2_5_4;
57pub mod ruby_2_5_5;
58pub mod ruby_2_5_6;
59pub mod ruby_2_5_7;
60pub mod ruby_2_5_8;
61pub mod ruby_2_5_9;
62pub mod ruby_2_6_0;
63pub mod ruby_2_6_1;
64pub mod ruby_2_6_10;
65pub mod ruby_2_6_2;
66pub mod ruby_2_6_3;
67pub mod ruby_2_6_4;
68pub mod ruby_2_6_5;
69pub mod ruby_2_6_6;
70pub mod ruby_2_6_7;
71pub mod ruby_2_6_8;
72pub mod ruby_2_6_9;
73pub mod ruby_2_7_0;
74pub mod ruby_2_7_1;
75pub mod ruby_2_7_2;
76pub mod ruby_2_7_3;
77pub mod ruby_2_7_4;
78pub mod ruby_2_7_5;
79pub mod ruby_2_7_6;
80pub mod ruby_2_7_7;
81pub mod ruby_2_7_8;
82pub mod ruby_3_0_0;
83pub mod ruby_3_0_1;
84pub mod ruby_3_0_2;
85pub mod ruby_3_0_3;
86pub mod ruby_3_0_4;
87pub mod ruby_3_0_5;
88pub mod ruby_3_0_6;
89pub mod ruby_3_0_7;
90pub mod ruby_3_1_0;
91pub mod ruby_3_1_1;
92pub mod ruby_3_1_2;
93pub mod ruby_3_1_3;
94pub mod ruby_3_1_4;
95pub mod ruby_3_1_5;
96pub mod ruby_3_1_6;
97pub mod ruby_3_1_7;
98pub mod ruby_3_2_0;
99pub mod ruby_3_2_1;
100pub mod ruby_3_2_10;
101pub mod ruby_3_2_11;
102pub mod ruby_3_2_2;
103pub mod ruby_3_2_3;
104pub mod ruby_3_2_4;
105pub mod ruby_3_2_5;
106pub mod ruby_3_2_6;
107pub mod ruby_3_2_7;
108pub mod ruby_3_2_8;
109pub mod ruby_3_2_9;
110pub mod ruby_3_3_0;
111pub mod ruby_3_3_1;
112pub mod ruby_3_3_10;
113pub mod ruby_3_3_11;
114pub mod ruby_3_3_12;
115pub mod ruby_3_3_2;
116pub mod ruby_3_3_3;
117pub mod ruby_3_3_4;
118pub mod ruby_3_3_5;
119pub mod ruby_3_3_6;
120pub mod ruby_3_3_7;
121pub mod ruby_3_3_8;
122pub mod ruby_3_3_9;
123pub mod ruby_3_4_0;
124pub mod ruby_3_4_1;
125pub mod ruby_3_4_10;
126pub mod ruby_3_4_11;
127pub mod ruby_3_4_2;
128pub mod ruby_3_4_3;
129pub mod ruby_3_4_4;
130pub mod ruby_3_4_5;
131pub mod ruby_3_4_6;
132pub mod ruby_3_4_7;
133pub mod ruby_3_4_8;
134pub mod ruby_3_4_9;
135pub mod ruby_4_0_0;
136pub mod ruby_4_0_1;
137pub mod ruby_4_0_2;
138pub mod ruby_4_0_3;
139pub mod ruby_4_0_4;
140pub mod ruby_4_0_5;
141pub mod ruby_4_0_6;
142pub mod ruby_4_0_7;
143pub mod ruby_4_0_8;
144
145#[cfg(test)]
146mod tests {
147    use syn::{spanned::Spanned, visit::Visit};
148
149    #[derive(Default)]
150    struct BoolFields {
151        lines: Vec<usize>,
152    }
153
154    impl<'ast> Visit<'ast> for BoolFields {
155        fn visit_field(&mut self, field: &'ast syn::Field) {
156            if let syn::Type::Path(ty) = &field.ty {
157                if ty.qself.is_none() && ty.path.is_ident("bool") {
158                    self.lines.push(field.span().start().line);
159                }
160            }
161            syn::visit::visit_field(self, field);
162        }
163    }
164
165    fn bool_field_lines(source: &str) -> syn::Result<Vec<usize>> {
166        let syntax = syn::parse_file(source)?;
167        let mut fields = BoolFields::default();
168        fields.visit_file(&syntax);
169        Ok(fields.lines)
170    }
171
172    #[test]
173    fn detects_bool_fields_regardless_of_formatting() {
174        let source = concat!(
175            "struct Example {\n",
176            "\tpub\tr#type: bool ,\n",
177            "    pub Flag:\n",
178            "        /* comment */ bool,\n",
179            "}\n",
180            "union Either { pub enabled: bool, pub byte: u8 }\n",
181            "struct Tuple(pub bool);\n",
182        );
183        assert_eq!(bool_field_lines(source).unwrap(), vec![2, 3, 6, 7]);
184    }
185
186    #[test]
187    fn ignores_bool_parameters_returns_and_comments() {
188        let source = r#"
189// pub misleading: bool,
190struct Example {
191    pub enabled: u8,
192    pub callback: unsafe extern "C" fn(bool) -> bool,
193}
194impl Example {
195    pub fn set_bit(&mut self, val: bool) {}
196    pub fn enabled(&self) -> bool { true }
197}
198"#;
199        assert!(bool_field_lines(source).unwrap().is_empty());
200    }
201
202    #[test]
203    fn bindings_have_no_bool_fields() -> std::io::Result<()> {
204        let source = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
205        let mut violations = Vec::new();
206        let mut files_checked = 0;
207        for entry in std::fs::read_dir(source)? {
208            let path = entry?.path();
209            let name = path.file_name().unwrap().to_string_lossy();
210            if !name.starts_with("ruby_") || !name.ends_with(".rs") {
211                continue;
212            }
213            files_checked += 1;
214            let contents = std::fs::read_to_string(&path)?;
215            let lines = bool_field_lines(&contents)
216                .unwrap_or_else(|error| panic!("Failed to parse {}: {error}", path.display()));
217            for line in lines {
218                violations.push(format!("{}:{line}", path.display()));
219            }
220        }
221        assert!(files_checked > 0, "No Ruby binding files were checked");
222        violations.sort();
223        assert!(
224            violations.is_empty(),
225            "Ruby bindings must use u8 instead of bool fields so every byte value is valid. \
226             Regenerate with the current xtask or replace these fields with u8:\n{}",
227            violations.join("\n")
228        );
229        Ok(())
230    }
231}