Skip to content

Commit 96333a2

Browse files
committed
ctest: Add skips and mappings.
1 parent c7b8729 commit 96333a2

13 files changed

+187
-175
lines changed

ctest-next/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "ctest-next"
33
version = "0.1.0"
44
edition = "2021"
5-
rust-version = "1.77"
5+
rust-version = "1.87"
66
license = "MIT OR Apache-2.0"
77
repository = "https://github.com/rust-lang/libc"
88
publish = false

ctest-next/src/generator.rs

Lines changed: 160 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ use crate::{
1313
expand,
1414
ffi_items::FfiItems,
1515
template::{CTestTemplate, RustTestTemplate},
16-
Const, Field, Language, MapInput, Parameter, Result, Static, Struct, TyKind, Type,
17-
VolatileItemKind,
16+
Const, Field, MapInput, Parameter, Result, Static, Struct, Type, VolatileItemKind,
1817
};
1918

2019
/// A function that takes a mappable input and returns its mapping as Some, otherwise
@@ -35,14 +34,12 @@ pub struct TestGenerator {
3534
pub(crate) target: Option<String>,
3635
pub(crate) includes: Vec<PathBuf>,
3736
out_dir: Option<PathBuf>,
38-
/// The language chosen for testing bindings.
39-
pub language: Language,
4037
flags: Vec<String>,
4138
defines: Vec<(String, Option<String>)>,
4239
mapped_names: Vec<MappedName>,
4340
skips: Vec<Skip>,
4441
verbose_skip: bool,
45-
volatile_item: Option<VolatileItem>,
42+
volatile_items: Vec<VolatileItem>,
4643
array_arg: Option<ArrayArg>,
4744
}
4845

@@ -156,37 +153,116 @@ impl TestGenerator {
156153
self
157154
}
158155

