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
2 changes: 2 additions & 0 deletions prosa_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ syn = { version = "3", features = ["full"] }
quote = "1"
proc-macro2 = "1"
chrono.workspace = true
thiserror.workspace = true
num-traits = "0.2"

[dev-dependencies]
bytes.workspace = true
Expand Down
66 changes: 45 additions & 21 deletions prosa_macros/src/tvf.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,70 @@
mod buffer;
mod literal;
mod value;
/// Structures to properly identify the TVF fields to generate
pub(crate) mod expr;

use buffer::{generate_list, generate_map};
/// Consume tvf! macro tokens and generate corresponding tree structure of expressions
pub(crate) mod parser;

/// Convert the expressions into tokens to be passed to the compiler
pub(crate) mod tokens;

use crate::tvf::{parser::TvfParser, tokens::buffer_to_tokens};
use proc_macro2::{Delimiter, TokenStream, TokenTree};
use syn::Error;
use quote::quote;
use syn::spanned::Spanned;

pub(crate) fn gen_tvf_impl(input: TokenStream) -> Result<TokenStream, Error> {
pub(crate) fn gen_tvf_impl(input: TokenStream) -> Result<TokenStream, syn::Error> {
// Process the token tree
let span = input.span();
let mut tokens = input.into_iter();

// The first token must be the buffer type used
let buffer_type = if let Some(TokenTree::Ident(buffer_type)) = tokens.next() {
buffer_type
} else {
return Err(Error::new_spanned(
TokenStream::new(),
return Err(syn::Error::new(
span,
"First argument must be a type identifier",
));
};

// The second token must be the content enclosed in {} or []
let result = if let Some(TokenTree::Group(content)) = tokens.next() {
match content.delimiter() {
Delimiter::Brace => generate_map(&buffer_type, &content),
Delimiter::Bracket => generate_list(&buffer_type, &content),
_ => Err(Error::new_spanned(
content,
"Invalid delimiter, expected {} or []",
)),
let output = if let Some(TokenTree::Group(group)) = tokens.next() {
match group.delimiter() {
Delimiter::Brace => {
let mut parser = TvfParser::new(group.stream(), true);
let exprs = parser.collect_expr()?;
buffer_to_tokens(&buffer_type, &exprs)?
}
Delimiter::Bracket => {
let mut parser = TvfParser::new(group.stream(), false);
let exprs = parser.collect_expr()?;
buffer_to_tokens(&buffer_type, &exprs)?
}
_ => {
return Err(syn::Error::new(
span,
"Invalid delimiter, expected {} or []",
));
}
}
} else {
Err(Error::new_spanned(
TokenStream::new(),
return Err(syn::Error::new(
span,
"Second argument must be the content enclosed in {} or []",
))
));
};

// Raise an error on any extra tokens
if let Some(token) = tokens.next() {
return Err(Error::new_spanned(token, "Unexpected token"));
return Err(syn::Error::new_spanned(token, "Unexpected token"));
}

result
Ok(quote![
{
use ::prosa_utils::msg::tvf as __tvf;
use ::prosa_utils::msg::bytes as __bytes;
use ::prosa_utils::msg::chrono as __chrono;
#output
}
])
}
135 changes: 0 additions & 135 deletions prosa_macros/src/tvf/buffer.rs

This file was deleted.

137 changes: 137 additions & 0 deletions prosa_macros/src/tvf/expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use chrono::{NaiveDate, NaiveDateTime};
use proc_macro2::{Span, TokenStream};
use syn::spanned::Spanned;

/// Define a field expression which includes the following elements
/// - a target field identifier
/// - a modifier (unary operator)
/// - a literal / variable / rust-expression
/// - a implicit or explicit target type
pub(crate) struct TvfExpr {
/// Field identifier to use
pub id: TvfId,

/// Modifier for the value
pub modifier: Modifier,

/// Value to use for the field
pub value: TvfValue,

/// Output type to insert the field in the buffer
pub explicit_type: Option<TvfType>,
}

/// Identifier of a field
pub(crate) enum TvfId {
/// We directly have an integer value
Int(usize),

/// Identifier of a variable
Ident(syn::Ident),

/// Rust expression (surrounded by parenthesis)
Expr(TokenStream),
}

/// Define a value to insert into the buffer
pub(crate) enum TvfValue {
/// Simple literal which can be used to implicitely identify the type of the value
Lit(syn::Lit),

/// Identifier of a variable
Ident(syn::Ident),

/// Rust expression (surrounded by parenthesis)
Expr(TokenStream),

/// Sub-buffer
Buffer(Vec<TvfExpr>, Span),
}

impl TvfValue {
pub(crate) fn span(&self) -> Span {
match self {
TvfValue::Lit(lit) => lit.span(),
TvfValue::Ident(ident) => ident.span(),
TvfValue::Expr(stream) => stream.span(),
TvfValue::Buffer(_, span) => *span,
}
}
}

/// The types that can be added to a TVF buffer
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum TvfType {
Byte,
Signed,
Unsigned,
Float,
String,
Bytes,
Date,
DateTime,
Buffer,
}

/// Modifier for a value
/// Usually a not for boolean or a minus sign for numbers, etc..
#[repr(u8)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Modifier {
/// No operator
#[default]
None,

/// + sign, results in no operator being added
Positive,

/// - sign, negate the following value
Negative,

/// logical not for boolean values
LogicalNot,

/// * dereference
Dereference,

/// & borrow
Borrow,
}

/// Simple sequence of bytes to serialize
#[derive(Debug, Default, Clone)]
pub(crate) struct Bytes(pub Vec<u8>);

/// Values that have been identified
pub(crate) enum Value<'e> {
Bool(bool),
Byte(u8),
Signed(i64),
Unsigned(u64),
Float(f64),
String(String),
Bytes(Bytes),
Date(NaiveDate),
DateTime(NaiveDateTime),
Buffer(&'e [TvfExpr]),
}

impl<'e> Value<'e> {
/// Process the TvfValue to identify its type
#[rustfmt::skip]
pub(crate) fn identify(&self) -> TvfType {
match self {
Self::Bool (_) => TvfType::Byte,
Self::Byte (_) => TvfType::Byte,
Self::Signed (_) => TvfType::Signed,
Self::Unsigned (_) => TvfType::Unsigned,
Self::Float (_) => TvfType::Float,
Self::String (_) => TvfType::String,
Self::Bytes (_) => TvfType::Bytes,
Self::Date (_) => TvfType::Date,
Self::DateTime (_) => TvfType::DateTime,
Self::Buffer (_) => TvfType::Buffer,
}
}
}
Loading
Loading