Skip to content

Test for proc macro reworking #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
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
8 changes: 8 additions & 0 deletions udf-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
)]

mod register;
mod simplified;
mod types;

use proc_macro::TokenStream;
Expand Down Expand Up @@ -78,3 +79,10 @@ pub fn register(args: TokenStream, item: TokenStream) -> TokenStream {
// Keep this file clean by keeping the dirty work in entry
register::register(&args, item)
}

/// Apply `#[udf::udf]` to any function to make it a UDF
#[proc_macro_attribute]
pub fn udf(args: TokenStream, item: TokenStream) -> TokenStream {
// Keep this file clean by keeping the dirty work in entry
simplified::simple_udf(&args, item)
}
26 changes: 26 additions & 0 deletions udf-macros/src/simplified.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use heck::AsSnakeCase;
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote};
use syn::parse::{Parse, ParseStream, Parser};
use syn::punctuated::Punctuated;
use syn::{
parse_macro_input, parse_quote, DeriveInput, Error, Expr, ExprLit, Ident, ImplItem,
ImplItemType, Item, ItemFn, ItemImpl, Lit, Meta, Path, PathSegment, Token, Type, TypePath,
TypeReference, Visibility,
};

use crate::match_variant;
use crate::types::{make_type_list, ImplType, RetType, TypeClass};

pub fn simple_udf(args: &TokenStream, input: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(input as ItemFn);
if !matches!(parsed.vis, Visibility::Public(_)) {
return Error::new_spanned(parsed.vis, "UDFs must be marked `pub`")
.into_compile_error()
.into();
}
// let sig = parsed.sig
// input
todo!()
}
6 changes: 6 additions & 0 deletions udf-macros/tests/fail/shortform_not_pub.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Functions must be public

#[udf_macros::udf]
fn foo() {}

fn main() {}
7 changes: 7 additions & 0 deletions udf-macros/tests/fail/shortform_not_pub.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: UDFs must be marked `pub`
--> tests/fail/shortform_not_pub.rs:3:1
|
3 | #[udf_macros::udf]
| ^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `udf_macros::udf` (in Nightly builds, run with -Z macro-backtrace for more info)
72 changes: 72 additions & 0 deletions udf/src/argparse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! Helpers for parsing args simply
use std::str;

use crate::{SqlResult, SqlType};

// OPTIOANL ARGS specify these in the proc macro signature
pub enum Error<'res> {
InvalidType(SqlType),
UnexpectedNull,
Utf8(&'res [u8], str::Utf8Error),
}

/// Types with this trait can easily be used as an argument
pub trait SqlArg<'res>: Sized {
/// How to set argument coercion
const COERCE_TYPE: SqlType;

fn from_res(value: SqlResult<'res>) -> Result<Self, Error>;
}

impl<'res> SqlArg<'res> for &'res str {
const COERCE_TYPE: SqlType = SqlType::String;

fn from_res(value: SqlResult<'res>) -> Result<Self, Error> {
match value {
SqlResult::String(Some(v)) => Ok(str::from_utf8(v).map_err(|e| Error::Utf8(v, e))?),
SqlResult::Decimal(Some(s)) => Ok(s),
SqlResult::String(None) | SqlResult::Decimal(None) => todo!(),
SqlResult::Real(_) | SqlResult::Int(_) => Err(Error::InvalidType(value.as_type())),
}
}
}

impl<'res> SqlArg<'res> for i64 {
const COERCE_TYPE: SqlType = SqlType::Int;

fn from_res(value: SqlResult<'res>) -> Result<Self, Error> {
match value {
SqlResult::Int(Some(v)) => Ok(v),
SqlResult::Int(None) => Err(Error::UnexpectedNull),
SqlResult::String(_) | SqlResult::Decimal(_) | SqlResult::Real(_) => {
Err(Error::InvalidType(value.as_type()))
}
}
}
}

impl<'res> SqlArg<'res> for f64 {
const COERCE_TYPE: SqlType = SqlType::Real;

fn from_res(value: SqlResult<'res>) -> Result<Self, Error> {
match value {
SqlResult::Real(Some(v)) => Ok(v),
SqlResult::Real(None) => Err(Error::UnexpectedNull),
SqlResult::String(_) | SqlResult::Decimal(_) | SqlResult::Int(_) => {
Err(Error::InvalidType(value.as_type()))
}
}
}
}

impl<'res, T: SqlArg<'res>> SqlArg<'res> for Option<T> {
const COERCE_TYPE: SqlType = T::COERCE_TYPE;

