common_macro/row/
attribute.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 std::collections::HashMap;
16
17use once_cell::sync::Lazy;
18use syn::meta::ParseNestedMeta;
19use syn::spanned::Spanned;
20use syn::{Attribute, LitStr, Meta, Result};
21
22use crate::row::utils::{
23    ColumnDataTypeWithExtension, SemanticType, column_data_type_from_str, semantic_type_from_str,
24};
25use crate::row::{
26    META_KEY_COL, META_KEY_DATATYPE, META_KEY_NAME, META_KEY_SEMANTIC, META_KEY_SKIP,
27};
28
29/// Column attribute.
30#[derive(Default)]
31pub(crate) struct ColumnAttribute {
32    /// User-defined name of the column.,
33    pub(crate) name: Option<String>,
34    /// Data type of the column.
35    pub(crate) datatype: Option<ColumnDataTypeWithExtension>,
36    /// Semantic type of the column.
37    pub(crate) semantic_type: SemanticType,
38    /// Whether to skip the column.
39    pub(crate) skip: bool,
40}
41
42/// Find the column attribute in the attributes.
43pub(crate) fn find_column_attribute(attrs: &[Attribute]) -> Option<&Attribute> {
44    attrs
45        .iter()
46        .find(|attr| matches!(&attr.meta, Meta::List(list) if list.path.is_ident(META_KEY_COL)))
47}
48
49/// Parse the column attribute.
50pub(crate) fn parse_column_attribute(attr: &Attribute) -> Result<ColumnAttribute> {
51    match &attr.meta {
52        Meta::List(list) if list.path.is_ident(META_KEY_COL) => {
53            let mut attribute = ColumnAttribute::default();
54            list.parse_nested_meta(|meta| parse_column_attribute_field(&meta, &mut attribute))?;
55            Ok(attribute)
56        }
57        _ => Err(syn::Error::new(
58            attr.span(),
59            format!(
60                "expected `{META_KEY_COL}({META_KEY_NAME} = \"...\", {META_KEY_DATATYPE} = \"...\", {META_KEY_SEMANTIC} = \"...\")`"
61            ),
62        )),
63    }
64}
65
66type ParseColumnAttributeField = fn(&ParseNestedMeta, &mut ColumnAttribute) -> Result<()>;
67
68static PARSE_COLUMN_ATTRIBUTE_FIELDS: Lazy<HashMap<&str, ParseColumnAttributeField>> =
69    Lazy::new(|| {
70        HashMap::from([
71            (META_KEY_NAME, parse_name_field as _),
72            (META_KEY_DATATYPE, parse_datatype_field as _),
73            (META_KEY_SEMANTIC, parse_semantic_field as _),
74            (META_KEY_SKIP, parse_skip_field as _),
75        ])
76    });
77
78fn parse_name_field(meta: &ParseNestedMeta<'_>, attribute: &mut ColumnAttribute) -> Result<()> {
79    let value = meta.value()?;
80    let s: LitStr = value.parse()?;
81    attribute.name = Some(s.value());
82    Ok(())
83}
84
85fn parse_datatype_field(meta: &ParseNestedMeta<'_>, attribute: &mut ColumnAttribute) -> Result<()> {
86    let value = meta.value()?;
87    let s: LitStr = value.parse()?;
88    let ident = s.value();
89    let Some(value) = column_data_type_from_str(&ident) else {
90        return Err(meta.error(format!("unexpected {META_KEY_DATATYPE}: {ident}")));
91    };
92    attribute.datatype = Some(value);
93    Ok(())
94}
95
96fn parse_semantic_field(meta: &ParseNestedMeta<'_>, attribute: &mut ColumnAttribute) -> Result<()> {
97    let value = meta.value()?;
98    let s: LitStr = value.parse()?;
99    let ident = s.value();
100    let Some(value) = semantic_type_from_str(&ident) else {
101        return Err(meta.error(format!("unexpected {META_KEY_SEMANTIC}: {ident}")));
102    };
103    attribute.semantic_type = value;
104    Ok(())
105}
106
107fn parse_skip_field(_: &ParseNestedMeta<'_>, attribute: &mut ColumnAttribute) -> Result<()> {
108    attribute.skip = true;
109    Ok(())
110}
111
112fn parse_column_attribute_field(
113    meta: &ParseNestedMeta<'_>,
114    attribute: &mut ColumnAttribute,
115) -> Result<()> {
116    let Some(ident) = meta.path.get_ident() else {
117        return Err(meta.error(format!("expected `{META_KEY_COL}({META_KEY_NAME} = \"...\", {META_KEY_DATATYPE} = \"...\", {META_KEY_SEMANTIC} = \"...\")`")));
118    };
119    let Some(parse_column_attribute) =
120        PARSE_COLUMN_ATTRIBUTE_FIELDS.get(ident.to_string().as_str())
121    else {
122        return Err(meta.error(format!("unexpected attribute: {ident}")));
123    };
124
125    parse_column_attribute(meta, attribute)
126}