use darling::{FromDeriveInput, FromMeta};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(source), supports(struct_named))]
struct SourceAttrs {
ident: syn::Ident,
#[darling(default)]
#[allow(dead_code)]
name: Option<String>,
#[darling(default)]
#[allow(dead_code)]
version: Option<String>,
#[darling(default)]
#[allow(dead_code)]
description: Option<String>,
#[darling(default)]
#[allow(dead_code)]
author: Option<String>,
#[darling(default)]
#[allow(dead_code)]
license: Option<String>,
#[darling(default)]
#[allow(dead_code)]
documentation_url: Option<String>,
#[darling(default)]
#[allow(dead_code)]
incremental: bool,
#[darling(default)]
#[allow(dead_code)]
source_types: Option<SourceTypesAttr>,
}
#[derive(Debug, Default, FromMeta)]
#[allow(dead_code)]
struct SourceTypesAttr {
full_refresh: bool,
incremental: bool,
}
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(sink), supports(struct_named))]
struct SinkAttrs {
ident: syn::Ident,
#[darling(default)]
#[allow(dead_code)]
name: Option<String>,
#[darling(default)]
#[allow(dead_code)]
version: Option<String>,
#[darling(default)]
#[allow(dead_code)]
description: Option<String>,
#[darling(default)]
#[allow(dead_code)]
author: Option<String>,
#[darling(default)]
#[allow(dead_code)]
license: Option<String>,
#[darling(default)]
#[allow(dead_code)]
documentation_url: Option<String>,
#[darling(default)]
#[allow(dead_code)]
batching: bool,
#[darling(default)]
#[allow(dead_code)]
batch_size: Option<usize>,
}
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(transform), supports(struct_named))]
struct TransformAttrs {
ident: syn::Ident,
#[darling(default)]
#[allow(dead_code)]
name: Option<String>,
#[darling(default)]
#[allow(dead_code)]
version: Option<String>,
#[darling(default)]
#[allow(dead_code)]
description: Option<String>,
}
#[proc_macro_derive(SourceConfig, attributes(source))]
pub fn derive_source_config(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let attrs = match SourceAttrs::from_derive_input(&input) {
Ok(v) => v,
Err(e) => return TokenStream::from(e.write_errors()),
};
let struct_name = &attrs.ident;
let spec_struct_name = quote::format_ident!("{}Spec", struct_name);
let name = attrs.name.unwrap_or_else(|| {
let name = struct_name.to_string();
name.strip_suffix("Config").unwrap_or(&name).to_lowercase()
});
let version_code = match &attrs.version {
Some(v) => quote! { #v },
None => quote! { match option_env!("CARGO_PKG_VERSION") {
Some(v) => v,
None => "0.0.1",
} },
};
let description_code = match &attrs.description {
Some(desc) => quote! { .description(#desc) },
None => quote! {},
};
let author_code = match &attrs.author {
Some(author) => quote! { .author(#author) },
None => quote! {},
};
let license_code = match &attrs.license {
Some(license) => quote! { .license(#license) },
None => quote! {},
};
let doc_url_code = match &attrs.documentation_url {
Some(url) => quote! { .documentation_url(#url) },
None => quote! {},
};
let incremental_code = if attrs.incremental {
quote! { .incremental(true) }
} else {
quote! {}
};
let expanded = quote! {
pub struct #spec_struct_name;
impl #spec_struct_name {
pub fn spec() -> rivven_connect::ConnectorSpec {
rivven_connect::ConnectorSpec::builder(#name, #version_code)
#description_code
#author_code
#license_code
#doc_url_code
#incremental_code
.config_schema::<#struct_name>()
.build()
}
pub const fn name() -> &'static str {
#name
}
pub fn version() -> &'static str {
#version_code
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_derive(SinkConfig, attributes(sink))]
pub fn derive_sink_config(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let attrs = match SinkAttrs::from_derive_input(&input) {
Ok(v) => v,
Err(e) => return TokenStream::from(e.write_errors()),
};
let struct_name = &attrs.ident;
let spec_struct_name = quote::format_ident!("{}Spec", struct_name);
let name = attrs.name.unwrap_or_else(|| {
let name = struct_name.to_string();
name.strip_suffix("Config").unwrap_or(&name).to_lowercase()
});
let version_code = match &attrs.version {
Some(v) => quote! { #v },
None => quote! { match option_env!("CARGO_PKG_VERSION") {
Some(v) => v,
None => "0.0.1",
} },
};
let description_code = match &attrs.description {
Some(desc) => quote! { .description(#desc) },
None => quote! {},
};
let author_code = match &attrs.author {
Some(author) => quote! { .author(#author) },
None => quote! {},
};
let license_code = match &attrs.license {
Some(license) => quote! { .license(#license) },
None => quote! {},
};
let doc_url_code = match &attrs.documentation_url {
Some(url) => quote! { .documentation_url(#url) },
None => quote! {},
};
let batch_config_code = if attrs.batching {
let batch_size = attrs.batch_size.unwrap_or(10_000);
quote! {
pub fn batch_config() -> rivven_connect::BatchConfig {
rivven_connect::BatchConfig {
max_records: #batch_size,
..Default::default()
}
}
}
} else {
quote! {}
};
let expanded = quote! {
pub struct #spec_struct_name;
impl #spec_struct_name {
pub fn spec() -> rivven_connect::ConnectorSpec {
rivven_connect::ConnectorSpec::builder(#name, #version_code)
#description_code
#author_code
#license_code
#doc_url_code
.config_schema::<#struct_name>()
.build()
}
pub const fn name() -> &'static str {
#name
}
pub fn version() -> &'static str {
#version_code
}
#batch_config_code
}
};
TokenStream::from(expanded)
}
#[proc_macro_derive(TransformConfig, attributes(transform))]
pub fn derive_transform_config(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let attrs = match TransformAttrs::from_derive_input(&input) {
Ok(v) => v,
Err(e) => return TokenStream::from(e.write_errors()),
};
let struct_name = &attrs.ident;
let spec_struct_name = quote::format_ident!("{}Spec", struct_name);
let name = attrs.name.unwrap_or_else(|| {
let name = struct_name.to_string();
name.strip_suffix("Config").unwrap_or(&name).to_lowercase()
});
let version_code = match &attrs.version {
Some(v) => quote! { #v },
None => quote! { match option_env!("CARGO_PKG_VERSION") {
Some(v) => v,
None => "0.0.1",
} },
};
let description_code = match attrs.description {
Some(desc) => quote! { .description(#desc) },
None => quote! {},
};
let expanded = quote! {
pub struct #spec_struct_name;
impl #spec_struct_name {
pub fn spec() -> rivven_connect::ConnectorSpec {
rivven_connect::ConnectorSpec::builder(#name, #version_code)
#description_code
.config_schema::<#struct_name>()
.build()
}
pub const fn name() -> &'static str {
#name
}
pub fn version() -> &'static str {
#version_code
}
}
};
TokenStream::from(expanded)
}
#[derive(Debug, Default, FromMeta)]
struct ConnectorSpecAttrs {
#[darling(default)]
name: Option<String>,
#[darling(default)]
#[allow(dead_code)]
version: Option<String>,
#[darling(default)]
#[allow(dead_code)]
description: Option<String>,
#[darling(default)]
#[allow(dead_code)]
documentation_url: Option<String>,
}
#[proc_macro_attribute]
pub fn connector_spec(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr_args = match darling::ast::NestedMeta::parse_meta_list(attr.into()) {
Ok(v) => v,
Err(e) => return TokenStream::from(darling::Error::from(e).write_errors()),
};
let attrs = match ConnectorSpecAttrs::from_list(&attr_args) {
Ok(v) => v,
Err(e) => return TokenStream::from(e.write_errors()),
};
let name = match attrs.name {
Some(n) => n,
None => {
return TokenStream::from(
syn::Error::new(
proc_macro2::Span::call_site(),
"connector_spec requires `name = \"...\"` attribute",
)
.to_compile_error(),
);
}
};
let version_code = match &attrs.version {
Some(v) => quote! { #v },
None => quote! { match option_env!("CARGO_PKG_VERSION") {
Some(v) => v,
None => "0.0.1",
} },
};
let description_code = match attrs.description {
Some(desc) => quote! { .description(#desc) },
None => quote! {},
};
let doc_url_code = match attrs.documentation_url {
Some(url) => quote! { .documentation_url(#url) },
None => quote! {},
};
let item: proc_macro2::TokenStream = item.into();
let expanded = quote! {
#item
pub fn connector_spec() -> rivven_connect::ConnectorSpec {
rivven_connect::ConnectorSpec::builder(#name, #version_code)
#description_code
#doc_url_code
.build()
}
};
TokenStream::from(expanded)
}
#[cfg(test)]
mod tests {
#[test]
fn test_derives_compile() {
}
}