common_macro/row/
to_row.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
15use proc_macro2::TokenStream as TokenStream2;
16use quote::quote;
17use syn::spanned::Spanned;
18use syn::{DeriveInput, Result};
19
20use crate::row::utils::{
21    ParsedField, convert_column_data_type_to_value_data_ident, extract_struct_fields,
22    get_column_data_type, parse_fields_from_fields_named,
23};
24use crate::row::{META_KEY_COL, META_KEY_DATATYPE};
25
26pub(crate) fn derive_to_row_impl(input: DeriveInput) -> Result<TokenStream2> {
27    let Some(fields) = extract_struct_fields(&input.data) else {
28        return Err(syn::Error::new(
29            input.span(),
30            "ToRow can only be derived for structs",
31        ));
32    };
33    let ident = input.ident;
34    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
35    let fields = parse_fields_from_fields_named(fields)?;
36
37    // Implement `to_row` method.
38    let impl_to_row_method = impl_to_row_method_combined(&fields)?;
39    Ok(quote! {
40        impl #impl_generics #ident #ty_generics #where_clause {
41            #impl_to_row_method
42        }
43    })
44}
45
46fn impl_to_row_method_combined(fields: &[ParsedField<'_>]) -> Result<TokenStream2> {
47    let value_exprs = fields
48        .iter()
49        .map(|field| {
50            let ParsedField {ident, field_type, column_data_type, column_attribute} = field;
51            let Some(column_data_type) = get_column_data_type(column_data_type, column_attribute)
52            else {
53                return Err(syn::Error::new(
54                    ident.span(),
55                    format!(
56                        "expected to set data type explicitly via [({META_KEY_COL}({META_KEY_DATATYPE} = \"...\"))]"
57                    ),
58                ));
59            };
60            let value_data = convert_column_data_type_to_value_data_ident(&column_data_type.data_type);
61            let expr = if field_type.is_optional() {
62                quote! {
63                    match &self.#ident {
64                        Some(v) => Value {
65                            value_data: Some(ValueData::#value_data(v.clone().into())),
66                        },
67                        None => Value { value_data: None },
68                    }
69                }
70            } else {
71                quote! {
72                    Value {
73                        value_data: Some(ValueData::#value_data(self.#ident.clone().into())),
74                    }
75                }
76            };
77            Ok(expr)
78        })
79        .collect::<Result<Vec<_>>>()?;
80
81    Ok(quote! {
82        pub fn to_row(&self) -> Row {
83            Row {
84                values: vec![ #( #value_exprs ),* ]
85            }
86        }
87    })
88}