common_macro/
lib.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15mod admin_fn;
16mod aggr_func;
17mod print_caller;
18mod range_fn;
19mod row;
20mod stack_trace_debug;
21mod utils;
22
23use aggr_func::{impl_aggr_func_type_store, impl_as_aggr_func_creator};
24use print_caller::process_print_caller;
25use proc_macro::TokenStream;
26use quote::quote;
27use range_fn::process_range_fn;
28use syn::{Data, DeriveInput, Fields, parse_macro_input};
29
30use crate::admin_fn::process_admin_fn;
31use crate::row::into_row::derive_into_row_impl;
32use crate::row::schema::derive_schema_impl;
33use crate::row::to_row::derive_to_row_impl;
34
35/// Make struct implemented trait [AggrFuncTypeStore], which is necessary when writing UDAF.
36/// This derive macro is expect to be used along with attribute macro [macro@as_aggr_func_creator].
37#[proc_macro_derive(AggrFuncTypeStore)]
38pub fn aggr_func_type_store_derive(input: TokenStream) -> TokenStream {
39    let ast = parse_macro_input!(input as DeriveInput);
40    impl_aggr_func_type_store(&ast)
41}
42
43/// A struct can be used as a creator for aggregate function if it has been annotated with this
44/// attribute first.
45///
46/// This attribute add a necessary field which is intended to store the input
47/// data's types to the struct.
48/// This attribute is expected to be used along with derive macro [AggrFuncTypeStore].
49#[proc_macro_attribute]
50pub fn as_aggr_func_creator(args: TokenStream, input: TokenStream) -> TokenStream {
51    impl_as_aggr_func_creator(args, input)
52}
53
54/// Attribute macro to convert an arithimetic function to a range function. The annotated function
55/// should accept servaral arrays as input and return a single value as output.
56///
57/// This procedure macro can works on any number of input parameters. Return type can be either
58/// primitive type or wrapped in `Option`.
59///
60/// # Example
61/// Take `count_over_time()` in PromQL as an example:
62/// ```rust, ignore
63/// /// The count of all values in the specified interval.
64/// #[range_fn(
65///     name = "CountOverTime",
66///     ret = "Float64Array",
67///     display_name = "prom_count_over_time"
68/// )]
69/// pub fn count_over_time(_: &TimestampMillisecondArray, values: &Float64Array) -> f64 {
70///      values.len() as f64
71/// }
72/// ```
73///
74/// # Arguments
75/// - `name`: The name of the generated `ScalarUDF` struct.
76/// - `ret`: The return type of the generated UDF function.
77/// - `display_name`: The display name of the generated UDF function.
78#[proc_macro_attribute]
79pub fn range_fn(args: TokenStream, input: TokenStream) -> TokenStream {
80    process_range_fn(args, input)
81}
82
83/// Attribute macro to convert a normal function to SQL administration function. The annotated function
84/// should accept:
85///    - `&ProcedureServiceHandlerRef` or `&TableMutationHandlerRef` or `FlowServiceHandlerRef` as the first argument,
86///    - `&QueryContextRef` as the second argument, and
87///    - `&[ValueRef<'_>]` as the third argument which is SQL function input values in each row.
88///
89/// Return type must be `common_query::error::Result<Value>`.
90///
91/// # Example see `common/function/src/system/procedure_state.rs`.
92///
93/// # Arguments
94/// - `name`: The name of the generated `Function` implementation.
95/// - `ret`: The return type of the generated SQL function, it will be transformed into `ConcreteDataType::{ret}_datatype()` result.
96/// - `display_name`: The display name of the generated SQL function.
97/// - `sig_fn`: the function to returns `Signature` of generated `Function`.
98/// - `user_path`: Optional path to the trait and context (e.g., `crate`);
99///   defaults to `crate` if not provided.
100#[proc_macro_attribute]
101pub fn admin_fn(args: TokenStream, input: TokenStream) -> TokenStream {
102    process_admin_fn(args, input)
103}
104
105/// Attribute macro to print the caller to the annotated function.
106/// The caller is printed as its filename and the call site line number.
107///
108/// This macro works like this: inject the tracking codes as the first statement to the annotated
109/// function body. The tracking codes use [backtrace-rs](https://crates.io/crates/backtrace) to get
110/// the callers. So you must dependent on the `backtrace-rs` crate.
111///
112/// # Arguments
113/// - `depth`: The max depth of call stack to print. Optional, defaults to 1.
114///
115/// # Example
116/// ```rust, ignore
117///
118/// #[print_caller(depth = 3)]
119/// fn foo() {}
120/// ```
121#[proc_macro_attribute]
122pub fn print_caller(args: TokenStream, input: TokenStream) -> TokenStream {
123    process_print_caller(args, input)
124}
125
126/// Attribute macro to derive [std::fmt::Debug] for the annotated `Error` type.
127///
128/// The generated `Debug` implementation will print the error in a stack trace style. E.g.:
129/// ```plaintext
130/// 0: Foo error, at src/common/catalog/src/error.rs:80:10
131/// 1: Bar error, at src/common/function/src/error.rs:90:10
132/// 2: Root cause, invalid table name, at src/common/catalog/src/error.rs:100:10
133/// ```
134///
135/// Notes on using this macro:
136/// - `#[snafu(display)]` must present on each enum variants,
137///   and should not include `location` and `source`.
138/// - Only our internal error can be named `source`.
139///   All external error should be `error` with an `#[snafu(source)]` annotation.
140/// - `common_error` crate must be accessible.
141#[proc_macro_attribute]
142pub fn stack_trace_debug(args: TokenStream, input: TokenStream) -> TokenStream {
143    stack_trace_debug::stack_trace_style_impl(args.into(), input.into()).into()
144}
145
146/// Generates implementation for `From<&TableMeta> for TableMetaBuilder`
147#[proc_macro_derive(ToMetaBuilder)]
148pub fn derive_meta_builder(input: TokenStream) -> TokenStream {
149    let input = parse_macro_input!(input as DeriveInput);
150
151    let Data::Struct(data_struct) = input.data else {
152        panic!("ToMetaBuilder can only be derived for structs");
153    };
154
155    let Fields::Named(fields) = data_struct.fields else {
156        panic!("ToMetaBuilder can only be derived for structs with named fields");
157    };
158
159    // Check that this is being applied to TableMeta struct
160    if input.ident != "TableMeta" {
161        panic!("ToMetaBuilder can only be derived for TableMeta struct");
162    }
163
164    let field_init = fields.named.iter().map(|field| {
165        let field_name = field.ident.as_ref().unwrap();
166        quote! {
167            #field_name: Default::default(),
168        }
169    });
170
171    let field_assignments = fields.named.iter().map(|field| {
172        let field_name = field.ident.as_ref().unwrap();
173        quote! {
174            builder.#field_name(meta.#field_name.clone());
175        }
176    });
177
178    let generated = quote! {
179        impl From<&TableMeta> for TableMetaBuilder {
180            fn from(meta: &TableMeta) -> Self {
181                let mut builder = Self {
182                    #(#field_init)*
183                };
184
185                #(#field_assignments)*
186                builder
187            }
188        }
189    };
190
191    generated.into()
192}
193
194/// Derive macro to convert a struct to a row.
195///
196/// # Example
197/// ```rust, ignore
198/// use api::v1::Row;
199/// use api::v1::value::ValueData;
200/// use api::v1::Value;
201///
202/// #[derive(ToRow)]
203/// struct ToRowTest {
204///     my_value: i32,
205///     #[col(name = "string_value", datatype = "string", semantic = "tag")]
206///     my_string: String,  
207///     my_bool: bool,
208///     my_float: f32,
209///     #[col(
210///         name = "timestamp_value",
211///         semantic = "Timestamp",
212///         datatype = "TimestampMillisecond"
213///     )]
214///     my_timestamp: i64,
215///     #[col(skip)]
216///     my_skip: i32,
217/// }
218///
219/// let row = ToRowTest {
220///     my_value: 1,
221///     my_string: "test".to_string(),
222///     my_bool: true,
223///     my_float: 1.0,
224///     my_timestamp: 1718563200000,
225///     my_skip: 1,
226/// }.to_row();
227/// ```
228#[proc_macro_derive(ToRow, attributes(col))]
229pub fn derive_to_row(input: TokenStream) -> TokenStream {
230    let input = parse_macro_input!(input as DeriveInput);
231    let output = derive_to_row_impl(input);
232    output.unwrap_or_else(|e| e.to_compile_error()).into()
233}
234
235/// Derive macro to convert a struct to a row with move semantics.
236///
237/// # Example
238/// ```rust, ignore
239/// use api::v1::Row;
240/// use api::v1::value::ValueData;
241/// use api::v1::Value;
242///
243/// #[derive(IntoRow)]
244/// struct IntoRowTest {
245///     my_value: i32,
246///     #[col(name = "string_value", datatype = "string", semantic = "tag")]
247///     my_string: String,  
248///     my_bool: bool,
249///     my_float: f32,
250///     #[col(
251///         name = "timestamp_value",
252///         semantic = "Timestamp",
253///         datatype = "TimestampMillisecond"
254///     )]
255///     my_timestamp: i64,
256///     #[col(skip)]
257///     my_skip: i32,
258/// }
259///
260/// let row = IntoRowTest {
261///     my_value: 1,
262///     my_string: "test".to_string(),
263///     my_bool: true,
264///     my_float: 1.0,
265///     my_timestamp: 1718563200000,
266///     my_skip: 1,
267/// }.into_row();
268/// ```
269#[proc_macro_derive(IntoRow, attributes(col))]
270pub fn derive_into_row(input: TokenStream) -> TokenStream {
271    let input = parse_macro_input!(input as DeriveInput);
272    let output = derive_into_row_impl(input);
273    output.unwrap_or_else(|e| e.to_compile_error()).into()
274}
275
276/// Derive macro to convert a struct to a schema.
277///
278/// # Example
279/// ```rust, ignore
280/// use api::v1::ColumnSchema;
281///
282/// #[derive(Schema)]
283/// struct SchemaTest {
284///     my_value: i32,
285///     #[col(name = "string_value", datatype = "string", semantic = "tag")]
286///     my_string: String,  
287///     my_bool: bool,
288///     my_float: f32,
289///     #[col(
290///         name = "timestamp_value",
291///         semantic = "Timestamp",
292///         datatype = "TimestampMillisecond"
293///     )]
294///     my_timestamp: i64,
295///     #[col(skip)]
296///     my_skip: i32,
297/// }
298///
299/// let schema = SchemaTest::schema();
300/// ```
301#[proc_macro_derive(Schema, attributes(col))]
302pub fn derive_schema(input: TokenStream) -> TokenStream {
303    let input = parse_macro_input!(input as DeriveInput);
304    let output = derive_schema_impl(input);
305    output.unwrap_or_else(|e| e.to_compile_error()).into()
306}