159-
/// Is volatile?
156+
/// Is field volatile?
160157
///
161-
/// The closure given takes a `VolatileKind` denoting a particular item that
162-
/// could be volatile, and returns whether this is the case. This is used to
163-
/// make sure that the generated test code also has the volatile keyword.
158+
/// This is used to make sure that the generated test code also has the volatile keyword.
164159
///
165160
/// # Examples
166161
///
167162
/// ```no_run
168163
/// use ctest_next::{TestGenerator, VolatileItemKind};
169164
///
170165
/// let mut cfg = TestGenerator::new();
171-
/// cfg.volatile_item(|item| {
172-
/// match item {
173-
/// VolatileItemKind::StructField(s, f)
174-
/// if s.ident() == "foo_struct" && f.ident() == "foo_field"
175-
/// => true,
176-
/// _ => false,
177-
/// }});
166+
/// cfg.volatile_field(|s, f| {
167+
/// s.ident() == "foo_t" && f.ident() == "inner_t"
168+
/// });
169+
/// ```
170+
pub fn volatile_field(&mut self, f: impl Fn(Struct, Field) -> bool + 'static) -> &mut Self {
171+
self.volatile_items.push(Box::new(move |item| {
172+
if let VolatileItemKind::StructField(s, f_) = item {
173+
f(s, f_)
174+
} else {
175+
false
176+
}
177+
}));
178+
self
179+
}
180+
181+
/// Is static variable volatile?
182+
///
183+
/// This is used to make sure that the generated test code also has the volatile keyword.
184+
///
185+
/// # Examples
186+
///
187+
/// ```no_run
188+
/// use ctest_next::{TestGenerator, VolatileItemKind};
189+
///
190+
/// let mut cfg = TestGenerator::new();
191+
/// cfg.volatile_static(|s| {
192+
/// s.ident() == "foo_t"
193+
/// });
194+
/// ```
195+
pub fn volatile_static(&mut self, f: impl Fn(Static) -> bool + 'static) -> &mut Self {
196+
self.volatile_items.push(Box::new(move |item| {
197+
if let VolatileItemKind::Static(s) = item {
198+
f(s)
199+
} else {
200+
false
201+
}
202+
}));
203+
self
204+
}
205+
206+
/// Is function argument volatile?
207+
///
208+
/// This is used to make sure that the generated test code also has the volatile keyword.
209+
///
210+
/// # Examples
211+
///
212+
/// ```no_run
213+
/// use ctest_next::{TestGenerator, VolatileItemKind};
214+
///
215+
/// let mut cfg = TestGenerator::new();
216+
/// cfg.volatile_fn_arg(|f, _p| {
217+
/// f.ident() == "size_of_T"
218+
/// });
178219
/// ```
179-
pub fn volatile_item(&mut self, f: impl Fn(VolatileItemKind) -> bool + 'static) -> &mut Self {
180-
self.volatile_item = Some(Box::new(f));
220+
pub fn volatile_fn_arg(
221+
&mut self,
222+
f: impl Fn(crate::Fn, Box<Parameter>) -> bool + 'static,
223+
) -> &mut Self {
224+
self.volatile_items.push(Box::new(move |item| {
225+
if let VolatileItemKind::FnArgument(func, param) = item {
226+
f(func, param)
227+
} else {
228+
false
229+
}
230+
}));
231+
self
232+
}
233+
234+
/// Is function return type volatile?
235+
///
236+
/// This is used to make sure that the generated test code also has the volatile keyword.
237+
///
238+
/// # Examples
239+
///
240+
/// ```no_run
241+
/// use ctest_next::{TestGenerator, VolatileItemKind};
242+
///
243+
/// let mut cfg = TestGenerator::new();
244+
/// cfg.volatile_fn_return_type(|f| {
245+
/// f.ident() == "size_of_T"
246+
/// });
247+
/// ```
248+
pub fn volatile_fn_return_type(
249+
&mut self,
250+
f: impl Fn(crate::Fn) -> bool + 'static,
251+
) -> &mut Self {
252+
self.volatile_items.push(Box::new(move |item| {
253+
if let VolatileItemKind::FnReturnType(func) = item {
254+
f(func)
255+
} else {
256+
false
257+
}
258+
}));
181259
self
182260
}
183261

184262
/// Is argument of function an array?
185263
///
186-
/// The closure denotes whether particular argument of a function is an array.
187-
/// This is used to figure out which pointer argument is actually an array. For
188-
/// example, `uint8_t*` could be a pointer to an integer or pointer to the start
189-
/// of an array.
264+
/// This closure should return true if a pointer argument to a function should be generated
265+
/// with `T foo[]` syntax rather than `T *foo`.
190266
///
191267
/// # Examples
192268
///
@@ -343,27 +419,6 @@ impl TestGenerator {
343419
self
344420
}
345421

346-
/// Sets the programming language.
347-
///
348-
/// This is used to generate C++ versions of the test that can be compiled
349-
/// and ran in the same way as C.
350-
///
351-
/// # Examples
352-
///
353-
/// ```no_run
354-
/// use std::env;
355-
/// use std::path::PathBuf;
356-
///
357-
/// use ctest_next::{TestGenerator, Language};
358-
///
359-
/// let mut cfg = TestGenerator::new();
360-
/// cfg.language(Language::CXX);
361-
/// ```
362-
pub fn language(&mut self, language: Language) -> &mut Self {
363-
self.language = language;
364-
self
365-
}
366-
367422
/// Add a flag to the C compiler invocation.
368423
///
369424
/// # Examples
@@ -466,24 +521,63 @@ impl TestGenerator {
466521
/// # Examples
467522
///
468523
/// ```no_run
469-
/// use ctest_next::{TestGenerator, TyKind};
524+
/// use ctest_next::TestGenerator;
470525
///
471526
/// let mut cfg = TestGenerator::new();
472-
/// cfg.rename_type(|ty, kind| {
473-
/// if kind == TyKind::Struct {
474-
/// Some(format!("{}_t", ty))
475-
/// } else {
476-
/// None
477-
/// }
527+
/// cfg.rename_type(|ty| {
528+
/// Some(format!("{}_t", ty))
478529
/// });
479530
/// ```
480-
pub fn rename_type(
481-
&mut self,
482-
f: impl Fn(&str, TyKind) -> Option<String> + 'static,
483-
) -> &mut Self {
531+
pub fn rename_type(&mut self, f: impl Fn(&str) -> Option<String> + 'static) -> &mut Self {
532+
self.mapped_names.push(Box::new(move |item| {
533+
if let MapInput::Type(ty) = item {
534+
f(ty)
535+
} else {
536+
None
537+
}
538+
}));
539+
self
540+
}
541+
542+
/// Configures how a Rust struct type is translated to a C struct type.
543+
///
544+
/// # Examples
545+
///
546+
/// ```no_run
547+
/// use ctest_next::TestGenerator;
548+
///
549+
/// let mut cfg = TestGenerator::new();
550+
/// cfg.rename_struct_ty(|ty| {
551+
/// (ty == "timeval").then(|| format!("{ty}_t"))
552+
/// });
553+
/// ```
554+
pub fn rename_struct_ty(&mut self, f: impl Fn(&str) -> Option<String> + 'static) -> &mut Self {
484555
self.mapped_names.push(Box::new(move |item| {
485-
if let MapInput::Type(ty, kind) = item {
486-
f(ty, *kind)
556+
if let MapInput::StructType(ty) = item {
557+
f(ty)
558+
} else {
559+
None
560+
}
561+
}));
562+
self
563+
}
564+
565+
/// Configures how a Rust union type is translated to a C union type.
566+
///
567+
/// # Examples
568+
///
569+
/// ```no_run
570+
/// use ctest_next::TestGenerator;
571+
///
572+
/// let mut cfg = TestGenerator::new();
573+
/// cfg.rename_struct_ty(|ty| {
574+
/// (ty == "T1Union").then(|| format!("__{ty}"))
575+
/// });
576+
/// ```
577+
pub fn rename_union_ty(&mut self, f: impl Fn(&str) -> Option<String> + 'static) -> &mut Self {
578+
self.mapped_names.push(Box::new(move |item| {
579+
if let MapInput::UnionType(ty) = item {
580+
f(ty)
487581
} else {
488582
None
489583
}
@@ -531,18 +625,13 @@ impl TestGenerator {
531625
.map_err(GenerationError::OsError)?;
532626

533627
// Generate the C/Cxx side of the tests.
534-
let c_output_path = output_file_path.with_extension(self.language.extension());
628+
let c_output_path = output_file_path.with_extension("c");
535629
File::create(&c_output_path)
536630
.map_err(GenerationError::OsError)?
537631
.write_all(
538632
CTestTemplate::new(&ffi_items, self)
539633
.render()
540-
.map_err(|e| {
541-
GenerationError::TemplateRender(
542-
self.language.display_name().to_string(),
543-
e.to_string(),
544-
)
545-
})?
634+
.map_err(|e| GenerationError::TemplateRender("C".to_string(), e.to_string()))?
546635
.as_bytes(),
547636
)
548637
.map_err(GenerationError::OsError)?;
@@ -557,11 +646,12 @@ impl TestGenerator {
557646

558647
macro_rules! filter {
559648
($field:ident, $variant:ident, $label:literal) => {{
560-
let (retained, skipped): (Vec<_>, Vec<_>) = ffi_items
649+
let skipped: Vec<_> = ffi_items
561650
.$field
562-
.drain(..)
563-
.partition(|item| !self.skips.iter().any(|f| f(&MapInput::$variant(item))));
564-
ffi_items.$field = retained;
651+
.extract_if(.., |item| {
652+
self.skips.iter().any(|f| f(&MapInput::$variant(item)))
653+
})
654+
.collect();
565655
if verbose {
566656
skipped
567657
.iter()
@@ -590,9 +680,9 @@ impl TestGenerator {
590680
MapInput::Struct(s) => s.ident().to_string(),
591681
MapInput::Alias(t) => t.ident().to_string(),
592682
MapInput::Field(_, f) => f.ident().to_string(),
593-
MapInput::Type(ty, TyKind::Struct) => format!("struct {ty}"),
594-
MapInput::Type(ty, TyKind::Union) => format!("union {ty}"),
595-
MapInput::Type(ty, TyKind::Other) => ty.to_string(),
683+
MapInput::StructType(ty) => format!("struct {ty}"),
684+
MapInput::UnionType(ty) => format!("union {ty}"),
685+
MapInput::Type(ty) => ty.to_string(),
596686
})
597687
}
598688
}

0 commit comments

Comments
 (0)