#![no_std]
extern crate alloc;
extern crate proc_macro;
use alloc::{
format,
string::{String, ToString},
vec::Vec,
};
use core::iter::FromIterator;
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
const RESERVED_FIELDS: [&str; 2] = ["__decodable_internal_data", "__decodable_internal_offset"];
fn is_already_sized(item: TokenStream) -> bool {
let stream = item.into_iter();
for next in stream {
if let TokenTree::Group(g) = next.clone() {
if g.delimiter() == proc_macro::Delimiter::Bracket {
for t in g.stream().into_iter() {
if let TokenTree::Ident(i) = t {
if i.to_string() == "already_sized" {
return true;
}
}
}
}
}
}
false
}
fn remove_attributes(item: TokenStream) -> TokenStream {
let stream = item.into_iter();
let mut is_attribute = false;
let mut result = Vec::new();
for next in stream {
match next.clone() {
TokenTree::Punct(p) => {
if p.to_string() == "#" {
is_attribute = true;
} else {
result.push(next.clone());
}
}
TokenTree::Group(g) => {
if is_attribute {
continue;
} else {
let delimiter = g.delimiter();
let cleaned_group = remove_attributes(g.stream());
let cleaned_group = TokenTree::Group(Group::new(delimiter, cleaned_group));
result.push(cleaned_group);
}
}
_ => {
is_attribute = false;
result.push(next.clone());
}
}
}
TokenStream::from_iter(result)
}
enum ParserState {
Name,
Type,
Generics(usize),
}
#[derive(Clone, Debug)]
struct ParsedStruct {
pub name: String,
pub generics: String,
pub fields: Vec<ParsedField>,
}
#[derive(Clone, Debug)]
struct ParsedField {
name: String,
span: Span,
type_: String,
generics: String,
}
struct MacroError {
span: Span,
message: String,
}
impl ParsedField {
pub fn new() -> Self {
ParsedField {
name: "".to_string(),
span: Span::call_site(),
type_: "".to_string(),
generics: "".to_string(),
}
}
pub fn get_generics(&self) -> String {
if self.generics == "<'decoder>" || self.generics.is_empty() {
"".to_string()
} else {
format!("::{}", self.generics.clone())
}
}
pub fn as_static(&self) -> String {
if self.generics.is_empty() {
"".to_string()
} else {
".into_static()".to_string()
}
}
pub fn is_empty(&self) -> bool {
self.name.is_empty() && self.type_.is_empty() && self.generics.is_empty()
}
}
fn emit_compile_error(message: &str) -> TokenStream {
emit_compile_error_at(Span::call_site(), message)
}
fn emit_compile_error_at(span: Span, message: &str) -> TokenStream {
let mut inner = TokenStream::new();
let mut literal = Literal::string(message);
literal.set_span(span);
inner.extend([TokenTree::Literal(literal)]);
let mut group = Group::new(Delimiter::Parenthesis, inner);
group.set_span(span);
let mut bang = Punct::new('!', Spacing::Alone);
bang.set_span(span);
let mut semicolon = Punct::new(';', Spacing::Alone);
semicolon.set_span(span);
TokenStream::from_iter([
TokenTree::Ident(Ident::new("compile_error", span)),
TokenTree::Punct(bang),
TokenTree::Group(group),
TokenTree::Punct(semicolon),
])
}
fn parse_generated_tokens(result: &str) -> TokenStream {
result.parse().unwrap_or_else(|err| {
emit_compile_error(&format!(
"derive_codec_sv2 generated invalid Rust tokens: {}",
err
))
})
}
fn get_struct_properties(item: TokenStream) -> Result<ParsedStruct, MacroError> {
let item = remove_attributes(item);
let mut stream = item.into_iter();
loop {
match stream.next() {
Some(TokenTree::Ident(i)) if i.to_string() == "struct" => break,
Some(_) => continue,
None => {
return Err(MacroError {
span: Span::call_site(),
message: "Expected a struct definition".to_string(),
})
}
}
}
let struct_name = match stream.next() {
Some(TokenTree::Ident(i)) => i.to_string(),
Some(token) => {
return Err(MacroError {
span: token.span(),
message: format!("Expected a struct name, found '{}'", token),
})
}
None => {
return Err(MacroError {
span: Span::call_site(),
message: "Struct has no name".to_string(),
})
}
};
let mut struct_generics = "".to_string();
let group: Vec<TokenTree>;
loop {
match stream.next() {
Some(TokenTree::Group(g)) => {
group = g.stream().into_iter().collect();
break;
}
Some(TokenTree::Punct(p)) => {
struct_generics = format!("{struct_generics}{p}");
}
Some(TokenTree::Ident(i)) => {
struct_generics = format!("{struct_generics}{i}");
}
Some(token) => {
return Err(MacroError {
span: token.span(),
message: format!(
"Struct '{}' has invalid tokens before its fields: '{}'",
struct_name, token
),
});
}
None => {
return Err(MacroError {
span: Span::call_site(),
message: format!("Struct '{}' has no fields", struct_name),
})
}
};
}
let fields = parse_struct_fields(group)?;
Ok(ParsedStruct {
name: struct_name,
generics: struct_generics,
fields,
})
}
fn parse_struct_fields(group: Vec<TokenTree>) -> Result<Vec<ParsedField>, MacroError> {
let mut fields = Vec::new();
let mut field_ = ParsedField::new();
let mut field_parser_state = ParserState::Name;
let mut last_span = Span::call_site();
for token in group {
last_span = token.span();
match (token, &field_parser_state) {
(TokenTree::Ident(i), ParserState::Name) => {
if i.to_string() == "pub" {
continue;
} else {
field_.name = i.to_string();
field_.span = i.span();
}
}
(TokenTree::Ident(i), ParserState::Type) => {
field_.type_ = i.to_string();
}
(TokenTree::Ident(i), ParserState::Generics(_)) => {
field_.generics = format!("{}{}", field_.generics, i);
}
(TokenTree::Punct(p), ParserState::Name) => {
if p.to_string() == ":" {
field_parser_state = ParserState::Type
} else {
return Err(MacroError {
span: p.span(),
message: format!("Unexpected token '{}' in parsing {:#?}", p, field_),
});
}
}
(TokenTree::Punct(p), ParserState::Type) => match p.to_string().as_ref() {
"," => {
field_parser_state = ParserState::Name;
fields.push(field_.clone());
field_ = ParsedField::new();
}
"<" => {
field_.generics = "<".to_string();
field_parser_state = ParserState::Generics(0);
}
_ => {
return Err(MacroError {
span: p.span(),
message: format!("Unexpected token '{}' in parsing {:#?}", p, field_),
})
}
},
(TokenTree::Punct(p), ParserState::Generics(open_brackets)) => {
match p.to_string().as_ref() {
"'" => {
field_.generics = format!("{}{}", field_.generics, p);
}
"<" => {
field_.generics = format!("{}{}", field_.generics, p);
field_parser_state = ParserState::Generics(open_brackets + 1);
}
">" => {
field_.generics = format!("{}{}", field_.generics, p);
if open_brackets == &0 {
field_parser_state = ParserState::Type
} else {
field_parser_state = ParserState::Generics(open_brackets - 1);
}
}
_ => {
field_.generics = format!("{}{}", field_.generics, p);
}
}
}
(token, _) => {
return Err(MacroError {
span: token.span(),
message: format!("Unexpected token '{}' while parsing struct fields", token),
});
}
}
}
if !field_.is_empty() {
if field_.name.is_empty() || field_.type_.is_empty() {
return Err(MacroError {
span: last_span,
message: format!(
"Incomplete field definition near '{}'; expected a named field like `name: Type`",
field_.name
),
});
}
fields.push(field_);
}
Ok(fields)
}
#[proc_macro_derive(Decodable)]
pub fn decodable(item: TokenStream) -> TokenStream {
let parsed_struct = match get_struct_properties(item) {
Ok(parsed_struct) => parsed_struct,
Err(err) => return emit_compile_error_at(err.span, &err.message),
};
let data_ident = RESERVED_FIELDS[0];
let offset_ident = RESERVED_FIELDS[1];
for field in &parsed_struct.fields {
if RESERVED_FIELDS.contains(&field.name.as_str()) {
return emit_compile_error_at(field.span, &format!(
"Field name '{}' is reserved and cannot be used in struct '{}'. Rename it to avoid conflicts.",
field.name, parsed_struct.name
));
}
}
let mut derive_fields = String::new();
for f in parsed_struct.fields.clone() {
let field = format!(
"
if {offset} > {data}.len() {{
return Err(Error::OutOfBound);
}}
let {name}: Vec<FieldMarker> = {ty}{generics}::get_structure(& {data}[{offset}..])?;
let __size = {name}.size_hint_(&{data}, {offset})?;
{offset} = ({offset}.checked_add(__size)
.ok_or(Error::OutOfBound)?) ;
let {name} = {name}.try_into()?;
fields.push({name});
",
name = f.name,
ty = f.type_,
generics = f.get_generics(),
data = data_ident,
offset = offset_ident
);
derive_fields.push_str(&field)
}
let mut derive_static_fields = String::new();
for f in parsed_struct.fields.clone() {
let field = format!(
"
{}: self.{}.clone(){},
",
f.name,
f.name,
f.as_static(),
);
derive_static_fields.push_str(&field)
}
let mut derive_decoded_fields = String::new();
let mut fields = parsed_struct.fields.clone();
fields.reverse();
for f in fields.clone() {
let field = format!(
"
{}: {}{}::from_decoded_fields({}.pop().ok_or(Error::NoDecodableFieldPassed)?.into())?,
",
f.name,
f.type_,
f.get_generics(),
data_ident
);
derive_decoded_fields.push_str(&field)
}
let impl_generics = if !parsed_struct.generics.is_empty() {
parsed_struct.clone().generics
} else {
"<'decoder>".to_string()
};
let result = format!(
"mod impl_parse_decodable_{} {{
use ::binary_sv2::{{decodable::DecodableField, decodable::FieldMarker, Decodable, Error, SizeHint}};
use super::*;
impl{} Decodable<'decoder> for {}{} {{
fn get_structure({}: &[u8]) -> Result<Vec<FieldMarker>, Error> {{
let mut fields = Vec::new();
let mut {} = 0;
{}
Ok(fields)
}}
fn from_decoded_fields(mut {}: Vec<DecodableField<'decoder>>) -> Result<Self, Error> {{
Ok(Self {{
{}
}})
}}
}}
impl{} {}{} {{
pub fn into_static(self) -> {}{} {{
{} {{
{}
}}
}}
}}
impl{} {}{} {{
pub fn as_static(&self) -> {}{} {{
{} {{
{}
}}
}}
}}
}}",
parsed_struct.name.to_lowercase(),
impl_generics,
parsed_struct.name,
parsed_struct.generics,
data_ident,
offset_ident,
derive_fields,
data_ident,
derive_decoded_fields,
impl_generics,
parsed_struct.name,
parsed_struct.generics,
parsed_struct.name,
get_static_generics(&parsed_struct.generics),
parsed_struct.name,
derive_static_fields,
impl_generics,
parsed_struct.name,
parsed_struct.generics,
parsed_struct.name,
get_static_generics(&parsed_struct.generics),
parsed_struct.name,
derive_static_fields,
);
parse_generated_tokens(&result)
}
fn get_static_generics(gen: &str) -> &str {
if gen.is_empty() {
gen
} else {
"<'static>"
}
}
#[proc_macro_derive(Encodable, attributes(already_sized))]
pub fn encodable(item: TokenStream) -> TokenStream {
let is_already_sized = is_already_sized(item.clone());
let parsed_struct = match get_struct_properties(item) {
Ok(parsed_struct) => parsed_struct,
Err(err) => return emit_compile_error_at(err.span, &err.message),
};
let fields = parsed_struct.fields.clone();
let mut field_into_decoded_field = String::new();
for f in fields.clone() {
let field = format!(
"
let val = v.{};
fields.push(val.into());
",
f.name
);
field_into_decoded_field.push_str(&field)
}
let mut sizes = String::new();
for f in fields {
let field = format!(
"
size += self.{}.get_size();
",
f.name
);
sizes.push_str(&field)
}
let impl_generics = if !parsed_struct.generics.is_empty() {
parsed_struct.clone().generics
} else {
"<'decoder>".to_string()
};
let get_size = if is_already_sized {
String::new()
} else {
format!(
"
impl{} ::binary_sv2::GetSize for {}{} {{
fn get_size(&self) -> usize {{
let mut size = 0;
{}
size
}}
}}
",
impl_generics, parsed_struct.name, parsed_struct.generics, sizes
)
};
let result = format!(
"mod impl_parse_encodable_{} {{
use ::binary_sv2::{{encodable::EncodableField, GetSize}};
use super::{};
extern crate alloc;
use alloc::vec::Vec;
impl{} From<{}{}> for ::binary_sv2::EncodableField<'decoder> {{
fn from(v: {}{}) -> Self {{
let mut fields: Vec<EncodableField> = Vec::new();
{}
Self::Struct(fields)
}}
}}
{}
}}",
parsed_struct.name.to_lowercase(),
parsed_struct.name,
impl_generics,
parsed_struct.name,
parsed_struct.generics,
parsed_struct.name,
parsed_struct.generics,
field_into_decoded_field,
get_size,
);
parse_generated_tokens(&result)
}