fn from_res(value: SqlResult<'res>) -> Result<Self, Error> {
if value.is_null() {
Ok(None)
} else {
Ok(Some(T::from_res(value)?))
}
}
}
42 changes: 18 additions & 24 deletions udf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,26 +120,15 @@
//! issues compiling, be sure to update your toolchain.

// Strict clippy
#![warn(
clippy::pedantic,
// clippy::cargo,
clippy::nursery,
clippy::str_to_string,
clippy::exhaustive_enums,
clippy::pattern_type_mismatch
)]
#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
#![warn(clippy::str_to_string)]
#![warn(clippy::exhaustive_enums)]
#![warn(clippy::pattern_type_mismatc)]
// Pedantic config
#![allow(
clippy::missing_const_for_fn,
// clippy::missing_panics_doc,
clippy::must_use_candidate,
clippy::cast_possible_truncation
)]

// We re-export this so we can use it in our macro, but don't need it
// to show up in our docs
#[doc(hidden)]
pub extern crate chrono;
#![allow(clippy::missing_const_for_fn)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::cast_possible_truncation)]

#[doc(hidden)]
pub extern crate udf_sys;
Expand All @@ -150,17 +139,22 @@ pub use udf_macros::register;

#[macro_use]
mod macros;
mod argparse;
pub mod mock;
pub mod prelude;
pub mod traits;
pub mod types;

// We hide this because it's really only used by our proc macros
#[doc(hidden)]
pub mod wrapper;
mod wrapper;

#[doc(inline)]
pub use traits::*;
#[doc(inline)]
pub use types::{MYSQL_ERRMSG_SIZE, *};

pub mod mock;
#[doc(hidden)]
pub mod internal {
pub use {chrono, udf_sys};

// pub use super::argparse::*;
pub use super::wrapper;
}
24 changes: 4 additions & 20 deletions udf/src/types/arg_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,30 +161,14 @@ mod tests {
// Verify no size issues
#[test]
fn args_size_init() {
assert_eq!(
size_of::<UDF_ARGS>(),
size_of::<ArgList<Init>>(),
concat!("Size of: ", stringify!(UDF_ARGS))
);
assert_eq!(
align_of::<UDF_ARGS>(),
align_of::<ArgList<Init>>(),
concat!("Alignment of ", stringify!(UDF_ARGS))
);
assert_eq!(size_of::<UDF_ARGS>(), size_of::<ArgList<Init>>(),);
assert_eq!(align_of::<UDF_ARGS>(), align_of::<ArgList<Init>>(),);
}

// Verify no size issues
#[test]
fn args_size_process() {
assert_eq!(
size_of::<UDF_ARGS>(),
size_of::<ArgList<Process>>(),
concat!("Size of: ", stringify!(UDF_ARGS))
);
assert_eq!(
align_of::<UDF_ARGS>(),
align_of::<ArgList<Process>>(),
concat!("Alignment of ", stringify!(UDF_ARGS))
);
assert_eq!(size_of::<UDF_ARGS>(), size_of::<ArgList<Process>>(),);
assert_eq!(align_of::<UDF_ARGS>(), align_of::<ArgList<Process>>(),);
}
}
20 changes: 19 additions & 1 deletion udf/src/types/sql_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl TryFrom<&SqlResult<'_>> for SqlType {
///
/// This enum is labeled `non_exhaustive` to leave room for future types and
/// coercion options.
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Clone, Copy)]
#[non_exhaustive]
pub enum SqlResult<'a> {
// INVALID_RESULT and ROW_RESULT are other options, but not valid for UDFs
Expand Down Expand Up @@ -209,6 +209,15 @@ impl<'a> SqlResult<'a> {
matches!(*self, Self::Decimal(_))
}

/// Check if this argument is a null type
#[inline]
pub fn is_null(&self) -> bool {
matches!(
self,
Self::String(None) | Self::Real(None) | Self::Int(None) | Self::Decimal(None)
)
}

/// Return this type as an integer if possible
///
/// This will exist if the variant is [`SqlResult::Int`], and it contains a
Expand Down Expand Up @@ -267,4 +276,13 @@ impl<'a> SqlResult<'a> {
_ => None,
}
}

pub(crate) fn as_type(&'a self) -> SqlType {
match self {
SqlResult::String(_) => SqlType::String,
SqlResult::Real(_) => SqlType::Real,
SqlResult::Int(_) => SqlType::Int,
SqlResult::Decimal(_) => SqlType::Decimal,
}
}
}
Loading