From 3e81b4560b0688690544a8468b1be6acabe608cb Mon Sep 17 00:00:00 2001 From: Anton Parfonov Date: Fri, 13 Dec 2024 00:44:50 +0200 Subject: [PATCH 1/5] derive: Use global scope in quote --- derive/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index bd5675a..73f31a1 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -54,10 +54,9 @@ pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - // TODO: replace `clickhouse` with `::clickhouse` here. let expanded = quote! { #[automatically_derived] - impl #impl_generics clickhouse::Row for #name #ty_generics #where_clause { + impl #impl_generics ::clickhouse::Row for #name #ty_generics #where_clause { const COLUMN_NAMES: &'static [&'static str] = #column_names; } }; From 91e8054269be74f47d8e683b7d604368b29b1f09 Mon Sep 17 00:00:00 2001 From: Anton Parfonov Date: Sat, 14 Dec 2024 04:03:00 +0200 Subject: [PATCH 2/5] Add support for #[row(crate = ...)] --- derive/src/lib.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- src/row.rs | 12 +++++++++--- src/sql/mod.rs | 4 ++-- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 73f31a1..677b82d 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,11 +1,49 @@ use proc_macro2::TokenStream; -use quote::quote; +use quote::{quote, ToTokens}; use serde_derive_internals::{ attr::{Container, Default as SerdeDefault, Field}, Ctxt, }; use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields}; +struct Attributes { + crate_name: syn::Path, +} + +impl From<&[syn::Attribute]> for Attributes { + fn from(attrs: &[syn::Attribute]) -> Self { + const ATTRIBUTE_NAME: &str = "row"; + const ATTRIBUTE_SYNTAX: &str = "#[row(crate = ...)]"; + + const CRATE_NAME: &str = "crate"; + const DEFAULT_CRATE_NAME: &str = "clickhouse"; + + let mut crate_name = None; + for attr in attrs { + if attr.path().is_ident(ATTRIBUTE_NAME) { + let row = attr.parse_args::().unwrap(); + let syn::Expr::Assign(syn::ExprAssign { left, right, .. }) = row else { + panic!("expected `{}`", ATTRIBUTE_SYNTAX); + }; + if left.to_token_stream().to_string() != CRATE_NAME { + panic!("expected `{}`", ATTRIBUTE_SYNTAX); + } + let syn::Expr::Path(syn::ExprPath { path, .. }) = *right else { + panic!("expected `{}`", ATTRIBUTE_SYNTAX); + }; + crate_name = Some(path); + } + } + let crate_name = crate_name.unwrap_or_else(|| { + syn::Path::from(syn::Ident::new( + DEFAULT_CRATE_NAME, + proc_macro2::Span::call_site(), + )) + }); + Self { crate_name } + } +} + fn column_names(data: &DataStruct, cx: &Ctxt, container: &Container) -> TokenStream { match &data.fields { Fields::Named(fields) => { @@ -36,11 +74,12 @@ fn column_names(data: &DataStruct, cx: &Ctxt, container: &Container) -> TokenStr // TODO: support wrappers `Wrapper(Inner)` and `Wrapper(T)`. // TODO: support the `nested` attribute. // TODO: support the `crate` attribute. -#[proc_macro_derive(Row)] +#[proc_macro_derive(Row, attributes(row))] pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let cx = Ctxt::new(); + let Attributes { crate_name } = Attributes::from(input.attrs.as_slice()); let container = Container::from_ast(&cx, &input); let name = input.ident; @@ -56,7 +95,7 @@ pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let expanded = quote! { #[automatically_derived] - impl #impl_generics ::clickhouse::Row for #name #ty_generics #where_clause { + impl #impl_generics #crate_name::Row for #name #ty_generics #where_clause { const COLUMN_NAMES: &'static [&'static str] = #column_names; } }; diff --git a/src/row.rs b/src/row.rs index c5ca680..8efa4db 100644 --- a/src/row.rs +++ b/src/row.rs @@ -75,21 +75,21 @@ pub(crate) fn join_column_names() -> Option { #[cfg(test)] mod tests { - // XXX: need for `derive(Row)`. Provide `row(crate = ..)` instead. - use crate as clickhouse; - use clickhouse::Row; + use crate::Row; use super::*; #[test] fn it_grabs_simple_struct() { #[derive(Row)] + #[row(crate = crate)] #[allow(dead_code)] struct Simple1 { one: u32, } #[derive(Row)] + #[row(crate = crate)] #[allow(dead_code)] struct Simple2 { one: u32, @@ -103,6 +103,7 @@ mod tests { #[test] fn it_grabs_mix() { #[derive(Row)] + #[row(crate = crate)] struct SomeRow { _a: u32, } @@ -115,6 +116,7 @@ mod tests { use serde::Serialize; #[derive(Row, Serialize)] + #[row(crate = crate)] #[allow(dead_code)] struct TopLevel { #[serde(rename = "two")] @@ -129,6 +131,7 @@ mod tests { use serde::Serialize; #[derive(Row, Serialize)] + #[row(crate = crate)] #[allow(dead_code)] struct TopLevel { one: u32, @@ -144,6 +147,7 @@ mod tests { use serde::Deserialize; #[derive(Row, Deserialize)] + #[row(crate = crate)] #[allow(dead_code)] struct TopLevel { one: u32, @@ -158,6 +162,7 @@ mod tests { fn it_rejects_other() { #[allow(dead_code)] #[derive(Row)] + #[row(crate = crate)] struct NamedTuple(u32, u32); assert_eq!(join_column_names::(), None); @@ -170,6 +175,7 @@ mod tests { use serde::Serialize; #[derive(Row, Serialize)] + #[row(crate = crate)] #[allow(dead_code)] struct MyRow { r#type: u32, diff --git a/src/sql/mod.rs b/src/sql/mod.rs index 66330f6..465fc77 100644 --- a/src/sql/mod.rs +++ b/src/sql/mod.rs @@ -149,12 +149,11 @@ impl SqlBuilder { mod tests { use super::*; - // XXX: need for `derive(Row)`. Provide `row(crate = ..)` instead. - use crate as clickhouse; use clickhouse_derive::Row; #[allow(unused)] #[derive(Row)] + #[row(crate = crate)] struct Row { a: u32, b: u32, @@ -162,6 +161,7 @@ mod tests { #[allow(unused)] #[derive(Row)] + #[row(crate = crate)] struct Unnamed(u32, u32); #[test] From b56dad8a5c24ba111290185f10de1b7b662e12fd Mon Sep 17 00:00:00 2001 From: Anton Parfonov Date: Sat, 14 Dec 2024 04:04:55 +0200 Subject: [PATCH 3/5] Rename crate_name to crate_path --- derive/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 677b82d..54ca752 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -7,7 +7,7 @@ use serde_derive_internals::{ use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields}; struct Attributes { - crate_name: syn::Path, + crate_path: syn::Path, } impl From<&[syn::Attribute]> for Attributes { @@ -15,8 +15,8 @@ impl From<&[syn::Attribute]> for Attributes { const ATTRIBUTE_NAME: &str = "row"; const ATTRIBUTE_SYNTAX: &str = "#[row(crate = ...)]"; - const CRATE_NAME: &str = "crate"; - const DEFAULT_CRATE_NAME: &str = "clickhouse"; + const CRATE_PATH: &str = "crate"; + const DEFAULT_CRATE_PATH: &str = "clickhouse"; let mut crate_name = None; for attr in attrs { @@ -25,7 +25,7 @@ impl From<&[syn::Attribute]> for Attributes { let syn::Expr::Assign(syn::ExprAssign { left, right, .. }) = row else { panic!("expected `{}`", ATTRIBUTE_SYNTAX); }; - if left.to_token_stream().to_string() != CRATE_NAME { + if left.to_token_stream().to_string() != CRATE_PATH { panic!("expected `{}`", ATTRIBUTE_SYNTAX); } let syn::Expr::Path(syn::ExprPath { path, .. }) = *right else { @@ -36,11 +36,11 @@ impl From<&[syn::Attribute]> for Attributes { } let crate_name = crate_name.unwrap_or_else(|| { syn::Path::from(syn::Ident::new( - DEFAULT_CRATE_NAME, + DEFAULT_CRATE_PATH, proc_macro2::Span::call_site(), )) }); - Self { crate_name } + Self { crate_path: crate_name } } } @@ -79,7 +79,7 @@ pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let cx = Ctxt::new(); - let Attributes { crate_name } = Attributes::from(input.attrs.as_slice()); + let Attributes { crate_path } = Attributes::from(input.attrs.as_slice()); let container = Container::from_ast(&cx, &input); let name = input.ident; @@ -95,7 +95,7 @@ pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let expanded = quote! { #[automatically_derived] - impl #impl_generics #crate_name::Row for #name #ty_generics #where_clause { + impl #impl_generics #crate_path::Row for #name #ty_generics #where_clause { const COLUMN_NAMES: &'static [&'static str] = #column_names; } }; From 43c5d83ff215cfcf0f034bd7bf7b80d49003953a Mon Sep 17 00:00:00 2001 From: Anton Parfonov Date: Sat, 14 Dec 2024 12:43:53 +0200 Subject: [PATCH 4/5] Restructure solution --- derive/src/attributes/mod.rs | 2 ++ derive/src/attributes/row.rs | 43 ++++++++++++++++++++++++++++++++++++ derive/src/lib.rs | 42 +++++++++++------------------------ 3 files changed, 58 insertions(+), 29 deletions(-) create mode 100644 derive/src/attributes/mod.rs create mode 100644 derive/src/attributes/row.rs diff --git a/derive/src/attributes/mod.rs b/derive/src/attributes/mod.rs new file mode 100644 index 0000000..d8412dc --- /dev/null +++ b/derive/src/attributes/mod.rs @@ -0,0 +1,2 @@ +mod row; +pub use row::Row; diff --git a/derive/src/attributes/row.rs b/derive/src/attributes/row.rs new file mode 100644 index 0000000..e08fa71 --- /dev/null +++ b/derive/src/attributes/row.rs @@ -0,0 +1,43 @@ +use quote::ToTokens; + +pub const ATTRIBUTE_NAME: &str = "row"; +pub const ATTRIBUTE_SYNTAX: &str = "#[row(crate = ...)]"; + +pub const CRATE_PATH: &str = "crate"; +pub const DEFAULT_CRATE_PATH: &str = "::clickhouse"; + +pub struct Row { + pub crate_path: syn::Path, +} + +impl Default for Row { + fn default() -> Self { + let default_crate_path = syn::parse_str::(DEFAULT_CRATE_PATH).unwrap(); + Self { + crate_path: default_crate_path, + } + } +} + +impl<'a> TryFrom<&'a syn::Attribute> for Row { + type Error = &'a syn::Attribute; + + fn try_from(attr: &'a syn::Attribute) -> Result { + if attr.path().is_ident(ATTRIBUTE_NAME) { + let row = attr.parse_args::().unwrap(); + let syn::Expr::Assign(syn::ExprAssign { left, right, .. }) = row else { + panic!("expected `{}`", ATTRIBUTE_SYNTAX); + }; + if left.to_token_stream().to_string() != CRATE_PATH { + panic!("expected `{}`", ATTRIBUTE_SYNTAX); + } + let syn::Expr::Path(syn::ExprPath { path, .. }) = *right else { + panic!("expected `{}`", ATTRIBUTE_SYNTAX); + }; + Ok(Self { crate_path: path }) + } else { + return Err(attr); + } + } +} + diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 54ca752..807ce83 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,5 +1,7 @@ +mod attributes; + use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; +use quote::quote; use serde_derive_internals::{ attr::{Container, Default as SerdeDefault, Field}, Ctxt, @@ -7,40 +9,20 @@ use serde_derive_internals::{ use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields}; struct Attributes { - crate_path: syn::Path, + row: attributes::Row, } impl From<&[syn::Attribute]> for Attributes { fn from(attrs: &[syn::Attribute]) -> Self { - const ATTRIBUTE_NAME: &str = "row"; - const ATTRIBUTE_SYNTAX: &str = "#[row(crate = ...)]"; - - const CRATE_PATH: &str = "crate"; - const DEFAULT_CRATE_PATH: &str = "clickhouse"; - - let mut crate_name = None; + let mut row = None; for attr in attrs { - if attr.path().is_ident(ATTRIBUTE_NAME) { - let row = attr.parse_args::().unwrap(); - let syn::Expr::Assign(syn::ExprAssign { left, right, .. }) = row else { - panic!("expected `{}`", ATTRIBUTE_SYNTAX); - }; - if left.to_token_stream().to_string() != CRATE_PATH { - panic!("expected `{}`", ATTRIBUTE_SYNTAX); - } - let syn::Expr::Path(syn::ExprPath { path, .. }) = *right else { - panic!("expected `{}`", ATTRIBUTE_SYNTAX); - }; - crate_name = Some(path); + if let Ok(r) = attributes::Row::try_from(attr) { + row = Some(r); } } - let crate_name = crate_name.unwrap_or_else(|| { - syn::Path::from(syn::Ident::new( - DEFAULT_CRATE_PATH, - proc_macro2::Span::call_site(), - )) - }); - Self { crate_path: crate_name } + Self { + row: row.unwrap_or_default(), + } } } @@ -79,7 +61,9 @@ pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); let cx = Ctxt::new(); - let Attributes { crate_path } = Attributes::from(input.attrs.as_slice()); + let Attributes { + row: attributes::Row { crate_path }, + } = Attributes::from(input.attrs.as_slice()); let container = Container::from_ast(&cx, &input); let name = input.ident; From 8357930e218314b8c20a4c1b6b26d16124b6f3ba Mon Sep 17 00:00:00 2001 From: YBoy <85903114+YBoy-git@users.noreply.github.com> Date: Sat, 14 Dec 2024 20:53:24 +0200 Subject: [PATCH 5/5] Remove resolved TODO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Philip Dubé --- derive/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 807ce83..0aa8786 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -55,7 +55,6 @@ fn column_names(data: &DataStruct, cx: &Ctxt, container: &Container) -> TokenStr // TODO: support wrappers `Wrapper(Inner)` and `Wrapper(T)`. // TODO: support the `nested` attribute. -// TODO: support the `crate` attribute. #[proc_macro_derive(Row, attributes(row))] pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput);