Skip to content

Commit ee299f6

Browse files
abonanderAnton ParfonovYBoy-gitserprex
authored
feat(derive): support #[clickhouse(crate = "...")] (#292)
Co-authored-by: Anton Parfonov <[email protected]> Co-authored-by: YBoy <[email protected]> Co-authored-by: Philip Dubé <[email protected]>
1 parent 92926fd commit ee299f6

File tree

9 files changed

+176
-33
lines changed

9 files changed

+176
-33
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
- types: a new crate `clickhouse-types` was added to the project workspace. This crate is required for
3737
`RowBinaryWithNamesAndTypes` struct definition validation, as it contains ClickHouse data types AST, as well as
3838
functions and utilities to parse the types out of the ClickHouse server response. ([#221]).
39+
- derive: added `#[clickhouse(crate = "...")]` attribute for `#[derive(Row)]` ([#189]/[#292])
3940

41+
[#189]: https://github.com/ClickHouse/clickhouse-rs/pull/189
4042
[#221]: https://github.com/ClickHouse/clickhouse-rs/pull/221
4143
[#245]: https://github.com/ClickHouse/clickhouse-rs/pull/245
44+
[#292]: https://github.com/ClickHouse/clickhouse-rs/pull/292
4245

4346
## [0.13.3] - 2025-05-29
4447
### Added

derive/src/attributes.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use syn::meta::ParseNestedMeta;
2+
3+
pub struct Attributes {
4+
pub crate_path: syn::Path,
5+
}
6+
7+
impl Default for Attributes {
8+
fn default() -> Self {
9+
Attributes {
10+
// Note: changing this to `::clickhouse` is likely a breaking change;
11+
// it's possible that the user has renamed the `clickhouse` package,
12+
// but then aliased it back to `clickhouse` to fix the derive.
13+
crate_path: syn::parse_str("clickhouse").expect("BUG: crate_path should parse"),
14+
}
15+
}
16+
}
17+
18+
impl TryFrom<&[syn::Attribute]> for Attributes {
19+
type Error = syn::Error;
20+
21+
fn try_from(attrs: &[syn::Attribute]) -> syn::Result<Self> {
22+
for attr in attrs {
23+
if attr.path().is_ident("clickhouse") {
24+
let mut out = Attributes::default();
25+
26+
attr.parse_nested_meta(|meta| parse_nested_meta(meta, &mut out))?;
27+
28+
return Ok(out);
29+
}
30+
}
31+
32+
Ok(Self::default())
33+
}
34+
}
35+
36+
/// Called for each meta-item inside the `#[clickhouse(...)]` attribute.
37+
fn parse_nested_meta(meta: ParseNestedMeta<'_>, out: &mut Attributes) -> syn::Result<()> {
38+
// #[clickhouse(crate = "<path>")]
39+
if meta.path.is_ident("crate") {
40+
out.crate_path = meta
41+
// Expect and eat the `=` token
42+
.value()?
43+
// Expect a string literal like Serde: https://serde.rs/container-attrs.html#crate
44+
.parse::<syn::LitStr>()?
45+
// Parse the literal content as `Path`
46+
.parse()?;
47+
} else {
48+
return Err(meta.error("unexpected `#[clickhouse(...)]` argument"));
49+
}
50+
51+
Ok(())
52+
}

derive/src/lib.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::attributes::Attributes;
12
use proc_macro2::{Span, TokenStream};
23
use quote::quote;
34
use serde_derive_internals::{
@@ -6,9 +7,21 @@ use serde_derive_internals::{
67
};
78
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Error, Fields, Lifetime, Result};
89

10+
mod attributes;
11+
912
#[cfg(test)]
1013
mod tests;
1114

15+
// TODO: support wrappers `Wrapper(Inner)` and `Wrapper<T>(T)`.
16+
// TODO: support the `nested` attribute.
17+
#[proc_macro_derive(Row, attributes(clickhouse))]
18+
pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
19+
let input = parse_macro_input!(input as DeriveInput);
20+
row_impl(input)
21+
.unwrap_or_else(Error::into_compile_error)
22+
.into()
23+
}
24+
1225
fn column_names(data: &DataStruct, cx: &Ctxt, container: &Container) -> Result<TokenStream> {
1326
Ok(match &data.fields {
1427
Fields::Named(fields) => {
@@ -36,19 +49,11 @@ fn column_names(data: &DataStruct, cx: &Ctxt, container: &Container) -> Result<T
3649
})
3750
}
3851

39-
// TODO: support wrappers `Wrapper(Inner)` and `Wrapper<T>(T)`.
40-
// TODO: support the `nested` attribute.
41-
// TODO: support the `crate` attribute.
42-
#[proc_macro_derive(Row)]
43-
pub fn row(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
44-
let input = parse_macro_input!(input as DeriveInput);
45-
row_impl(input)
46-
.unwrap_or_else(Error::into_compile_error)
47-
.into()
48-
}
49-
5052
fn row_impl(input: DeriveInput) -> Result<TokenStream> {
5153
let cx = Ctxt::new();
54+
55+
let Attributes { crate_path } = input.attrs[..].try_into()?;
56+
5257
let container = Container::from_ast(&cx, &input);
5358
let name = input.ident;
5459

@@ -89,14 +94,13 @@ fn row_impl(input: DeriveInput) -> Result<TokenStream> {
8994

9095
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
9196

92-
// TODO: replace `clickhouse` with `::clickhouse` here.
9397
Ok(quote! {
9498
#[automatically_derived]
95-
impl #impl_generics clickhouse::Row for #name #ty_generics #where_clause {
99+
impl #impl_generics #crate_path::Row for #name #ty_generics #where_clause {
96100
const NAME: &'static str = stringify!(#name);
97101
const COLUMN_NAMES: &'static [&'static str] = #column_names;
98-
const COLUMN_COUNT: usize = <Self as clickhouse::Row>::COLUMN_NAMES.len();
99-
const KIND: clickhouse::_priv::RowKind = clickhouse::_priv::RowKind::Struct;
102+
const COLUMN_COUNT: usize = <Self as #crate_path::Row>::COLUMN_NAMES.len();
103+
const KIND: #crate_path::_priv::RowKind = #crate_path::_priv::RowKind::Struct;
100104

101105
type Value<'__v> = #value;
102106
}

derive/src/tests/cases.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fn simple_owned_row() {
1414
a: i32,
1515
b: String,
1616
}
17-
};
17+
}
1818
}
1919

2020
#[test]
@@ -25,23 +25,23 @@ fn generic_owned_row() {
2525
a: i32,
2626
b: T,
2727
}
28-
};
28+
}
2929

3030
render! {
3131
#[derive(Row)]
3232
struct Sample<A, B> {
3333
a: A,
3434
b: B,
3535
}
36-
};
36+
}
3737

3838
render! {
3939
#[derive(Row)]
4040
struct Sample<T> where T: Clone {
4141
a: i32,
4242
b: T,
4343
}
44-
};
44+
}
4545
}
4646

4747
#[test]
@@ -52,7 +52,7 @@ fn simple_borrowed_row() {
5252
a: i32,
5353
b: &'a str,
5454
}
55-
};
55+
}
5656
}
5757

5858
#[test]
@@ -63,23 +63,23 @@ fn generic_borrowed_row() {
6363
a: i32,
6464
b: &'a T,
6565
}
66-
};
66+
}
6767

6868
render! {
6969
#[derive(Row)]
7070
struct Sample<'a, A, B> {
7171
a: A,
7272
b: &'a B,
7373
}
74-
};
74+
}
7575

7676
render! {
7777
#[derive(Row)]
7878
struct Sample<'a, T> where T: Clone {
7979
a: i32,
8080
b: &'a T,
8181
}
82-
};
82+
}
8383
}
8484

8585
#[test]
@@ -93,7 +93,7 @@ fn serde_rename() {
9393
#[serde(rename = "items.b")]
9494
items_b: Vec<u32>,
9595
}
96-
};
96+
}
9797
}
9898

9999
#[test]
@@ -105,7 +105,7 @@ fn serde_skip_serializing() {
105105
#[serde(skip_serializing)]
106106
b: u32,
107107
}
108-
};
108+
}
109109
}
110110

111111
#[test]
@@ -117,5 +117,17 @@ fn serde_skip_deserializing() {
117117
#[serde(skip_deserializing)]
118118
b: u32,
119119
}
120-
};
120+
}
121+
}
122+
123+
#[test]
124+
fn crate_attribute() {
125+
render! {
126+
#[derive(Row)]
127+
#[clickhouse(crate = "foo")]
128+
struct Foo {
129+
a: u32,
130+
b: u32,
131+
}
132+
}
121133
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
source: derive/src/tests/cases.rs
3+
assertion_line: 125
4+
---
5+
#[derive(Row)]
6+
#[clickhouse(crate = "foo")]
7+
struct Foo {
8+
a: u32,
9+
b: u32,
10+
}
11+
12+
/****** GENERATED ******/
13+
#[automatically_derived]
14+
impl foo::Row for Foo {
15+
const NAME: &'static str = stringify!(Foo);
16+
const COLUMN_NAMES: &'static [&'static str] = &["a", "b"];
17+
const COLUMN_COUNT: usize = <Self as foo::Row>::COLUMN_NAMES.len();
18+
const KIND: foo::_priv::RowKind = foo::_priv::RowKind::Struct;
19+
type Value<'__v> = Self;
20+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ pub use self::{
1010
row::{Row, RowOwned, RowRead, RowWrite},
1111
};
1212
use self::{error::Result, http_client::HttpClient};
13+
14+
#[doc = include_str!("row_derive.md")]
1315
pub use clickhouse_derive::Row;
16+
1417
use std::{collections::HashMap, fmt::Display, sync::Arc};
1518

1619
pub mod error;

src/row.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ pub enum RowKind {
1313
/// Represents a row that can be used in queries.
1414
///
1515
/// Implemented for:
16-
/// * All `#[derive(Row)]` items
16+
/// * All [`#[derive(Row)]`][row-derive] items
1717
/// * `(P1, P2, ...)` where P* is a primitive type or string
1818
///
19-
/// Do not implement this trait directly, use `#[derive(Row)]` instead.
19+
/// Do not implement this trait directly, use [`#[derive(Row)]`][row-derive] instead.
2020
///
2121
/// In order to write a generic code over rows, check
2222
/// * [`RowRead`] for reading queries.
2323
/// * [`RowWrite`] for writing queries.
2424
/// * [`RowOwned`] for rows that do not hold any references.
25+
///
26+
/// [row-derive]: derive@crate::Row
2527
pub trait Row {
2628
// NOTE: all properties are unstable and, hence, not following semver.
2729

@@ -277,21 +279,21 @@ pub(crate) fn join_column_names<R: Row>() -> Option<String> {
277279

278280
#[cfg(test)]
279281
mod tests {
280-
// XXX: need for `derive(Row)`. Provide `row(crate = ..)` instead.
281-
use crate as clickhouse;
282-
use clickhouse::Row;
282+
use crate::Row;
283283

284284
use super::*;
285285

286286
#[test]
287287
fn it_grabs_simple_struct() {
288288
#[derive(Row)]
289+
#[clickhouse(crate = "crate")]
289290
#[allow(dead_code)]
290291
struct Simple1 {
291292
one: u32,
292293
}
293294

294295
#[derive(Row)]
296+
#[clickhouse(crate = "crate")]
295297
#[allow(dead_code)]
296298
struct Simple2 {
297299
one: u32,
@@ -305,6 +307,7 @@ mod tests {
305307
#[test]
306308
fn it_grabs_mix() {
307309
#[derive(Row)]
310+
#[clickhouse(crate = "crate")]
308311
struct SomeRow {
309312
_a: u32,
310313
}
@@ -317,6 +320,7 @@ mod tests {
317320
use serde::Serialize;
318321

319322
#[derive(Row, Serialize)]
323+
#[clickhouse(crate = "crate")]
320324
#[allow(dead_code)]
321325
struct TopLevel {
322326
#[serde(rename = "two")]
@@ -331,6 +335,7 @@ mod tests {
331335
use serde::Serialize;
332336

333337
#[derive(Row, Serialize)]
338+
#[clickhouse(crate = "crate")]
334339
#[allow(dead_code)]
335340
struct TopLevel {
336341
one: u32,
@@ -346,6 +351,7 @@ mod tests {
346351
use serde::Deserialize;
347352

348353
#[derive(Row, Deserialize)]
354+
#[clickhouse(crate = "crate")]
349355
#[allow(dead_code)]
350356
struct TopLevel {
351357
one: u32,
@@ -360,6 +366,7 @@ mod tests {
360366
fn it_rejects_other() {
361367
#[allow(dead_code)]
362368
#[derive(Row)]
369+
#[clickhouse(crate = "crate")]
363370
struct NamedTuple(u32, u32);
364371

365372
assert_eq!(join_column_names::<u32>(), None);
@@ -372,6 +379,7 @@ mod tests {
372379
use serde::Serialize;
373380

374381
#[derive(Row, Serialize)]
382+
#[clickhouse(crate = "crate")]
375383
#[allow(dead_code)]
376384
struct MyRow {
377385
r#type: u32,

0 commit comments

Comments
 (0)