Hi,
I'm writing a macro that would generate different structs, in order to create wrappers around C linked lists.
I wrote a first macro that is working well.
Here's the code invoking the macro :
linked_list_iter!(
sys, // namespace
libvlc_module_description, // C raw type
ModuleDescription, // Rust base struct name
{
name: (String, str),
shortname: (String, str),
longname: (String, str),
help: (String, str),
} // fields
);
And here's the working first version :
macro_rules! linked_list_iter {
(
$namespace:path,
$c_type: ident,
$name: ident,
{
$($(#[$field_meta:meta])*
$field_vis:vis $field_name:ident: ($field_type:ty, $field_b_type:ty)),* $(,)+
}
) => {
paste::item! {
use std::marker::PhantomData;
use crate::$namespace::[<$c_type _t>];
use crate::$namespace::[<$c_type _list_release>];
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct $name {
$(
$(#[$field_meta:meta])*
$field_vis $field_name : Option<$field_type>,
)*
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct [<$name Ref>]<'a> {
$(
$(#[$field_meta:meta])*
$field_vis $field_name : Option<Cow<'a, $field_b_type>>,
)*
}
impl<'a> [<$name Ref>]<'a> {
/// Convert to owned strings.
pub fn into_owned(&'a self) -> $name {
$name {
$($field_name: self.$field_name.as_ref().map(|s| s.clone().into_owned()),)*
}
}
}
pub struct [<$name ListIter>]<'a> {
ptr: *mut [<$c_type _t>],
_phantomdata: PhantomData<&'a [<$c_type _t>]>,
}
impl<'a> Iterator for [<$name ListIter>]<'a> {
type Item = [<$name Ref>]<'a>;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if self.ptr.is_null() {
return None;
}
let p = self.ptr;
self.ptr = (*p).p_next;
Some([<$name Ref>] {
$($field_name: from_cstr_ref((*p).[<psz_ $field_name>]),)*
})
}
}
}
pub struct [<$name List>] {
ptr: *mut [<$c_type _t>]
}
impl [<$name List>] {
/// Returns raw pointer
pub fn raw(&self) -> *mut [<$c_type _t>] {
self.ptr
}
}
impl Drop for [<$name List>] {
fn drop(&mut self) {
unsafe{ [<$c_type _list_release>](self.ptr) };
}
}
impl<'a> IntoIterator for &'a [<$name List>] {
type Item = [<$name Ref>]<'a>;
type IntoIter = [<$name ListIter>]<'a>;
fn into_iter(self) -> Self::IntoIter {
[<$name ListIter>]{ptr: self.ptr, _phantomdata: PhantomData}
}
}
}
}
}
Now, I'd prefer to generate these structs inside a module, so I modified a little bit the macro call :
linked_list_iter!(
sys, // namespace
libvlc_module_description, // C raw type
module_description, // Rust module name
{
name: (String, str),
shortname: (String, str),
longname: (String, str),
help: (String, str),
} // fields
);
And the macro :
macro_rules! linked_list_iter {
(
$namespace:path,
$c_type:ident,
$name:ident,
{
$($(#[$field_meta:meta])*
$field_vis:vis $field_name:ident: ($field_type:ty, $field_b_type:ty)),* $(,)+
}
) => {
paste::item! {
mod $name {
use std::borrow::Cow;
use std::marker::PhantomData;
use crate::tools::from_cstr_ref;
use crate::$namespace::[<$c_type _t>];
use crate::$namespace::[<$c_type _list_release>];
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Item {
$(
$(#[$field_meta:meta])*
$field_vis $field_name : Option<$field_type>,
)*
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct ItemRef<'a> {
$(
$(#[$field_meta:meta])*
$field_vis $field_name : Option<Cow<'a, $field_b_type>>,
)*
}
impl<'a> ItemRef<'a> {
/// Convert to owned strings.
pub fn into_owned(&'a self) -> $name {
$name {
$($field_name: self.$field_name.as_ref().map(|s| s.clone().into_owned()),)*
}
}
}
pub struct ListIter<'a> {
ptr: *mut [<$c_type _t>],
_phantomdata: PhantomData<&'a [<$c_type _t>]>,
}
impl<'a> Iterator for ListIter<'a> {
type Item = ItemRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if self.ptr.is_null() {
return None;
}
let p = self.ptr;
self.ptr = (*p).p_next;
Some(ItemRef {
$($field_name: from_cstr_ref((*p).[<psz_ $field_name>]),)*
})
}
}
}
pub struct List {
ptr: *mut [<$c_type _t>]
}
impl List {
/// Returns raw pointer
pub fn raw(&self) -> *mut [<$c_type _t>] {
self.ptr
}
}
impl Drop for List {
fn drop(&mut self) {
unsafe{ [<$c_type _list_release>](self.ptr) };
}
}
impl<'a> IntoIterator for &'a List {
type Item = ItemRef<'a>;
type IntoIter = ListIter<'a>;
fn into_iter(self) -> Self::IntoIter {
ListIter{ptr: self.ptr, _phantomdata: PhantomData}
}
}
}
// backward compatibility types
type [<$name:camel>] = $name::Item;
type [<$name:camel Ref>]<'a> = $name::ItemRef<'a>;
type [<$name:camel List>] = $name::List;
type [<$name:camel ListIter>]<'a> = $name::ListIter<'a>;
}
}
}
But I have an error in the macro call, complaining about "module_description" not found :
error[E0412]: cannot find type `module_description` in this scope
--> src/core.rs:32:3
|
32 | module_description, // Rust base struct name
| ^^^^^^^^^^^^^^^^^^ not found in this scope
error[E0422]: cannot find struct, variant or union type `module_description` in this scope
--> src/core.rs:32:3
|
32 | module_description, // Rust base struct name
| ^^^^^^^^^^^^^^^^^^ not found in this scope
Some errors have detailed explanations: E0412, E0422.
For more information about an error, try `rustc --explain E0412`.
Any idea on why this error occurs ?
Regards,
--
Pierre