Skip to content
Open
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.

155 changes: 155 additions & 0 deletions prosa_macros/src/tvf/expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
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),

/// Format expression (format! macro)
Format(TokenStream),

/// 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::Format(stream) => stream.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,
}

impl Modifier {
/// Filter out numerical/logical operator (+, -, !)
#[inline]
pub(crate) fn non_numeric(self) -> Self {
if matches!(self, Self::Borrow | Self::Dereference) {
self
} else {
Self::None
}
}
}

/// 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),

#[allow(dead_code)]
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