Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions ctest-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ fn test_ctest() {
.header("t1.h")
.include("src")
.skip_private(true)
.rename_fn(|f| f.link_name().unwrap_or(f.ident()).to_string().into())
.rename_static(|s| s.link_name().unwrap_or(s.ident()).to_string().into())
.rename_fn(|f| f.link_name().unwrap_or(f.path()).to_string().into())
.rename_static(|s| s.link_name().unwrap_or(s.path()).to_string().into())
.rename_union_ty(|ty| (ty == "T1Union").then_some(ty.to_string()))
.rename_struct_ty(|ty| (ty == "Transparent").then_some(ty.to_string()))
.volatile_struct_field(|s, f| s.ident() == "V" && f.ident() == "v")
.volatile_static(|s| s.ident() == "vol_ptr")
.volatile_static(|s| s.ident() == "T1_fn_ptr_vol")
.volatile_fn_arg(|f, p| f.ident() == "T1_vol0" && p.ident() == "arg0")
.volatile_fn_arg(|f, p| f.ident() == "T1_vol2" && p.ident() == "arg1")
.volatile_fn_return_type(|f| f.ident() == "T1_vol1")
.volatile_fn_return_type(|f| f.ident() == "T1_vol2")
.volatile_struct_field(|s, f| s.path() == "V" && f.ident() == "v")
.volatile_static(|s| s.path() == "vol_ptr")
.volatile_static(|s| s.path() == "T1_fn_ptr_vol")
.volatile_fn_arg(|f, p| f.path() == "T1_vol0" && p.ident() == "arg0")
.volatile_fn_arg(|f, p| f.path() == "T1_vol2" && p.ident() == "arg1")
.volatile_fn_return_type(|f| f.path() == "T1_vol1")
.volatile_fn_return_type(|f| f.path() == "T1_vol2")
// The parameter `a` of the functions `T1r`, `T1s`, `T1t`, `T1v` is an array.
.array_arg(|f, p| matches!(f.ident(), "T1r" | "T1s" | "T1t" | "T1v") && p.ident() == "a")
.array_arg(|f, p| matches!(f.path(), "T1r" | "T1s" | "T1t" | "T1v") && p.ident() == "a")
.skip_roundtrip(|n| n == "Arr");
ctest::generate_test(&mut t1gen, "src/t1.rs", "t1gen.rs").unwrap();

Expand Down
19 changes: 17 additions & 2 deletions ctest/src/ast/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,27 @@ use crate::BoxStr;
pub struct Const {
pub(crate) public: bool,
pub(crate) ident: BoxStr,
pub(crate) path: syn::Path,
pub(crate) ty: syn::Type,
}

impl Const {
/// Return the identifier of the constant as a string.
pub fn ident(&self) -> &str {
/// Return the full path to the constant variable as a string.
///
/// If inside a nested module, this will return a top-level-relative path,
/// but not a crate-relative path. For some item `foo` in module
/// `crate::bar`, the returned string will be `bar::foo`, and not
/// `crate::bar::foo`.
pub fn path(&self) -> &str {
&self.ident
}

/// Returns the last path of the identifier, from the absolute path returned
/// by [`Const::path`].
pub fn ident(&self) -> String {
let Some(syn::PathSegment { ident, .. }) = self.path.segments.last() else {
unreachable!("all parsed items have at least one element in their path")
};
ident.to_string()
}
}
19 changes: 17 additions & 2 deletions ctest/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct Fn {
#[expect(unused)]
pub(crate) abi: Abi,
pub(crate) ident: BoxStr,
pub(crate) path: syn::Path,
pub(crate) link_name: Option<BoxStr>,
#[expect(unused)]
pub(crate) parameters: Vec<Parameter>,
Expand All @@ -21,11 +22,25 @@ pub struct Fn {
}

impl Fn {
/// Return the identifier of the function as a string.
pub fn ident(&self) -> &str {
/// Return the full path to the function item as a string.
///
/// If inside a nested module, this will return a top-level-relative path,
/// but not a crate-relative path. For some item `foo` in module
/// `crate::bar`, the returned string will be `bar::foo`, and not
/// `crate::bar::foo`.
pub fn path(&self) -> &str {
&self.ident
}

/// Returns the last path of the identifier, from the absolute path returned
/// by [`Fn::path`].
pub fn ident(&self) -> String {
let Some(syn::PathSegment { ident, .. }) = self.path.segments.last() else {
unreachable!("all parsed items have at least one element in their path")
};
ident.to_string()
}

/// Return the name of the function to be linked C side with.
pub fn link_name(&self) -> Option<&str> {
self.link_name.as_deref()
Expand Down
20 changes: 20 additions & 0 deletions ctest/src/ast/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod constant;
mod field;
mod function;
mod module;
mod parameter;
mod static_variable;
mod structure;
Expand All @@ -12,12 +13,31 @@ use std::fmt;
pub use constant::Const;
pub use field::Field;
pub use function::Fn;
pub use module::Module;
pub use parameter::Parameter;
pub use static_variable::Static;
pub use structure::Struct;
pub use type_alias::Type;
pub use union::Union;

use crate::MapInput;

/// Transforms an item's absolute path to use `_` as path separator instead of
/// `::`.
pub(crate) fn escape_item_path<'a>(item: impl Into<MapInput<'a>>) -> String {
let path = match item.into() {
MapInput::Struct(s) => s.path(),
MapInput::Union(u) => u.path(),
MapInput::Fn(f) => f.path(),
MapInput::Alias(a) => a.path(),
MapInput::Const(c) => c.path(),
MapInput::Static(s) => s.path(),

_ => unimplemented!("other MapInput data constructors do not represent rust items"),
};
path.replace("::", "_")
}

/// The ABI as defined by the extern block.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Abi {
Expand Down
33 changes: 33 additions & 0 deletions ctest/src/ast/module.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::BoxStr;
use crate::ffi_items::FfiItems;

/// Represents a Rust module. `ctest` only considers items for which there is a
/// corresponding type in this crate.
#[derive(Debug, Clone)]
pub struct Module {
pub(crate) public: bool,
pub(crate) ident: BoxStr,
pub(crate) path: syn::Path,
pub(crate) items: FfiItems,
}

impl Module {
/// Returns the full path to the module item.
///
/// If inside a nested module, this will return a top-level-relative path,
/// but not a crate-relative path. For some item `foo` in module
/// `crate::bar`, the returned string will be `bar::foo`, and not
/// `crate::bar::foo`.
pub fn path(&self) -> &str {
&self.ident
}

/// Returns the last path of the identifier, from the absolute path returned
/// by [`Module::path`].
pub fn ident(&self) -> String {
let Some(syn::PathSegment { ident, .. }) = self.path.segments.last() else {
unreachable!("all parsed items have at least one element in their path")
};
ident.to_string()
}
}
19 changes: 17 additions & 2 deletions ctest/src/ast/static_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,31 @@ pub struct Static {
#[expect(unused)]
pub(crate) abi: Abi,
pub(crate) ident: BoxStr,
pub(crate) path: syn::Path,
pub(crate) link_name: Option<BoxStr>,
pub(crate) ty: syn::Type,
}

impl Static {
/// Return the identifier of the static variable as a string.
pub fn ident(&self) -> &str {
/// Return the full path to the static variable as a string.
///
/// If inside a nested module, this will return a top-level-relative path,
/// but not a crate-relative path. For some item `foo` in module
/// `crate::bar`, the returned string will be `bar::foo`, and not
/// `crate::bar::foo`.
pub fn path(&self) -> &str {
&self.ident
}

/// Returns the last path of the identifier, from the absolute path returned
/// by [`Static::path`].
pub fn ident(&self) -> String {
let Some(syn::PathSegment { ident, .. }) = self.path.segments.last() else {
unreachable!("all parsed items have at least one element in their path")
};
ident.to_string()
}

/// Return the name of the function to be linked C side with.
pub fn link_name(&self) -> Option<&str> {
self.link_name.as_deref()
Expand Down
19 changes: 17 additions & 2 deletions ctest/src/ast/structure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,27 @@ use crate::{
pub struct Struct {
pub(crate) public: bool,
pub(crate) ident: BoxStr,
pub(crate) path: syn::Path,
pub(crate) fields: Vec<Field>,
}

impl Struct {
/// Return the identifier of the struct as a string.
pub fn ident(&self) -> &str {
/// Return the full path to the struct item as a string.
///
/// If inside a nested module, this will return a top-level-relative path,
/// but not a crate-relative path. For some item `foo` in module
/// `crate::bar`, the returned string will be `bar::foo`, and not
/// `crate::bar::foo`.
pub fn path(&self) -> &str {
&self.ident
}

/// Returns the last path of the identifier, from the absolute path returned
/// by [`Struct::path`].
pub fn ident(&self) -> String {
let Some(syn::PathSegment { ident, .. }) = self.path.segments.last() else {
unreachable!("all parsed items have at least one element in their path")
};
ident.to_string()
}
}
19 changes: 17 additions & 2 deletions ctest/src/ast/type_alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,27 @@ use crate::BoxStr;
pub struct Type {
pub(crate) public: bool,
pub(crate) ident: BoxStr,
pub(crate) path: syn::Path,
pub(crate) ty: syn::Type,
}

impl Type {
/// Return the identifier of the type alias as a string.
pub fn ident(&self) -> &str {
/// Return the full path to the type alias as a string.
///
/// If inside a nested module, this will return a top-level-relative path,
/// but not a crate-relative path. For some item `foo` in module
/// `crate::bar`, the returned string will be `bar::foo`, and not
/// `crate::bar::foo`.
pub fn path(&self) -> &str {
&self.ident
}

/// Returns the last path of the identifier, from the absolute path returned
/// by [`Type::path`].
pub fn ident(&self) -> String {
let Some(syn::PathSegment { ident, .. }) = self.path.segments.last() else {
unreachable!("all parsed items have at least one element in their path")
};
ident.to_string()
}
}
19 changes: 17 additions & 2 deletions ctest/src/ast/union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,27 @@ use crate::{
pub struct Union {
pub(crate) public: bool,
pub(crate) ident: BoxStr,
pub(crate) path: syn::Path,
pub(crate) fields: Vec<Field>,
}

impl Union {
/// Return the identifier of the union as a string.
pub fn ident(&self) -> &str {
/// Return the full path to the union item as a string.
///
/// If inside a nested module, this will return a top-level-relative path,
/// but not a crate-relative path. For some item `foo` in module
/// `crate::bar`, the returned string will be `bar::foo`, and not
/// `crate::bar::foo`.
pub fn path(&self) -> &str {
&self.ident
}

/// Returns the last path of the identifier, from the absolute path returned
/// by [`Union::path`].
pub fn ident(&self) -> String {
let Some(syn::PathSegment { ident, .. }) = self.path.segments.last() else {
unreachable!("all parsed items have at least one element in their path")
};
ident.to_string()
}
}
Loading
Loading