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_13;
116pub mod ruby_3_3_2;
117pub mod ruby_3_3_3;
118pub mod ruby_3_3_4;
119pub mod ruby_3_3_5;
120pub mod ruby_3_3_6;
121pub mod ruby_3_3_7;
122pub mod ruby_3_3_8;
123pub mod ruby_3_3_9;
124pub mod ruby_3_4_0;
125pub mod ruby_3_4_1;
126pub mod ruby_3_4_10;
127pub mod ruby_3_4_11;
128pub mod ruby_3_4_12;
129pub mod ruby_3_4_2;
130pub mod ruby_3_4_3;
131pub mod ruby_3_4_4;
132pub mod ruby_3_4_5;
133pub mod ruby_3_4_6;
134pub mod ruby_3_4_7;
135pub mod ruby_3_4_8;
136pub mod ruby_3_4_9;
137pub mod ruby_4_0_0;
138pub mod ruby_4_0_1;
139pub mod ruby_4_0_2;
140pub mod ruby_4_0_3;
141pub mod ruby_4_0_4;
142pub mod ruby_4_0_5;
143pub mod ruby_4_0_6;
144pub mod ruby_4_0_7;
145pub mod ruby_4_0_8;
146pub mod ruby_4_0_9;
147
148#[cfg(test)]
149mod tests {
150    use syn::{spanned::Spanned, visit::Visit};
151
152    #[derive(Default)]
153    struct BoolFields {
154        lines: Vec<usize>,
155    }
156
157    impl<'ast> Visit<'ast> for BoolFields {
158        fn visit_field(&mut self, field: &'ast syn::Field) {
159            if let syn::Type::Path(ty) = &field.ty {
160                if ty.qself.is_none() && ty.path.is_ident("bool") {
161                    self.lines.push(field.span().start().line);
162                }
163            }
164            syn::visit::visit_field(self, field);
165        }
166    }
167
168    fn bool_field_lines(source: &str) -> syn::Result<Vec<usize>> {
169        let syntax = syn::parse_file(source)?;
170        let mut fields = BoolFields::default();
171        fields.visit_file(&syntax);
172        Ok(fields.lines)
173    }
174
175    #[test]
176    fn detects_bool_fields_regardless_of_formatting() {
177        let source = concat!(
178            "struct Example {\n",
179            "\tpub\tr#type: bool ,\n",
180            "    pub Flag:\n",
181            "        /* comment */ bool,\n",
182            "}\n",
183            "union Either { pub enabled: bool, pub byte: u8 }\n",
184            "struct Tuple(pub bool);\n",
185        );
186        assert_eq!(bool_field_lines(source).unwrap(), vec![2, 3, 6, 7]);
187    }
188
189    #[test]
190    fn ignores_bool_parameters_returns_and_comments() {
191        let source = r#"
192// pub misleading: bool,
193struct Example {
194    pub enabled: u8,
195    pub callback: unsafe extern "C" fn(bool) -> bool,
196}
197impl Example {
198    pub fn set_bit(&mut self, val: bool) {}
199    pub fn enabled(&self) -> bool { true }
200}
201"#;
202        assert!(bool_field_lines(source).unwrap().is_empty());
203    }
204
205    #[test]
206    fn bindings_have_no_bool_fields() -> std::io::Result<()> {
207        let source = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
208        let mut violations = Vec::new();
209        let mut files_checked = 0;
210        for entry in std::fs::read_dir(source)? {
211            let path = entry?.path();
212            let name = path.file_name().unwrap().to_string_lossy();
213            if !name.starts_with("ruby_") || !name.ends_with(".rs") {
214                continue;
215            }
216            files_checked += 1;
217            let contents = std::fs::read_to_string(&path)?;
218            let lines = bool_field_lines(&contents)
219                .unwrap_or_else(|error| panic!("Failed to parse {}: {error}", path.display()));
220            for line in lines {
221                violations.push(format!("{}:{line}", path.display()));
222            }
223        }
224        assert!(files_checked > 0, "No Ruby binding files were checked");
225        violations.sort();
226        assert!(
227            violations.is_empty(),
228            "Ruby bindings must use u8 instead of bool fields so every byte value is valid. \
229             Regenerate with the current xtask or replace these fields with u8:\n{}",
230            violations.join("\n")
231        );
232        Ok(())
233    }
234}