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

derive: Support #[row(crate = ...)] #189

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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 derive/src/attributes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod row;
pub use row::Row;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub(crate) (and all below). It seems this subcrate doesn't use https://github.com/ClickHouse/clickhouse-rs/blob/main/Cargo.toml#L17

43 changes: 43 additions & 0 deletions derive/src/attributes/row.rs
Original file line number Diff line number Diff line change
@@ -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::<syn::Path>(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<Self, Self::Error> {
if attr.path().is_ident(ATTRIBUTE_NAME) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not very important (because it's a private API), but it's more straightforward to return an error (even a simple string) and panic in the From instance.

let row = attr.parse_args::<syn::Expr>().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);
}
}
}

28 changes: 25 additions & 3 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod attributes;

use proc_macro2::TokenStream;
use quote::quote;
use serde_derive_internals::{
Expand All @@ -6,6 +8,24 @@ use serde_derive_internals::{
};
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields};

struct Attributes {
row: attributes::Row,
}

impl From<&[syn::Attribute]> for Attributes {
fn from(attrs: &[syn::Attribute]) -> Self {
let mut row = None;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is point to use None here instead of Row::default()?

for attr in attrs {
if let Ok(r) = attributes::Row::try_from(attr) {
row = Some(r);
}
}
Self {
row: row.unwrap_or_default(),
}
}
}

fn column_names(data: &DataStruct, cx: &Ctxt, container: &Container) -> TokenStream {
match &data.fields {
Fields::Named(fields) => {
Expand Down Expand Up @@ -36,11 +56,14 @@ fn column_names(data: &DataStruct, cx: &Ctxt, container: &Container) -> TokenStr
// TODO: support wrappers `Wrapper(Inner)` and `Wrapper<T>(T)`.
// TODO: support the `nested` attribute.
// TODO: support the `crate` attribute.
YBoy-git marked this conversation as resolved.
Show resolved Hide resolved
#[proc_macro_derive(Row)]
#[proc_macro_derive(Row, attributes(row))]
pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch deserves a docstring

let input = parse_macro_input!(input as DeriveInput);

let cx = Ctxt::new();
let Attributes {
row: attributes::Row { crate_path },
} = Attributes::from(input.attrs.as_slice());
let container = Container::from_ast(&cx, &input);
let name = input.ident;

Expand All @@ -54,10 +77,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 #crate_path::Row for #name #ty_generics #where_clause {
const COLUMN_NAMES: &'static [&'static str] = #column_names;
}
};
Expand Down
12 changes: 9 additions & 3 deletions src/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,21 @@ pub(crate) fn join_column_names<R: Row>() -> Option<String> {

#[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,
Expand All @@ -103,6 +103,7 @@ mod tests {
#[test]
fn it_grabs_mix() {
#[derive(Row)]
#[row(crate = crate)]
struct SomeRow {
_a: u32,
}
Expand All @@ -115,6 +116,7 @@ mod tests {
use serde::Serialize;

#[derive(Row, Serialize)]
#[row(crate = crate)]
#[allow(dead_code)]
struct TopLevel {
#[serde(rename = "two")]
Expand All @@ -129,6 +131,7 @@ mod tests {
use serde::Serialize;

#[derive(Row, Serialize)]
#[row(crate = crate)]
#[allow(dead_code)]
struct TopLevel {
one: u32,
Expand All @@ -144,6 +147,7 @@ mod tests {
use serde::Deserialize;

#[derive(Row, Deserialize)]
#[row(crate = crate)]
#[allow(dead_code)]
struct TopLevel {
one: u32,
Expand All @@ -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::<u32>(), None);
Expand All @@ -170,6 +175,7 @@ mod tests {
use serde::Serialize;

#[derive(Row, Serialize)]
#[row(crate = crate)]
#[allow(dead_code)]
struct MyRow {
r#type: u32,
Expand Down
4 changes: 2 additions & 2 deletions src/sql/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,19 @@ 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,
}

#[allow(unused)]
#[derive(Row)]
#[row(crate = crate)]
struct Unnamed(u32, u32);

#[test]
Expand Down
Loading