abi: add struct types and move some terminal definitions
This commit is contained in:
parent
2577e28ce1
commit
12b481398d
@ -47,12 +47,12 @@ fn generate_syscall_dispatcher<P: AsRef<Path>>(out_dir: P) {
|
|||||||
fat_pointer_width: TypeWidth::U128,
|
fat_pointer_width: TypeWidth::U128,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap_fancy("");
|
.unwrap_fancy(yggdrasil_abi_def::ABI_FILE);
|
||||||
|
|
||||||
let generated_dispatcher = out_dir.as_ref().join("generated_dispatcher.rs");
|
let generated_dispatcher = out_dir.as_ref().join("generated_dispatcher.rs");
|
||||||
let file = prettyplease::unparse(
|
let file = prettyplease::unparse(
|
||||||
&abi.emit_syscall_dispatcher("handle_syscall", "impls")
|
&abi.emit_syscall_dispatcher("handle_syscall", "impls")
|
||||||
.unwrap_fancy(""),
|
.expect("Could not generate syscall dispatcher"),
|
||||||
);
|
);
|
||||||
|
|
||||||
fs::write(generated_dispatcher, file.as_bytes()).unwrap();
|
fs::write(generated_dispatcher, file.as_bytes()).unwrap();
|
||||||
|
@ -26,13 +26,11 @@ extern {
|
|||||||
type FileMetadataUpdate = yggdrasil_abi::io::FileMetadataUpdate;
|
type FileMetadataUpdate = yggdrasil_abi::io::FileMetadataUpdate;
|
||||||
type DirectoryEntry = yggdrasil_abi::io::DirectoryEntry;
|
type DirectoryEntry = yggdrasil_abi::io::DirectoryEntry;
|
||||||
type FileAttr = yggdrasil_abi::io::FileAttr;
|
type FileAttr = yggdrasil_abi::io::FileAttr;
|
||||||
|
|
||||||
#[thin]
|
#[thin]
|
||||||
type SeekFrom = yggdrasil_abi::io::SeekFrom;
|
type SeekFrom = yggdrasil_abi::io::SeekFrom;
|
||||||
#[thin]
|
#[thin]
|
||||||
type MessageDestination = yggdrasil_abi::io::MessageDestination;
|
type MessageDestination = yggdrasil_abi::io::MessageDestination;
|
||||||
|
|
||||||
type TerminalOptions = yggdrasil_abi::io::TerminalOptions;
|
|
||||||
type TerminalSize = yggdrasil_abi::io::TerminalSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// abi::error
|
// abi::error
|
||||||
@ -80,6 +78,7 @@ enum Signal(u32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newtype ProcessId(u32);
|
newtype ProcessId(u32);
|
||||||
|
newtype ProcessGroupId(u32);
|
||||||
newtype ThreadId(u32);
|
newtype ThreadId(u32);
|
||||||
|
|
||||||
// abi::io
|
// abi::io
|
||||||
@ -148,6 +147,80 @@ enum PollControl(u32) {
|
|||||||
AddFd = 1,
|
AddFd = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// abi::io::terminal
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct TerminalControlCharacters {
|
||||||
|
/// End-of-file character (0x04, ^D)
|
||||||
|
pub eof: u8,
|
||||||
|
/// Erase character (0x7F, ^?)
|
||||||
|
pub erase: u8,
|
||||||
|
/// Word erase character (0x17, ^W)
|
||||||
|
pub werase: u8,
|
||||||
|
/// Interrupt character (0x03, ^C)
|
||||||
|
pub interrupt: u8,
|
||||||
|
/// Kill character (0x15, ^U)
|
||||||
|
pub kill: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Controls the line discipline of the terminal
|
||||||
|
#[default(SIGNAL | CANONICAL | ECHO | ECHO_ERASE | ECHO_KILL | ECHO_NL)]
|
||||||
|
bitfield TerminalLineOptions(u32) {
|
||||||
|
/// Enables signal processing for special bytes (INTR, QUIT, etc.)
|
||||||
|
SIGNAL: 0,
|
||||||
|
/// Enables canonical mode
|
||||||
|
CANONICAL: 1,
|
||||||
|
/// Echo input characters back
|
||||||
|
ECHO: 2,
|
||||||
|
/// If CANONICAL is set, ERASE character erases chars, WORD_ERASE erases words
|
||||||
|
ECHO_ERASE: 3,
|
||||||
|
/// If CANONICAL is set, KILL erases lines
|
||||||
|
ECHO_KILL: 4,
|
||||||
|
/// If CANONICAL is set, echo newline even if ECHO is not set
|
||||||
|
ECHO_NL: 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines how output to the terminal should be processed
|
||||||
|
#[default(NL_TO_CRNL)]
|
||||||
|
bitfield TerminalOutputOptions(u32) {
|
||||||
|
/// Translate \n to \r\n
|
||||||
|
NL_TO_CRNL: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines how input should be presented to the reader
|
||||||
|
#[default(CR_TO_NL)]
|
||||||
|
bitfield TerminalInputOptions(u32) {
|
||||||
|
/// Translate \n to \r on input
|
||||||
|
NL_TO_CR: 0,
|
||||||
|
/// Translate \r to \n on input
|
||||||
|
CR_TO_NL: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Terminal I/O transformation and control settings
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct TerminalOptions {
|
||||||
|
/// Controls output processing
|
||||||
|
pub output: TerminalOutputOptions,
|
||||||
|
/// Controls input processing
|
||||||
|
pub input: TerminalInputOptions,
|
||||||
|
/// Controls special bytes and line discipline
|
||||||
|
pub line: TerminalLineOptions,
|
||||||
|
/// Specifies control characters of the terminal
|
||||||
|
pub chars: TerminalControlCharacters,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes terminal size in rows and columns
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct TerminalSize {
|
||||||
|
/// Number of text rows
|
||||||
|
pub rows: usize,
|
||||||
|
/// Number of text columns
|
||||||
|
pub columns: usize,
|
||||||
|
}
|
||||||
|
|
||||||
// abi::net
|
// abi::net
|
||||||
|
|
||||||
enum SocketType(u32) {
|
enum SocketType(u32) {
|
||||||
|
@ -17,7 +17,7 @@ fn generate_abi() {
|
|||||||
fat_pointer_width: TypeWidth::U64,
|
fat_pointer_width: TypeWidth::U64,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap_fancy("Could not parse/read ABI file");
|
.unwrap_fancy(yggdrasil_abi_def::ABI_FILE);
|
||||||
|
|
||||||
let types = prettyplease::unparse(
|
let types = prettyplease::unparse(
|
||||||
&abi.emit_file(true, false)
|
&abi.emit_file(true, false)
|
||||||
|
@ -1,83 +1,7 @@
|
|||||||
use crate::bitflags;
|
pub use crate::generated::{
|
||||||
|
TerminalControlCharacters, TerminalInputOptions, TerminalLineOptions, TerminalOptions,
|
||||||
bitflags! {
|
TerminalOutputOptions, TerminalSize,
|
||||||
#[doc = "Defines how output to the terminal should be processed"]
|
};
|
||||||
#[default = (NL_TO_CRNL)]
|
|
||||||
pub struct TerminalOutputOptions: u32 {
|
|
||||||
#[doc = "Translate \n to \r\n"]
|
|
||||||
const NL_TO_CRNL: bit 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[doc = "Defines how input should be presented to the reader"]
|
|
||||||
#[default = (CR_TO_NL)]
|
|
||||||
pub struct TerminalInputOptions: u32 {
|
|
||||||
#[doc = "Translate \n to \r on input"]
|
|
||||||
const NL_TO_CR: bit 0;
|
|
||||||
#[doc = "Translate \r to \n on input"]
|
|
||||||
const CR_TO_NL: bit 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[doc = "Controls the line discipline of the terminal"]
|
|
||||||
#[default = (SIGNAL | CANONICAL | ECHO | ECHO_ERASE | ECHO_KILL | ECHO_NL)]
|
|
||||||
pub struct TerminalLineOptions: u32 {
|
|
||||||
#[doc = "Enables signal processing for special bytes (INTR, QUIT, etc.)"]
|
|
||||||
const SIGNAL: bit 0;
|
|
||||||
#[doc = "Enables canonical mode"]
|
|
||||||
const CANONICAL: bit 1;
|
|
||||||
#[doc = "Echo input characters back"]
|
|
||||||
const ECHO: bit 2;
|
|
||||||
#[doc = "If CANONICAL is set, ERASE character erases chars, WORD_ERASE erases words"]
|
|
||||||
const ECHO_ERASE: bit 3;
|
|
||||||
#[doc = "If CANONICAL is set, KILL erases lines"]
|
|
||||||
const ECHO_KILL: bit 4;
|
|
||||||
#[doc = "If CANONICAL is set, echo newline even if ECHO is not set"]
|
|
||||||
const ECHO_NL: bit 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specifies a set of special control characters
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TerminalControlCharacters {
|
|
||||||
/// End-of-file character (0x04, ^D)
|
|
||||||
pub eof: u8,
|
|
||||||
/// Erase character (0x7F, ^?)
|
|
||||||
pub erase: u8,
|
|
||||||
/// Word erase character (0x17, ^W)
|
|
||||||
pub werase: u8,
|
|
||||||
/// Interrupt character (0x03, ^C)
|
|
||||||
pub interrupt: u8,
|
|
||||||
/// Kill character (0x15, ^U)
|
|
||||||
pub kill: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Terminal I/O transformation and control settings
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TerminalOptions {
|
|
||||||
/// Controls output processing
|
|
||||||
pub output: TerminalOutputOptions,
|
|
||||||
/// Controls input processing
|
|
||||||
pub input: TerminalInputOptions,
|
|
||||||
/// Controls special bytes and line discipline
|
|
||||||
pub line: TerminalLineOptions,
|
|
||||||
/// Specifies control characters of the terminal
|
|
||||||
pub chars: TerminalControlCharacters,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes terminal size in rows and columns
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TerminalSize {
|
|
||||||
/// Number of text rows
|
|
||||||
pub rows: usize,
|
|
||||||
/// Number of text columns
|
|
||||||
pub columns: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TerminalControlCharacters {
|
impl TerminalControlCharacters {
|
||||||
/// const-version of [Default] trait impl
|
/// const-version of [Default] trait impl
|
||||||
|
@ -49,7 +49,7 @@ fn generate_abi() {
|
|||||||
fat_pointer_width: TypeWidth::U64,
|
fat_pointer_width: TypeWidth::U64,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap_fancy("Could not parse/read ABI file");
|
.unwrap_fancy(yggdrasil_abi_def::ABI_FILE);
|
||||||
|
|
||||||
let calls = prettyplease::unparse(
|
let calls = prettyplease::unparse(
|
||||||
&abi.emit_file(false, true)
|
&abi.emit_file(false, true)
|
||||||
|
@ -12,7 +12,10 @@ mod generated {
|
|||||||
// Import all the necessary types from generated ABI
|
// Import all the necessary types from generated ABI
|
||||||
use abi::{
|
use abi::{
|
||||||
error::Error,
|
error::Error,
|
||||||
io::{ChannelPublisherId, FileMode, OpenOptions, PollControl, RawFd},
|
io::{
|
||||||
|
ChannelPublisherId, FileMode, OpenOptions, PollControl, RawFd, TerminalOptions,
|
||||||
|
TerminalSize,
|
||||||
|
},
|
||||||
mem::MappingSource,
|
mem::MappingSource,
|
||||||
net::SocketType,
|
net::SocketType,
|
||||||
process::{ProcessId, Signal},
|
process::{ProcessId, Signal},
|
||||||
|
42
tool/abi-generator/Cargo.lock
generated
42
tool/abi-generator/Cargo.lock
generated
@ -12,42 +12,6 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "abi-lib"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"compiler_builtins",
|
|
||||||
"rustc-std-workspace-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "compiler_builtins"
|
|
||||||
version = "0.1.108"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d68bc55329711cd719c2687bb147bc06211b0521f97ef398280108ccb23227e9"
|
|
||||||
dependencies = [
|
|
||||||
"rustc-std-workspace-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "example-abi"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"abi-generator",
|
|
||||||
"abi-lib",
|
|
||||||
"prettyplease",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "prettyplease"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.78"
|
version = "1.0.78"
|
||||||
@ -66,12 +30,6 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-std-workspace-core"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.52"
|
version = "2.0.52"
|
||||||
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = "^1.0.63"
|
proc-macro2 = { version = "^1.0.63", features = ["span-locations"] }
|
||||||
quote = "^1.0"
|
quote = "^1.0"
|
||||||
syn = { version = "2.0.32", features = ["full"] }
|
syn = { version = "2.0.32", features = ["full"] }
|
||||||
thiserror = "1.0.47"
|
thiserror = "1.0.47"
|
||||||
|
@ -8,9 +8,11 @@ pub mod ty;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abi::ty::{AbiPrimitive, ComplexType, SimpleType},
|
abi::ty::{AbiPrimitive, ComplexType, SimpleType},
|
||||||
|
error::Error,
|
||||||
syntax::{
|
syntax::{
|
||||||
parse_abi, Attributes, BitfieldRange, BitfieldType, BitfieldTypeField,
|
parse_abi, Attributes, BitfieldDefaultValue, BitfieldRange, BitfieldType,
|
||||||
DocumentTypeDefinition, EnumType, EnumTypeVariant, NewType, ParseError, TypeRepr,
|
BitfieldTypeField, DocumentTypeDefinition, EnumType, EnumTypeVariant, NewType, ParseError,
|
||||||
|
StructType, TypeRepr,
|
||||||
},
|
},
|
||||||
TargetEnv,
|
TargetEnv,
|
||||||
};
|
};
|
||||||
@ -101,7 +103,7 @@ impl AbiBuilder {
|
|||||||
&self,
|
&self,
|
||||||
dispatcher_fn: &str,
|
dispatcher_fn: &str,
|
||||||
impl_mod: &str,
|
impl_mod: &str,
|
||||||
) -> Result<syn::File, ParseError> {
|
) -> Result<syn::File, Error> {
|
||||||
let dispatcher_fn = syn::Ident::new(dispatcher_fn, Span::call_site());
|
let dispatcher_fn = syn::Ident::new(dispatcher_fn, Span::call_site());
|
||||||
let impl_mod = syn::Ident::new(impl_mod, Span::call_site());
|
let impl_mod = syn::Ident::new(impl_mod, Span::call_site());
|
||||||
|
|
||||||
@ -269,6 +271,13 @@ impl Abi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GenerateTypeDefinition for StructType {
|
||||||
|
fn generate_type_definition(&self, _abi_type: &ComplexType, _env: &TargetEnv) -> TokenStream {
|
||||||
|
let Self { inner } = self;
|
||||||
|
quote!(#inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GenerateTypeDefinition for EnumType {
|
impl GenerateTypeDefinition for EnumType {
|
||||||
fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream {
|
fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream {
|
||||||
let Self {
|
let Self {
|
||||||
@ -363,6 +372,8 @@ fn generate_transparent_struct(
|
|||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let TypeRepr(repr) = &repr;
|
let TypeRepr(repr) = &repr;
|
||||||
|
|
||||||
|
let attributes = attributes.filtered(|attr| !attr.path().is_ident("default"));
|
||||||
|
|
||||||
let direct = quote! {
|
let direct = quote! {
|
||||||
#attributes
|
#attributes
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||||
@ -440,6 +451,11 @@ impl GenerateTypeDefinition for BitfieldType {
|
|||||||
fields,
|
fields,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
|
// Find default attribute, if any
|
||||||
|
let default_attr: Option<Result<BitfieldDefaultValue, _>> = attributes
|
||||||
|
.iter()
|
||||||
|
.find_map(|attr| attr.path().is_ident("default").then_some(attr.parse_args()));
|
||||||
|
|
||||||
let base = generate_transparent_struct(
|
let base = generate_transparent_struct(
|
||||||
attributes,
|
attributes,
|
||||||
name,
|
name,
|
||||||
@ -465,6 +481,24 @@ impl GenerateTypeDefinition for BitfieldType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let const_default_fn = if let Some(default_attr) = default_attr {
|
||||||
|
// TODO handle error
|
||||||
|
let default_attr = default_attr.unwrap();
|
||||||
|
let value = default_attr
|
||||||
|
.items
|
||||||
|
.into_iter()
|
||||||
|
.map(|ident| quote!(Self::#ident.bits()))
|
||||||
|
.collect::<Punctuated<_, Token![|]>>();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
pub const fn const_default() -> Self {
|
||||||
|
Self(#value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TokenStream::new()
|
||||||
|
};
|
||||||
|
|
||||||
let base = quote! {
|
let base = quote! {
|
||||||
#base
|
#base
|
||||||
|
|
||||||
@ -478,6 +512,12 @@ impl GenerateTypeDefinition for BitfieldType {
|
|||||||
pub const fn contains(&self, value: Self) -> bool {
|
pub const fn contains(&self, value: Self) -> bool {
|
||||||
self.0 & value.0 == value.0
|
self.0 & value.0 == value.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn contains_any(&self, value: Self) -> bool {
|
||||||
|
self.0 & value.0 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#const_default_fn
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::ops::BitOrAssign for #name {
|
impl core::ops::BitOrAssign for #name {
|
||||||
@ -527,6 +567,7 @@ impl GenerateTypeDefinition for DocumentTypeDefinition {
|
|||||||
Self::Enum(inner) => inner.generate_type_definition(abi_type, env),
|
Self::Enum(inner) => inner.generate_type_definition(abi_type, env),
|
||||||
Self::Bitfield(inner) => inner.generate_type_definition(abi_type, env),
|
Self::Bitfield(inner) => inner.generate_type_definition(abi_type, env),
|
||||||
Self::NewType(inner) => inner.generate_type_definition(abi_type, env),
|
Self::NewType(inner) => inner.generate_type_definition(abi_type, env),
|
||||||
|
Self::Struct(inner) => inner.generate_type_definition(abi_type, env),
|
||||||
// Extern types require no definition
|
// Extern types require no definition
|
||||||
Self::ExternType(_) => TokenStream::new(),
|
Self::ExternType(_) => TokenStream::new(),
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,11 @@ pub enum ComplexType {
|
|||||||
Result(Rc<ComplexType>),
|
Result(Rc<ComplexType>),
|
||||||
Option(Rc<ComplexType>),
|
Option(Rc<ComplexType>),
|
||||||
Tuple(Vec<Rc<ComplexType>>),
|
Tuple(Vec<Rc<ComplexType>>),
|
||||||
|
Struct {
|
||||||
|
name: syn::Ident,
|
||||||
|
kind: ExternKind,
|
||||||
|
arguments: syn::PathArguments,
|
||||||
|
},
|
||||||
Extern {
|
Extern {
|
||||||
kind: ExternKind,
|
kind: ExternKind,
|
||||||
alias: syn::Type,
|
alias: syn::Type,
|
||||||
@ -62,6 +67,7 @@ impl Type for ComplexType {
|
|||||||
},
|
},
|
||||||
Self::Tuple(_) => TypeWidth::Unknown,
|
Self::Tuple(_) => TypeWidth::Unknown,
|
||||||
Self::Extern { .. } => TypeWidth::Unknown,
|
Self::Extern { .. } => TypeWidth::Unknown,
|
||||||
|
Self::Struct { .. } => TypeWidth::Unknown,
|
||||||
// MaybeUninit has the same layout as its underlying data
|
// MaybeUninit has the same layout as its underlying data
|
||||||
Self::MaybeUninit(ty) => ty.width(env),
|
Self::MaybeUninit(ty) => ty.width(env),
|
||||||
}
|
}
|
||||||
@ -99,6 +105,7 @@ impl Type for ComplexType {
|
|||||||
} => {
|
} => {
|
||||||
quote!(#alias #arguments)
|
quote!(#alias #arguments)
|
||||||
}
|
}
|
||||||
|
Self::Struct { name, .. } => quote!(#name),
|
||||||
Self::MaybeUninit(ty) => {
|
Self::MaybeUninit(ty) => {
|
||||||
let ty = ty.as_rust_type();
|
let ty = ty.as_rust_type();
|
||||||
quote!(core::mem::MaybeUninit<#ty>)
|
quote!(core::mem::MaybeUninit<#ty>)
|
||||||
@ -174,6 +181,7 @@ impl fmt::Debug for ComplexType {
|
|||||||
}
|
}
|
||||||
r.finish()
|
r.finish()
|
||||||
}
|
}
|
||||||
|
Self::Struct { .. } => todo!(),
|
||||||
Self::Extern { alias, .. } => f.debug_tuple("Extern").field("e!(#alias)).finish(),
|
Self::Extern { alias, .. } => f.debug_tuple("Extern").field("e!(#alias)).finish(),
|
||||||
Self::MaybeUninit(ty) => f.debug_tuple("MaybeUninit").field(&ty).finish(),
|
Self::MaybeUninit(ty) => f.debug_tuple("MaybeUninit").field(&ty).finish(),
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ pub enum AbiPrimitive {
|
|||||||
I32,
|
I32,
|
||||||
U64,
|
U64,
|
||||||
I64,
|
I64,
|
||||||
|
U128,
|
||||||
USize,
|
USize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ impl Type for AbiPrimitive {
|
|||||||
Self::U16 | Self::I16 => TypeWidth::U16,
|
Self::U16 | Self::I16 => TypeWidth::U16,
|
||||||
Self::U32 | Self::I32 => TypeWidth::U32,
|
Self::U32 | Self::I32 => TypeWidth::U32,
|
||||||
Self::U64 | Self::I64 => TypeWidth::U64,
|
Self::U64 | Self::I64 => TypeWidth::U64,
|
||||||
|
Self::U128 => TypeWidth::U128,
|
||||||
Self::USize => env.thin_pointer_width,
|
Self::USize => env.thin_pointer_width,
|
||||||
// ???
|
// ???
|
||||||
Self::Bool => TypeWidth::U32,
|
Self::Bool => TypeWidth::U32,
|
||||||
@ -52,16 +54,17 @@ impl Type for AbiPrimitive {
|
|||||||
Self::I32 => quote!(i32),
|
Self::I32 => quote!(i32),
|
||||||
Self::U64 => quote!(u64),
|
Self::U64 => quote!(u64),
|
||||||
Self::I64 => quote!(i64),
|
Self::I64 => quote!(i64),
|
||||||
|
Self::U128 => quote!(u128),
|
||||||
Self::USize => quote!(usize),
|
Self::USize => quote!(usize),
|
||||||
Self::Bool => quote!(bool),
|
Self::Bool => quote!(bool),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_to_syscall_arguments(&self, _env: &TargetEnv, value: &Ident) -> TokenStream {
|
fn emit_to_syscall_arguments(&self, _env: &TargetEnv, value: &Ident) -> TokenStream {
|
||||||
if *self == Self::USize {
|
match self {
|
||||||
quote!(#value)
|
Self::USize => quote!(#value),
|
||||||
} else {
|
Self::U128 => todo!("Emit to syscall for u128"),
|
||||||
quote!(#value as usize)
|
_ => quote!(#value as usize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +76,7 @@ impl Type for AbiPrimitive {
|
|||||||
) -> (TokenStream, usize) {
|
) -> (TokenStream, usize) {
|
||||||
match self {
|
match self {
|
||||||
Self::Bool => (quote!(#args[#index] != 0), 1),
|
Self::Bool => (quote!(#args[#index] != 0), 1),
|
||||||
|
Self::U128 => todo!("Emit from syscall for u128"),
|
||||||
_ => {
|
_ => {
|
||||||
let ty = self.as_rust_type();
|
let ty = self.as_rust_type();
|
||||||
(quote!(#args[#index] as #ty), 1)
|
(quote!(#args[#index] as #ty), 1)
|
||||||
@ -94,6 +98,7 @@ impl FromStr for AbiPrimitive {
|
|||||||
"u32" => Ok(Self::U32),
|
"u32" => Ok(Self::U32),
|
||||||
"i64" => Ok(Self::I64),
|
"i64" => Ok(Self::I64),
|
||||||
"u64" => Ok(Self::U64),
|
"u64" => Ok(Self::U64),
|
||||||
|
"u128" => Ok(Self::U128),
|
||||||
"bool" => Ok(Self::Bool),
|
"bool" => Ok(Self::Bool),
|
||||||
"usize" => Ok(Self::USize),
|
"usize" => Ok(Self::USize),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
|
@ -7,6 +7,11 @@ use super::{
|
|||||||
document::DocumentItemAttributes,
|
document::DocumentItemAttributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BitfieldDefaultValue {
|
||||||
|
pub items: Punctuated<syn::Ident, Token![|]>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum BitfieldRange {
|
pub enum BitfieldRange {
|
||||||
Single(syn::LitInt),
|
Single(syn::LitInt),
|
||||||
@ -34,6 +39,13 @@ impl DocumentItemAttributes for BitfieldType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for BitfieldDefaultValue {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let items = input.parse_terminated(syn::Ident::parse, Token![|])?;
|
||||||
|
Ok(Self { items })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl syn::parse::Parse for BitfieldRange {
|
impl syn::parse::Parse for BitfieldRange {
|
||||||
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
|
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
|
||||||
let start = input.parse()?;
|
let start = input.parse()?;
|
||||||
|
@ -13,6 +13,10 @@ impl Attributes {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(vec![])
|
Self(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn filtered<P: Fn(&syn::Attribute) -> bool>(&self, pred: P) -> Self {
|
||||||
|
Self(self.0.iter().cloned().filter(pred).collect())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Attributes {
|
impl Deref for Attributes {
|
||||||
|
@ -6,6 +6,7 @@ use super::{
|
|||||||
enum_type::EnumType,
|
enum_type::EnumType,
|
||||||
extern_block::{ExternType, ExternTypeBlock},
|
extern_block::{ExternType, ExternTypeBlock},
|
||||||
newtype::NewType,
|
newtype::NewType,
|
||||||
|
struct_type::StructType,
|
||||||
syscall::SyscallDefinition,
|
syscall::SyscallDefinition,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ pub enum DocumentTypeDefinition {
|
|||||||
Bitfield(BitfieldType),
|
Bitfield(BitfieldType),
|
||||||
Enum(EnumType),
|
Enum(EnumType),
|
||||||
ExternType(ExternType),
|
ExternType(ExternType),
|
||||||
|
Struct(StructType),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Document {
|
pub struct Document {
|
||||||
@ -33,6 +35,7 @@ enum DocumentItem {
|
|||||||
NewType(NewType),
|
NewType(NewType),
|
||||||
Bitfield(BitfieldType),
|
Bitfield(BitfieldType),
|
||||||
Enum(EnumType),
|
Enum(EnumType),
|
||||||
|
Struct(StructType),
|
||||||
|
|
||||||
// Syscall definitions
|
// Syscall definitions
|
||||||
SyscallDefinition(SyscallDefinition),
|
SyscallDefinition(SyscallDefinition),
|
||||||
@ -64,6 +67,8 @@ impl syn::parse::Parse for DocumentItem {
|
|||||||
} else if lookahead.peek(Token![enum]) {
|
} else if lookahead.peek(Token![enum]) {
|
||||||
input.parse::<Token![enum]>()?;
|
input.parse::<Token![enum]>()?;
|
||||||
EnumType::parse(input).map(Self::Enum)
|
EnumType::parse(input).map(Self::Enum)
|
||||||
|
} else if lookahead.peek(Token![struct]) {
|
||||||
|
StructType::parse(input).map(Self::Struct)
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!()
|
||||||
}?;
|
}?;
|
||||||
@ -82,6 +87,7 @@ impl DocumentItemAttributes for DocumentItem {
|
|||||||
Self::Bitfield(inner) => inner.extend_attributes(attrs),
|
Self::Bitfield(inner) => inner.extend_attributes(attrs),
|
||||||
Self::SyscallDefinition(inner) => inner.extend_attributes(attrs),
|
Self::SyscallDefinition(inner) => inner.extend_attributes(attrs),
|
||||||
Self::ExternTypeBlock(_) => unreachable!(),
|
Self::ExternTypeBlock(_) => unreachable!(),
|
||||||
|
Self::Struct(inner) => inner.extend_attributes(attrs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,6 +118,7 @@ impl syn::parse::Parse for Document {
|
|||||||
.map(DocumentTypeDefinition::ExternType),
|
.map(DocumentTypeDefinition::ExternType),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
DocumentItem::Struct(inner) => types.push(DocumentTypeDefinition::Struct(inner)),
|
||||||
DocumentItem::SyscallDefinition(inner) => {
|
DocumentItem::SyscallDefinition(inner) => {
|
||||||
syscalls.push(inner);
|
syscalls.push(inner);
|
||||||
}
|
}
|
||||||
@ -130,6 +137,7 @@ impl ToTokens for DocumentItem {
|
|||||||
Self::NewType(inner) => inner.to_tokens(tokens),
|
Self::NewType(inner) => inner.to_tokens(tokens),
|
||||||
Self::SyscallDefinition(inner) => inner.to_tokens(tokens),
|
Self::SyscallDefinition(inner) => inner.to_tokens(tokens),
|
||||||
Self::ExternTypeBlock(inner) => inner.to_tokens(tokens),
|
Self::ExternTypeBlock(inner) => inner.to_tokens(tokens),
|
||||||
|
Self::Struct(inner) => inner.to_tokens(tokens),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,31 @@
|
|||||||
use std::path::Path;
|
use std::ops::Range;
|
||||||
|
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
|
use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
|
trait SpanHelper {
|
||||||
|
fn matching_columns(&self, line: usize) -> Range<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpanHelper for Span {
|
||||||
|
fn matching_columns(&self, line: usize) -> Range<usize> {
|
||||||
|
let start = self.start();
|
||||||
|
let end = self.end();
|
||||||
|
|
||||||
|
if line == start.line && line == end.line {
|
||||||
|
start.column..end.column
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum SyntaxError {
|
pub enum SyntaxError {
|
||||||
UnhandledType(syn::Type),
|
UnhandledType(syn::Type),
|
||||||
UndefinedIdentifier(syn::Ident),
|
UndefinedIdentifier(syn::Ident),
|
||||||
|
|
||||||
|
PrimitiveReprRequired(syn::Ident),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
@ -15,7 +35,7 @@ pub enum ParseError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait UnwrapFancy<T> {
|
pub trait UnwrapFancy<T> {
|
||||||
fn unwrap_fancy<P: AsRef<Path>>(self, context: P) -> T;
|
fn unwrap_fancy<S: AsRef<str>>(self, source_code: S) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for ParseError {
|
impl From<std::io::Error> for ParseError {
|
||||||
@ -41,37 +61,76 @@ impl SyntaxError {
|
|||||||
match self {
|
match self {
|
||||||
Self::UndefinedIdentifier(id) => id.span(),
|
Self::UndefinedIdentifier(id) => id.span(),
|
||||||
Self::UnhandledType(ty) => ty.span(),
|
Self::UnhandledType(ty) => ty.span(),
|
||||||
|
Self::PrimitiveReprRequired(id) => id.span(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn describe(&self) -> String {
|
pub fn describe(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::UnhandledType(_ty) => "Unhandled type".into(),
|
Self::UnhandledType(ty) => format!("Unhandled type '{}'", quote!(#ty)),
|
||||||
Self::UndefinedIdentifier(id) => format!("Undefined identifier '{}'", id),
|
Self::UndefinedIdentifier(id) => format!("Undefined identifier '{}'", id),
|
||||||
|
Self::PrimitiveReprRequired(id) => {
|
||||||
|
format!("Primitive repr required in type definition, got '{}'", id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print(&self, source_code: &str) {
|
||||||
|
let span = self.span();
|
||||||
|
let start = span.start();
|
||||||
|
let end = span.end();
|
||||||
|
let line_range = start.line..=end.line;
|
||||||
|
|
||||||
|
let lines_context = source_code
|
||||||
|
.split('\n')
|
||||||
|
.skip(start.line.saturating_sub(2))
|
||||||
|
.take(3 + end.line - start.line);
|
||||||
|
|
||||||
|
if start.line == end.line {
|
||||||
|
eprintln!("[ERROR] {}:", self.describe());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, line) in lines_context.enumerate() {
|
||||||
|
let line_idx = i + start.line - 1;
|
||||||
|
eprintln!("{:4} | {}", line_idx, line);
|
||||||
|
if line_range.contains(&line_idx) {
|
||||||
|
let columns = span.matching_columns(line_idx);
|
||||||
|
eprint!("{0:4} | {0:>width$}", "", width = columns.start);
|
||||||
|
for _ in columns {
|
||||||
|
eprint!("~");
|
||||||
|
}
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> UnwrapFancy<T> for Result<T, ParseError> {
|
impl<T> UnwrapFancy<T> for Result<T, ParseError> {
|
||||||
fn unwrap_fancy<P: AsRef<Path>>(self, path: P) -> T {
|
fn unwrap_fancy<S: AsRef<str>>(self, source_code: S) -> T {
|
||||||
match self {
|
match self {
|
||||||
Self::Ok(value) => value,
|
Self::Ok(value) => value,
|
||||||
Self::Err(err) => {
|
Self::Err(err) => {
|
||||||
match err {
|
let n_errors = match err {
|
||||||
ParseError::IoError(err) => {
|
ParseError::IoError(err) => {
|
||||||
eprintln!("{}: {}", path.as_ref().display(), err);
|
eprintln!("ABI read error: {}", err);
|
||||||
|
|
||||||
|
1
|
||||||
}
|
}
|
||||||
ParseError::HardError(err) => {
|
ParseError::HardError(err) => {
|
||||||
eprintln!("{}: {}", path.as_ref().display(), err);
|
eprintln!("{}", err);
|
||||||
|
|
||||||
|
1
|
||||||
}
|
}
|
||||||
ParseError::SyntaxError(errs) => {
|
ParseError::SyntaxError(errs) => {
|
||||||
eprintln!("{}:", path.as_ref().display());
|
for err in errs.iter() {
|
||||||
for err in errs {
|
err.print(source_code.as_ref());
|
||||||
eprintln!("* ...: {}", err.describe());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errs.len()
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
panic!("Compilation aborted");
|
panic!("Compilation aborted, {} error(s)", n_errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,19 +15,21 @@ mod bitfield_type;
|
|||||||
mod enum_type;
|
mod enum_type;
|
||||||
mod extern_block;
|
mod extern_block;
|
||||||
mod newtype;
|
mod newtype;
|
||||||
|
mod struct_type;
|
||||||
|
|
||||||
mod document;
|
mod document;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod syscall;
|
mod syscall;
|
||||||
|
|
||||||
pub use bitfield_type::{BitfieldRange, BitfieldType, BitfieldTypeField};
|
pub use bitfield_type::{BitfieldDefaultValue, BitfieldRange, BitfieldType, BitfieldTypeField};
|
||||||
pub use common::{Attributes, TypeRepr};
|
pub use common::{Attributes, TypeRepr};
|
||||||
pub use document::{Document, DocumentTypeDefinition};
|
pub use document::{Document, DocumentTypeDefinition};
|
||||||
pub use enum_type::{EnumType, EnumTypeVariant};
|
pub use enum_type::{EnumType, EnumTypeVariant};
|
||||||
pub use error::{ParseError, SyntaxError, UnwrapFancy};
|
pub use error::{ParseError, SyntaxError, UnwrapFancy};
|
||||||
pub use extern_block::{ExternType, ExternTypeBlock};
|
pub use extern_block::{ExternType, ExternTypeBlock};
|
||||||
pub use newtype::NewType;
|
pub use newtype::NewType;
|
||||||
|
pub use struct_type::StructType;
|
||||||
|
|
||||||
pub fn parse_abi_document(input: &str) -> Result<Document, ParseError> {
|
pub fn parse_abi_document(input: &str) -> Result<Document, ParseError> {
|
||||||
let document: Document = syn::parse_str(input)?;
|
let document: Document = syn::parse_str(input)?;
|
||||||
@ -167,7 +169,8 @@ fn process_type(t: &syn::Type, known_types: &TypeEnv) -> Result<Rc<ComplexType>,
|
|||||||
|
|
||||||
fn require_primitive_repr(repr: &TypeRepr) -> Result<AbiPrimitive, ParseError> {
|
fn require_primitive_repr(repr: &TypeRepr) -> Result<AbiPrimitive, ParseError> {
|
||||||
let TypeRepr(repr) = repr;
|
let TypeRepr(repr) = repr;
|
||||||
Ok(AbiPrimitive::from_str(repr.to_string().as_str()).expect("TODO proper error message"))
|
Ok(AbiPrimitive::from_str(repr.to_string().as_str())
|
||||||
|
.map_err(|_| ParseError::syntax1(SyntaxError::PrimitiveReprRequired(repr.clone())))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_single_ident_type(ident: syn::Ident) -> syn::Type {
|
fn make_single_ident_type(ident: syn::Ident) -> syn::Type {
|
||||||
@ -242,6 +245,21 @@ fn process_enum_type(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_struct_type(
|
||||||
|
def: &StructType,
|
||||||
|
_known_types: &TypeEnv,
|
||||||
|
) -> Result<(syn::Ident, Rc<ComplexType>), ParseError> {
|
||||||
|
let name = def.inner.ident.clone();
|
||||||
|
Ok((
|
||||||
|
name.clone(),
|
||||||
|
Rc::new(ComplexType::Struct {
|
||||||
|
name,
|
||||||
|
kind: ExternKind::None,
|
||||||
|
arguments: syn::PathArguments::None,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn process_newtype(
|
fn process_newtype(
|
||||||
def: &NewType,
|
def: &NewType,
|
||||||
_known_types: &TypeEnv,
|
_known_types: &TypeEnv,
|
||||||
@ -269,6 +287,8 @@ fn process_type_definition(
|
|||||||
.map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))),
|
.map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))),
|
||||||
DocumentTypeDefinition::NewType(inner) => process_newtype(inner, known_types)
|
DocumentTypeDefinition::NewType(inner) => process_newtype(inner, known_types)
|
||||||
.map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))),
|
.map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))),
|
||||||
|
DocumentTypeDefinition::Struct(inner) => process_struct_type(inner, known_types)
|
||||||
|
.map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
29
tool/abi-generator/src/syntax/struct_type.rs
Normal file
29
tool/abi-generator/src/syntax/struct_type.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use quote::{quote, ToTokens};
|
||||||
|
|
||||||
|
use super::document::DocumentItemAttributes;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StructType {
|
||||||
|
pub inner: syn::ItemStruct,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::parse::Parse for StructType {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let mut inner: syn::ItemStruct = input.parse()?;
|
||||||
|
inner.vis = syn::Visibility::Public(syn::token::Pub(input.span()));
|
||||||
|
Ok(Self { inner })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DocumentItemAttributes for StructType {
|
||||||
|
fn extend_attributes<I: IntoIterator<Item = syn::Attribute>>(&mut self, attrs: I) {
|
||||||
|
self.inner.attrs.extend(attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for StructType {
|
||||||
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||||
|
let Self { inner } = self;
|
||||||
|
tokens.extend(quote!(#inner));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user