Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "f4f75ea470ce25572f3d3c856d6ebf1b10d6f62f"
},
"path_in_vcs": "sea-query-derive"
}
+385
View File
@@ -0,0 +1,385 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hashbrown"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "inherent"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c727f80bfa4a6c6e2508d2f05b6f4bfce242030bd88ed15ae5331c5b5d30fba7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "sea-query"
version = "1.0.0-rc.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7e01d76c67146262612a9db50cbc4b4b3236a96a1f04f1744ef2582234187e2"
dependencies = [
"inherent",
"itoa",
"sea-query-derive 1.0.0-rc.12",
]
[[package]]
name = "sea-query-derive"
version = "1.0.0-rc.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d88ad44b6ad9788c8b9476b6b91f94c7461d1e19d39cd8ea37838b1e6ff5aa8"
dependencies = [
"darling",
"heck",
"proc-macro2",
"quote",
"syn",
"thiserror",
]
[[package]]
name = "sea-query-derive"
version = "1.0.0"
dependencies = [
"darling",
"heck",
"proc-macro2",
"quote",
"sea-query",
"strum",
"syn",
"thiserror",
"trybuild",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [
"serde_core",
]
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "target-triple"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b"
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "trybuild"
version = "1.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e"
dependencies = [
"glob",
"serde",
"serde_derive",
"serde_json",
"target-triple",
"termcolor",
"toml",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+100
View File
@@ -0,0 +1,100 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2024"
rust-version = "1.88.0"
name = "sea-query-derive"
version = "1.0.0"
authors = [
"Follpvosten <wolfi@karpador.xyz>",
"Rene Leveille <rene@nestingsafe.com>",
]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Derive macro for sea-query's Iden trait"
documentation = "https://docs.rs/sea-query"
readme = false
keywords = [
"database",
"sql",
"mysql",
"postgres",
"sqlite",
]
categories = ["database"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/SeaQL/sea-query"
[features]
default = ["sea-query"]
sea-orm = []
sea-query = [
"heck",
"darling",
"thiserror",
]
[lib]
name = "sea_query_derive"
path = "src/lib.rs"
proc-macro = true
[[test]]
name = "test_build"
path = "tests/test_build.rs"
[dependencies.darling]
version = "0.20"
optional = true
default-features = false
[dependencies.heck]
version = "0.4"
optional = true
default-features = false
[dependencies.proc-macro2]
version = "1"
default-features = false
[dependencies.quote]
version = "1"
default-features = false
[dependencies.syn]
version = "2"
features = [
"parsing",
"proc-macro",
"derive",
"printing",
]
default-features = false
[dependencies.thiserror]
version = "2"
optional = true
default-features = false
[dev-dependencies.sea-query]
version = ">=1.0.0-rc.34, <=1.0.0"
[dev-dependencies.strum]
version = "0.25"
features = ["derive"]
[dev-dependencies.trybuild]
version = "1.0.86"
+33
View File
@@ -0,0 +1,33 @@
[package]
name = "sea-query-derive"
version = "1.0.0"
authors = [ "Follpvosten <wolfi@karpador.xyz>", "Rene Leveille <rene@nestingsafe.com>" ]
edition = "2024"
description = "Derive macro for sea-query's Iden trait"
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/sea-query"
repository = "https://github.com/SeaQL/sea-query"
categories = [ "database" ]
keywords = [ "database", "sql", "mysql", "postgres", "sqlite" ]
rust-version = "1.88.0"
[lib]
proc-macro = true
[dependencies]
syn = { version = "2", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] }
quote = { version = "1", default-features = false }
heck = { version = "0.4", default-features = false, optional = true }
darling = { version = "0.20", default-features = false, optional = true }
proc-macro2 = { version = "1", default-features = false }
thiserror = { version = "2", default-features = false, optional = true }
[dev-dependencies]
trybuild = "1.0.86"
sea-query = { version = ">=1.0.0-rc.34, <=1.0.0", path = ".." }
strum = { version = "0.25", features = ["derive"] }
[features]
default = ["sea-query"]
sea-orm = []
sea-query = ["heck", "darling", "thiserror"]
+1
View File
@@ -0,0 +1 @@
cp ../src/token.rs ./src/raw_sql/
+104
View File
@@ -0,0 +1,104 @@
use darling::FromMeta;
use heck::{ToPascalCase, ToSnakeCase};
use proc_macro::{self, TokenStream};
use quote::quote_spanned;
use syn::{Data, DataStruct, DeriveInput, Fields, Ident, parse_macro_input, spanned::Spanned};
use crate::iden::{GenEnumArgs, NamingHolder};
pub fn expand(args: TokenStream, input: TokenStream) -> TokenStream {
let attr_args = match darling::ast::NestedMeta::parse_meta_list(args.into()) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(darling::Error::from(e).write_errors());
}
};
let input = parse_macro_input!(input as DeriveInput);
let args = match GenEnumArgs::from_list(&attr_args) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(e.write_errors());
}
};
let fields =
match &input.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => &fields.named,
_ => return quote_spanned! {
input.span() => compile_error!("#[enum_def] can only be used on non-tuple structs");
}
.into(),
};
let field_names: Vec<NamingHolder> = fields
.iter()
.map(|field| {
let ident = field.ident.as_ref().unwrap();
NamingHolder {
default: ident.clone(),
pascal: Ident::new(ident.to_string().to_pascal_case().as_str(), ident.span()),
}
})
.collect();
let table_name = Ident::new(
args.table_name
.unwrap_or_else(|| input.ident.to_string().to_snake_case())
.as_str(),
input.ident.span(),
);
let enum_name = quote::format_ident!(
"{}{}{}",
args.prefix
.unwrap_or_else(|| crate::iden::DEFAULT_PREFIX.to_string()),
&input.ident,
args.suffix
.unwrap_or_else(|| crate::iden::DEFAULT_SUFFIX.to_string())
);
let pascal_def_names = field_names.iter().map(|field| &field.pascal);
let pascal_def_names2 = pascal_def_names.clone();
let default_names = field_names.iter().map(|field| &field.default);
let default_names2 = default_names.clone();
let import_name = Ident::new(
args.crate_name
.unwrap_or_else(|| crate::iden::DEFAULT_CRATE_NAME.to_string())
.as_str(),
input.span(),
);
TokenStream::from(quote::quote! {
#input
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum #enum_name {
Table,
#(#pascal_def_names,)*
}
impl #import_name::IdenStatic for #enum_name {
fn as_str(&self) -> &'static str {
match self {
#enum_name::Table => stringify!(#table_name),
#(#enum_name::#pascal_def_names2 => stringify!(#default_names2)),*
}
}
}
impl #import_name::Iden for #enum_name {
fn unquoted(&self) -> &str {
<Self as #import_name::IdenStatic>::as_str(&self)
}
}
impl ::std::convert::AsRef<str> for #enum_name {
fn as_ref(&self) -> &str {
<Self as #import_name::IdenStatic>::as_str(&self)
}
}
})
}
+94
View File
@@ -0,0 +1,94 @@
use std::convert::{TryFrom, TryInto};
use syn::spanned::Spanned;
use syn::{Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta};
use super::{error::ErrorMsg, path::IdenPath};
#[derive(PartialEq, Eq)]
pub(crate) enum IdenAttr {
Rename(String),
Method(Ident),
Flatten,
}
impl IdenAttr {
fn extract_method(meta: Meta) -> syn::Result<Self> {
match meta {
Meta::NameValue(nv) => match nv.value {
Expr::Lit(ExprLit { lit, .. }) => match lit {
Lit::Str(name) => {
Ok(Self::Method(Ident::new(name.value().as_str(), name.span())))
}
_ => Err(Error::new_spanned(nv.eq_token, ErrorMsg::WrongLiteral)),
},
_ => Err(Error::new_spanned(nv, ErrorMsg::WrongLiteral)),
},
a => Err(Error::new_spanned(
a,
ErrorMsg::WrongNamedValueFormat(IdenPath::Method, IdenPath::Method),
)),
}
}
fn extract_iden(meta: Meta) -> syn::Result<Self> {
match &meta {
Meta::NameValue(nv) => match &nv.value {
Expr::Lit(ExprLit { lit, .. }) => match lit {
Lit::Str(lit) => Ok(IdenAttr::Rename(lit.value())),
_ => Err(Error::new_spanned(&nv.value, ErrorMsg::WrongLiteral)),
},
_ => Err(Error::new_spanned(nv, ErrorMsg::WrongLiteral)),
},
Meta::List(list) if list.path.is_ident("iden") => {
let mut iden_attr: Option<Self> = None;
list.parse_nested_meta(|nested| {
if nested.path.is_ident(&IdenPath::Flatten) {
iden_attr = Some(IdenAttr::Flatten);
Ok(())
} else if nested.path.is_ident(&IdenPath::Rename) {
let value = nested.value()?;
let value: LitStr = value.parse()?;
iden_attr = Some(IdenAttr::Rename(value.value()));
Ok(())
} else if nested.path.is_ident(&IdenPath::Method) {
let value = nested.value()?;
let value: LitStr = value.parse()?;
iden_attr = Some(IdenAttr::Method(Ident::new(&value.value(), meta.span())));
Ok(())
} else {
Err(Error::new_spanned(
&meta,
ErrorMsg::UnsupportedKeyword(nested.path.get_ident().unwrap().clone()),
))
}
})?;
iden_attr.ok_or(Error::new_spanned(meta, ErrorMsg::WrongListFormat))
}
a => Err(Error::new_spanned(a, ErrorMsg::WrongAttributeFormat)),
}
}
}
impl TryFrom<&Attribute> for IdenAttr {
type Error = Error;
fn try_from(value: &Attribute) -> Result<Self, Self::Error> {
value.meta.clone().try_into()
}
}
impl TryFrom<Meta> for IdenAttr {
type Error = Error;
fn try_from(value: Meta) -> Result<Self, Self::Error> {
let path = value.path();
if path.is_ident(&IdenPath::Method) {
Self::extract_method(value)
} else if path.is_ident(&IdenPath::Iden) {
Self::extract_iden(value)
} else {
todo!()
}
}
}
+27
View File
@@ -0,0 +1,27 @@
use syn::Ident;
use super::path::IdenPath;
#[derive(Debug, thiserror::Error)]
pub enum ErrorMsg {
#[error(
"Only the attributes `#[iden = \"name\"]` or `#[iden(rename = \"name\") are supported in this position"
)]
ContainerAttr,
#[error("Must be a string literal")]
WrongLiteral,
#[error(
"The method attribute only supports the `#[{0} = \"name\"]` or `#[iden({0} = \"name\")]` formats"
)]
WrongNamedValueFormat(IdenPath, IdenPath),
#[error("Must one of the following attributes: `flatten`, `rename` or `method`")]
WrongListFormat,
#[error(
"The iden attribute supports only the formats `#[iden = \"name\"]` or `#[iden(<ATTRIBUTE>)]` where ATTRIBUTE is either `flatten`, `rename` or `method`"
)]
WrongAttributeFormat,
#[error("{0} is not a supported keyword")]
UnsupportedKeyword(Ident),
#[error("Must have a single field is supported for flattenning")]
UnsupportedFlattenTarget,
}
@@ -0,0 +1,83 @@
use super::*;
pub fn expand(input: DeriveInput) -> TokenStream {
let DeriveInput {
ident, data, attrs, ..
} = input;
let sea_query_path = sea_query_path();
let table_name = match get_table_name(&ident, attrs) {
Ok(v) => v,
Err(e) => return e.to_compile_error().into(),
};
// Currently we only support enums and unit structs
let variants =
match data {
syn::Data::Enum(DataEnum { variants, .. }) => variants,
syn::Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => {
let impl_iden = impl_iden_for_unit_struct(&ident, &table_name);
return quote! {
#impl_iden
impl #sea_query_path::IdenStatic for #ident {
fn as_str(&self) -> &'static str {
#table_name
}
}
impl std::convert::AsRef<str> for #ident {
fn as_ref(&self) -> &str {
self.as_str()
}
}
}
.into();
}
_ => return quote_spanned! {
ident.span() => compile_error!("you can only derive Iden on enums or unit structs");
}
.into(),
};
if variants.is_empty() {
return TokenStream::new();
}
let impl_iden = impl_iden_for_enum(&ident, &table_name, variants.iter());
let match_arms = match variants
.iter()
.map(|v| (table_name.as_str(), v))
.map(IdenVariant::<DeriveIdenStatic>::try_from)
.collect::<syn::Result<Vec<_>>>()
{
Ok(v) => v,
Err(e) => return e.to_compile_error().into(),
};
let output = quote! {
#impl_iden
impl #sea_query_path::IdenStatic for #ident {
fn as_str(&self) -> &'static str {
match self {
#(#match_arms),*
}
}
}
impl std::convert::AsRef<str> for #ident {
fn as_ref(&self) -> &'static str {
self.as_str()
}
}
};
output.into()
}
+200
View File
@@ -0,0 +1,200 @@
mod attr;
mod error;
pub mod iden_static;
mod path;
mod write_arm;
use darling::FromMeta;
use heck::ToSnakeCase;
use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use std::convert::{TryFrom, TryInto};
use syn::{Attribute, DataEnum, DataStruct, DeriveInput, Fields, Ident, Variant};
use attr::IdenAttr;
use error::ErrorMsg;
use path::IdenPath;
use write_arm::IdenVariant;
struct DeriveIden;
struct DeriveIdenStatic;
pub fn expand(input: DeriveInput) -> TokenStream {
let DeriveInput {
ident, data, attrs, ..
} = input;
let table_name = match get_table_name(&ident, attrs) {
Ok(v) => v,
Err(e) => return e.to_compile_error().into(),
};
// Currently we only support enums and unit structs
let variants =
match data {
syn::Data::Enum(DataEnum { variants, .. }) => variants,
syn::Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => return impl_iden_for_unit_struct(&ident, &table_name).into(),
_ => return quote_spanned! {
ident.span() => compile_error!("you can only derive Iden on enums or unit structs");
}
.into(),
};
if variants.is_empty() {
return TokenStream::new();
}
let output = impl_iden_for_enum(&ident, &table_name, variants.iter());
output.into()
}
fn impl_iden_for_unit_struct(
ident: &proc_macro2::Ident,
table_name: &str,
) -> proc_macro2::TokenStream {
let sea_query_path = sea_query_path();
if is_static_iden(table_name) {
quote! {
impl #sea_query_path::Iden for #ident {
fn quoted(&self) -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Borrowed(#table_name)
}
fn unquoted(&self) -> &str {
#table_name
}
}
}
} else {
quote! {
impl #sea_query_path::Iden for #ident {
fn unquoted(&self) -> &str {
#table_name
}
}
}
}
}
fn impl_iden_for_enum<'a, T>(
ident: &proc_macro2::Ident,
table_name: &str,
variants: T,
) -> proc_macro2::TokenStream
where
T: Iterator<Item = &'a Variant>,
{
let sea_query_path = sea_query_path();
let mut is_all_static_iden = true;
let match_arms = match variants
.map(|v| {
let v = IdenVariant::<DeriveIden>::try_from((table_name, v))?;
is_all_static_iden &= v.is_static_iden();
Ok(v)
})
.collect::<syn::Result<Vec<_>>>()
{
Ok(v) => v,
Err(e) => return e.to_compile_error(),
};
if is_all_static_iden {
quote! {
impl #sea_query_path::Iden for #ident {
fn quoted(&self) -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Borrowed(match self {
#(#match_arms),*
})
}
fn unquoted(&self) -> &str {
match self {
#(#match_arms),*
}
}
}
}
} else {
quote! {
impl #sea_query_path::Iden for #ident {
fn unquoted(&self) -> &str {
match self {
#(#match_arms),*
}
}
}
}
}
}
fn sea_query_path() -> proc_macro2::TokenStream {
if cfg!(feature = "sea-orm") {
quote!(sea_orm::sea_query)
} else {
quote!(sea_query)
}
}
pub struct NamingHolder {
pub default: Ident,
pub pascal: Ident,
}
#[derive(Debug, FromMeta)]
pub struct GenEnumArgs {
#[darling(default)]
pub prefix: Option<String>,
#[darling(default)]
pub suffix: Option<String>,
#[darling(default)]
pub crate_name: Option<String>,
#[darling(default)]
pub table_name: Option<String>,
}
pub const DEFAULT_PREFIX: &str = "";
pub const DEFAULT_SUFFIX: &str = "Iden";
pub const DEFAULT_CRATE_NAME: &str = "sea_query";
impl Default for GenEnumArgs {
fn default() -> Self {
Self {
prefix: Some(DEFAULT_PREFIX.to_string()),
suffix: Some(DEFAULT_SUFFIX.to_string()),
crate_name: Some(DEFAULT_CRATE_NAME.to_string()),
table_name: None,
}
}
}
fn get_table_name(ident: &proc_macro2::Ident, attrs: Vec<Attribute>) -> Result<String, syn::Error> {
let table_name = match find_attr(&attrs) {
Some(att) => match att.try_into()? {
IdenAttr::Rename(lit) => lit,
_ => return Err(syn::Error::new_spanned(att, ErrorMsg::ContainerAttr)),
},
None => ident.to_string().to_snake_case(),
};
Ok(table_name)
}
fn find_attr(attrs: &[Attribute]) -> Option<&Attribute> {
attrs.iter().find(|attr| {
attr.path().is_ident(&IdenPath::Iden) || attr.path().is_ident(&IdenPath::Method)
})
}
pub fn is_static_iden(name: &str) -> bool {
// can only begin with [a-z_]
name.chars()
.take(1)
.all(|c| c == '_' || c.is_ascii_alphabetic())
&& name.chars().all(|c| c == '_' || c.is_ascii_alphanumeric())
}
+32
View File
@@ -0,0 +1,32 @@
use std::fmt::Display;
#[derive(Debug)]
pub enum IdenPath {
Iden,
Method,
Rename,
Flatten,
}
impl IdenPath {
const fn as_str(&self) -> &'static str {
match self {
IdenPath::Iden => "iden",
IdenPath::Method => "method",
IdenPath::Rename => "rename",
IdenPath::Flatten => "flatten",
}
}
}
impl Display for IdenPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl PartialEq<IdenPath> for syn::Ident {
fn eq(&self, other: &IdenPath) -> bool {
self.eq(other.as_str())
}
}
@@ -0,0 +1,204 @@
use std::convert::TryFrom;
use std::marker::PhantomData;
use heck::ToSnakeCase;
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt, quote};
use syn::{Error, Fields, FieldsNamed, Ident, Variant};
use super::{
DeriveIden, DeriveIdenStatic, attr::IdenAttr, error::ErrorMsg, find_attr, is_static_iden,
};
pub(crate) trait WriteArm {
fn variant(variant: TokenStream, name: TokenStream) -> TokenStream;
fn flattened(variant: TokenStream, name: &Ident) -> TokenStream;
}
impl WriteArm for DeriveIden {
fn variant(variant: TokenStream, name: TokenStream) -> TokenStream {
quote! { Self::#variant => #name }
}
fn flattened(variant: TokenStream, name: &Ident) -> TokenStream {
quote! { Self::#variant => #name.unquoted() }
}
}
impl WriteArm for DeriveIdenStatic {
fn variant(variant: TokenStream, name: TokenStream) -> TokenStream {
quote! { Self::#variant => #name }
}
fn flattened(variant: TokenStream, name: &Ident) -> TokenStream {
quote! { Self::#variant => #name.as_str() }
}
}
pub(crate) struct IdenVariant<'a, T> {
ident: &'a Ident,
fields: &'a Fields,
table_name: &'a str,
attr: Option<IdenAttr>,
_p: PhantomData<T>,
}
impl<'a, T> TryFrom<(&'a str, &'a Variant)> for IdenVariant<'a, T>
where
T: WriteArm,
{
type Error = Error;
fn try_from((table_name, value): (&'a str, &'a Variant)) -> Result<Self, Self::Error> {
let Variant {
ident,
fields,
attrs,
..
} = value;
let attr = find_attr(attrs).map(IdenAttr::try_from).transpose()?;
Self::new(ident, fields, table_name, attr)
}
}
impl<T> ToTokens for IdenVariant<'_, T>
where
T: WriteArm,
{
fn to_tokens(&self, tokens: &mut TokenStream) {
match self.fields {
Fields::Named(named) => self.to_tokens_from_named(named, tokens),
Fields::Unnamed(_) => self.to_tokens_from_unnamed(tokens),
Fields::Unit => self.to_tokens_from_unit(tokens),
}
}
}
impl<'a, T> IdenVariant<'a, T>
where
T: WriteArm,
{
fn new(
ident: &'a Ident,
fields: &'a Fields,
table_name: &'a str,
attr: Option<IdenAttr>,
) -> syn::Result<Self> {
let unsupported_error = Err(Error::new_spanned(
fields,
ErrorMsg::UnsupportedFlattenTarget,
));
// sanity check to not have flatten on a unit variant, or variants with more than 1 field
if attr == Some(IdenAttr::Flatten) {
match fields {
Fields::Named(n) => {
if n.named.len() != 1 {
return unsupported_error;
}
}
Fields::Unnamed(u) => {
if u.unnamed.len() != 1 {
return unsupported_error;
}
}
Fields::Unit => return unsupported_error,
}
}
Ok(Self {
ident,
fields,
table_name,
attr,
_p: PhantomData::<T>,
})
}
fn to_tokens_from_named(&self, named: &FieldsNamed, tokens: &mut TokenStream) {
let ident = self.ident;
let match_arm = if self.attr == Some(IdenAttr::Flatten) {
// indexing is safe because len is guaranteed to be 1 from the constructor.
let field = &named.named[0];
// Unwrapping the ident is also safe because a named field always has an ident.
let capture = field.ident.as_ref().unwrap();
let variant = quote! { #ident{#capture} };
T::flattened(variant, capture)
} else {
let variant = quote! { #ident{..} };
self.write_variant_name(variant)
};
tokens.append_all(match_arm)
}
fn to_tokens_from_unnamed(&self, tokens: &mut TokenStream) {
let ident = self.ident;
let match_arm = if self.attr == Some(IdenAttr::Flatten) {
// The case where unnamed fields length is not 1 is handled by new
let capture = Delegated.into();
let variant = quote! { #ident(#capture) };
T::flattened(variant, &capture)
} else {
let variant = quote! { #ident(..) };
self.write_variant_name(variant)
};
tokens.append_all(match_arm)
}
fn to_tokens_from_unit(&self, tokens: &mut TokenStream) {
let ident = self.ident;
let variant = quote! { #ident };
tokens.append_all(self.write_variant_name(variant))
}
fn table_or_snake_case(&self) -> String {
if self.ident == "Table" {
self.table_name.to_owned()
} else {
self.ident.to_string().to_snake_case()
}
}
fn write_variant_name(&self, variant: TokenStream) -> TokenStream {
let name = self
.attr
.as_ref()
.map(|a| match a {
IdenAttr::Rename(name) => quote! { #name },
IdenAttr::Method(method) => quote! { self.#method() },
IdenAttr::Flatten => unreachable!(),
})
.unwrap_or_else(|| {
let name = self.table_or_snake_case();
quote! { #name }
});
T::variant(variant, name)
}
pub(crate) fn is_static_iden(&self) -> bool {
let name: String = match &self.attr {
Some(a) => match a {
IdenAttr::Rename(name) => name.to_owned(),
IdenAttr::Method(_) => return false,
IdenAttr::Flatten => return false,
},
None => self.table_or_snake_case(),
};
is_static_iden(&name)
}
}
struct Delegated;
impl From<Delegated> for Ident {
fn from(_: Delegated) -> Self {
Ident::new("delegated", Span::call_site())
}
}
+98
View File
@@ -0,0 +1,98 @@
#![forbid(unsafe_code)]
use proc_macro::TokenStream;
#[cfg(feature = "sea-query")]
use syn::{DeriveInput, parse_macro_input};
#[cfg(feature = "sea-query")]
mod enum_def;
#[cfg(feature = "sea-query")]
mod iden;
mod raw_query;
mod raw_sql;
mod sqlx;
#[cfg(feature = "sea-query")]
#[proc_macro_derive(Iden, attributes(iden, method))]
pub fn derive_iden(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
iden::expand(input)
}
#[cfg(feature = "sea-query")]
#[proc_macro_derive(IdenStatic, attributes(iden, method))]
pub fn derive_iden_static(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
iden::iden_static::expand(input)
}
#[cfg(feature = "sea-query")]
#[proc_macro_attribute]
pub fn enum_def(args: TokenStream, input: TokenStream) -> TokenStream {
enum_def::expand(args, input)
}
#[proc_macro]
pub fn raw_sql(input: TokenStream) -> TokenStream {
match raw_sql::expand(input) {
Ok(token_stream) => token_stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[cfg(feature = "sea-query")]
#[proc_macro]
pub fn raw_query(input: TokenStream) -> TokenStream {
match raw_query::expand(input) {
Ok(token_stream) => token_stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro]
pub fn sqlx_sqlite_query(input: TokenStream) -> TokenStream {
match sqlx::sqlite_query(input) {
Ok(token_stream) => token_stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro]
pub fn sqlx_sqlite_query_as(input: TokenStream) -> TokenStream {
match sqlx::sqlite_query_as(input) {
Ok(token_stream) => token_stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro]
pub fn sqlx_mysql_query(input: TokenStream) -> TokenStream {
match sqlx::mysql_query(input) {
Ok(token_stream) => token_stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro]
pub fn sqlx_mysql_query_as(input: TokenStream) -> TokenStream {
match sqlx::mysql_query_as(input) {
Ok(token_stream) => token_stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro]
pub fn sqlx_postgres_query(input: TokenStream) -> TokenStream {
match sqlx::postgres_query(input) {
Ok(token_stream) => token_stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro]
pub fn sqlx_postgres_query_as(input: TokenStream) -> TokenStream {
match sqlx::postgres_query_as(input) {
Ok(token_stream) => token_stream.into(),
Err(err) => err.to_compile_error().into(),
}
}
+41
View File
@@ -0,0 +1,41 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
Ident, LitStr, Token,
parse::{Parse, ParseStream},
};
struct CallArgs {
builder: Ident,
_comma: Token![,],
sql_string: LitStr,
}
impl Parse for CallArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(CallArgs {
builder: input.parse()?,
_comma: input.parse()?,
sql_string: input.parse()?,
})
}
}
pub fn expand(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
let CallArgs {
builder,
sql_string,
..
} = syn::parse(input)?;
let builder = match builder.to_string().as_str() {
"PostgresQueryBuilder" => quote!(postgres),
"MysqlQueryBuilder" => quote!(mysql),
"SqliteQueryBuilder" => quote!(sqlite),
_ => quote!(#builder),
};
Ok(quote!(
sea_query::raw_sql!(seaql::#builder::query, #sql_string)
))
}
+290
View File
@@ -0,0 +1,290 @@
mod token;
use token::*;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
Ident, Index, LitStr, Member, Path, Token,
parse::{Parse, ParseStream},
};
struct CallArgs {
module: Ident,
backend: Ident,
method: Ident,
sql_wrapper: Option<Path>,
sql_holder: Option<Ident>,
sql_input: LitStr,
}
impl Parse for CallArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let module: Ident = input.parse()?;
let _colon1: Token![::] = input.parse()?;
let backend: Ident = input.parse()?;
let _colon2: Token![::] = input.parse()?;
let method: Ident = input.parse()?;
let _comma: Token![,] = input.parse()?;
let sql_wrapper = if input.peek(Token![@]) {
let _at: Token![@] = input.parse()?;
let wrapper = input.parse()?;
let _comma: Token![,] = input.parse()?;
Some(wrapper)
} else {
None
};
let sql_holder = if input.peek(Ident) {
let ident = input.parse()?;
let _assign: Token![=] = input.parse()?;
Some(ident)
} else {
None
};
let sql_input: LitStr = input.parse()?;
Ok(CallArgs {
module,
backend,
method,
sql_wrapper,
sql_holder,
sql_input,
})
}
}
pub fn expand(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
let CallArgs {
module,
backend,
method,
sql_wrapper,
sql_holder,
sql_input,
} = syn::parse(input)?;
let backend = match backend.to_string().as_str() {
"postgres" => quote!(sea_query::PostgresQueryBuilder),
"mysql" => quote!(sea_query::MysqlQueryBuilder),
"sqlite" => quote!(sea_query::SqliteQueryBuilder),
_ => quote!(#backend),
};
let mut fragments = Vec::new();
let mut params = Vec::new();
let sql_input = sql_input.value();
let tokens = Tokenizer::new(&sql_input);
let mut in_brace = false;
let mut in_paren = false;
let mut dot_count = 0;
let mut interpolate = false;
let mut nested_eval = false;
let mut has_ending_comma = false;
let mut fragment = String::new();
let mut vars: Vec<Var> = vec![Default::default()];
#[derive(Default)]
struct Var<'a> {
parts: Vec<&'a str>, // named parts, i.e. a.b.c
members: Vec<u32>, // tuple members
}
for token in tokens {
match token {
Token::Punctuation("{") => {
in_brace = true;
}
Token::Punctuation("}") => {
assert!(in_brace, "unmatched closing brace }}");
for vars in vars.iter_mut() {
if interpolate {
assert!(vars.members.len() >= 2, "expect 2 numbers around :");
let a = vars.members[vars.members.len() - 2];
let b = vars.members[vars.members.len() - 1];
assert!(a < b, "expect a < b in a:b");
vars.members = (a..=b).collect();
} else {
vars.members.clear();
}
}
let top = {
let v = Ident::new(vars[0].parts[0], Span::call_site());
quote!(#v)
};
if nested_eval {
assert!(has_ending_comma, "..(), must end with comma ,");
let group_size: usize = vars
.iter()
.map(|var| {
if var.members.is_empty() {
1
} else {
var.members.len()
}
})
.sum();
fragments
.push(quote!(.push_tuple_parameter_groups((&#top).len(), #group_size)));
}
let mut group = Vec::new();
for vars in vars {
let mut var = TokenStream::new();
for (i, v) in vars.parts.iter().enumerate() {
if i > 0 {
var.extend(quote!(.));
}
if is_ascii_digits(v) {
if interpolate {
// skip .x
break;
}
let v = Member::Unnamed(Index {
index: v.parse().unwrap(),
span: Span::call_site(),
});
var.extend(quote!(#v));
} else {
let v = Ident::new(v, Span::call_site());
var.extend(quote!(#v));
}
}
if !vars.members.is_empty() {
// there is a range operator `a:b`
if !nested_eval {
let len = vars.members.len();
fragments.push(quote!(.push_parameters(#len)));
}
for mul in vars.members.iter() {
let mut var = var.clone();
let mul = Member::Unnamed(Index {
index: *mul,
span: Span::call_site(),
});
var.extend(quote!(#mul));
group.push(quote! { query = query.bind(&#var); });
}
} else if dot_count == 2 && !nested_eval {
// non nested spread `..a`
fragments.push(quote!(.push_parameters((&#var).len())));
group.push(quote! {
for v in (&#var).iter() {
query = query.bind(v);
}
});
} else {
if !nested_eval {
fragments.push(quote!(.push_parameters(1)));
}
group.push(quote! { query = query.bind(&#var); });
}
}
if nested_eval {
params.push(quote! {
for #top in (&#top).iter() {
#(#group)*
}
});
} else {
params.append(&mut group);
}
in_brace = false;
in_paren = false;
dot_count = 0;
interpolate = false;
nested_eval = false;
has_ending_comma = false;
vars = vec![Default::default()];
}
Token::Unquoted(var) if in_brace => {
if !fragment.is_empty() {
fragments.push(quote!(.push_fragment(#fragment)));
fragment.clear();
}
vars.last_mut().unwrap().parts.push(var);
if is_ascii_digits(var) {
vars.last_mut()
.unwrap()
.members
.push(var.parse().expect("index out of range"));
}
}
Token::Punctuation(".") if in_brace => {
if vars.last_mut().unwrap().parts.is_empty() {
// prefix ..
dot_count += 1;
}
}
Token::Punctuation(":") if in_brace => {
if !vars.last_mut().unwrap().parts.is_empty() {
// postfix :
interpolate = true;
}
}
Token::Punctuation("(") if in_brace => {
nested_eval = true;
in_paren = true;
}
Token::Punctuation(")") if in_brace => {
assert!(in_paren, "unmatched closing parenthesis )");
in_paren = false
}
Token::Punctuation(",") if in_brace && in_paren && nested_eval => {
// push a new variable
vars.push(Default::default());
}
Token::Punctuation(",") if in_brace && !in_paren && nested_eval => {
has_ending_comma = true;
}
Token::Punctuation(",") if in_brace && !in_paren && !nested_eval => {
panic!("unknown extra comma ,")
}
_ => {
if !in_brace {
fragment.push_str(token.as_str());
}
}
}
}
if !fragment.is_empty() {
fragments.push(quote!(.push_fragment(#fragment)));
fragment.clear();
}
let (maybe_let, sql_holder) = if let Some(sql_holder) = sql_holder {
(quote!(), sql_holder)
} else {
(quote!(let), Ident::new("sql", Span::call_site()))
};
let query = if let Some(sql_wrapper) = sql_wrapper {
quote!(#module::#method(#sql_wrapper(#sql_holder.as_str())))
} else {
quote!(#module::#method(&#sql_holder))
};
let output = quote! {{
use sea_query::raw_sql::*;
let mut builder = RawSqlQueryBuilder::new(#backend);
builder
#(#fragments)*;
#maybe_let #sql_holder = builder.finish();
let mut query = #query;
#(#params)*
query
}};
Ok(output)
}
fn is_ascii_digits(s: &str) -> bool {
s.chars().all(|c| c.is_ascii_digit())
}
File diff suppressed because it is too large Load Diff
+94
View File
@@ -0,0 +1,94 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
Ident, LitStr, Token,
parse::{Parse, ParseStream},
};
struct CallArgs {
sql_holder: Ident,
_assign: Token![=],
sql_string: LitStr,
}
impl Parse for CallArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(CallArgs {
sql_holder: input.parse()?,
_assign: input.parse()?,
sql_string: input.parse()?,
})
}
}
pub fn sqlite_query(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
let CallArgs {
sql_holder,
sql_string,
..
} = syn::parse(input)?;
Ok(quote!(
sea_query::raw_sql!(sqlx::sqlite::query, @sqlx::AssertSqlSafe, #sql_holder = #sql_string)
))
}
pub fn sqlite_query_as(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
let CallArgs {
sql_holder,
sql_string,
..
} = syn::parse(input)?;
Ok(quote!(
sea_query::raw_sql!(sqlx::sqlite::query_as, @sqlx::AssertSqlSafe, #sql_holder = #sql_string)
))
}
pub fn mysql_query(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
let CallArgs {
sql_holder,
sql_string,
..
} = syn::parse(input)?;
Ok(quote!(
sea_query::raw_sql!(sqlx::mysql::query, @sqlx::AssertSqlSafe, #sql_holder = #sql_string)
))
}
pub fn mysql_query_as(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
let CallArgs {
sql_holder,
sql_string,
..
} = syn::parse(input)?;
Ok(quote!(
sea_query::raw_sql!(sqlx::mysql::query_as, @sqlx::AssertSqlSafe, #sql_holder = #sql_string)
))
}
pub fn postgres_query(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
let CallArgs {
sql_holder,
sql_string,
..
} = syn::parse(input)?;
Ok(quote!(
sea_query::raw_sql!(sqlx::postgres::query, @sqlx::AssertSqlSafe, #sql_holder = #sql_string)
))
}
pub fn postgres_query_as(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
let CallArgs {
sql_holder,
sql_string,
..
} = syn::parse(input)?;
Ok(quote!(
sea_query::raw_sql!(sqlx::postgres::query_as, @sqlx::AssertSqlSafe, #sql_holder = #sql_string)
))
}
@@ -0,0 +1,19 @@
use sea_query::Iden;
#[derive(Iden)]
enum Asset {
Table,
Id,
AssetName,
#[iden()]
Creation(CreationInfo),
}
#[derive(Iden)]
enum CreationInfo {
UserId,
#[iden = "creation_date"]
Date,
}
fn main() {}
@@ -0,0 +1,5 @@
error: Must one of the following attributes: `flatten`, `rename` or `method`
--> $DIR/empty_list_iden.rs:8:7
|
8 | #[iden()]
| ^^^^^^
@@ -0,0 +1,8 @@
use sea_query_derive::enum_def;
#[enum_def]
enum Hello {
Name,
}
fn main() {}
@@ -0,0 +1,5 @@
error: #[enum_def] can only be used on non-tuple structs
--> tests/compile-fail/enum_def/enum.rs:4:1
|
4 | enum Hello {
| ^^^^
@@ -0,0 +1,6 @@
use sea_query_derive::enum_def;
#[enum_def]
struct Hello(String);
fn main() {}
@@ -0,0 +1,5 @@
error: #[enum_def] can only be used on non-tuple structs
--> tests/compile-fail/enum_def/tuple_struct.rs:4:1
|
4 | struct Hello(String);
| ^^^^^^
@@ -0,0 +1,8 @@
use sea_query_derive::enum_def;
#[enum_def(unknown_field)]
pub struct Hello {
pub name: String,
}
fn main() {}
@@ -0,0 +1,5 @@
error: Unknown field: `unknown_field`
--> tests/compile-fail/enum_def/unknown_field.rs:3:12
|
3 | #[enum_def(unknown_field)]
| ^^^^^^^^^^^^^
@@ -0,0 +1,19 @@
use sea_query::Iden;
#[derive(Iden)]
enum Asset {
Table,
Id,
AssetName,
#[iden(flatten)]
Creation(CreationInfo, CreationInfo),
}
#[derive(Iden)]
enum CreationInfo {
UserId,
#[iden = "creation_date"]
Date,
}
fn main() {}
@@ -0,0 +1,5 @@
error: Must have a single field is supported for flattenning
--> $DIR/mutli_field_flatten.rs:9:13
|
9 | Creation(CreationInfo, CreationInfo),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -0,0 +1,12 @@
use sea_query::Iden;
#[derive(Iden)]
enum Asset {
Table,
Id,
AssetName,
#[iden]
Creation,
}
fn main() {}
@@ -0,0 +1,5 @@
error: The iden attribute supports only the formats `#[iden = "name"]` or `#[iden(<ATTRIBUTE>)]` where ATTRIBUTE is either `flatten`, `rename` or `method`
--> $DIR/path_iden.rs:8:7
|
8 | #[iden]
| ^^^^
@@ -0,0 +1,12 @@
use sea_query::Iden;
#[derive(Iden)]
enum Asset {
Table,
Id,
AssetName,
#[method]
Creation,
}
fn main() {}
@@ -0,0 +1,5 @@
error: The method attribute only supports the `#[method = "name"]` or `#[iden(method = "name")]` formats
--> $DIR/path_method.rs:8:7
|
8 | #[method]
| ^^^^^^
@@ -0,0 +1,8 @@
use sea_query::Iden;
#[derive(Iden)]
struct User {
id: usize,
}
fn main() {}
@@ -0,0 +1,5 @@
error: you can only derive Iden on enums or unit structs
--> $DIR/unsupported_container.rs:4:8
|
4 | struct User {
| ^^^^
@@ -0,0 +1,13 @@
use sea_query::Iden;
#[derive(Iden)]
enum User {
Table,
#[iden = 123]
Id,
FirstName,
LastName,
Email,
}
fn main() {}
@@ -0,0 +1,5 @@
error: Must be a string literal
--> $DIR/wrong_literal_type.rs:6:14
|
6 | #[iden = 123]
| ^^^
@@ -0,0 +1,61 @@
use sea_query::{Iden, IdenStatic};
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, IdenStatic, EnumIter)]
enum Asset {
Table,
Id,
AssetName,
#[iden(flatten)]
First {
first: FirstLevel,
},
}
#[derive(Copy, Clone, IdenStatic)]
enum FirstLevel {
LevelOne,
#[iden(flatten)]
Second(SecondLevel),
}
#[derive(Copy, Clone, IdenStatic, EnumIter)]
enum SecondLevel {
LevelTwo,
#[iden(flatten)]
Third(LevelThree),
UserId,
}
#[derive(Copy, Clone, IdenStatic, Default)]
struct LevelThree;
impl Default for FirstLevel {
fn default() -> Self {
Self::LevelOne
}
}
fn main() {
// custom ends up being default string which is an empty string
let expected = [
"asset",
"id",
"asset_name",
"level_one",
"level_two",
"level_three",
"user_id",
];
Asset::iter()
.chain(
SecondLevel::iter()
.map(FirstLevel::Second)
.map(|s| Asset::First { first: s }),
)
.zip(expected)
.for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
assert_eq!(var.as_str(), exp)
})
}
@@ -0,0 +1,40 @@
use sea_query::{Iden, IdenStatic};
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, IdenStatic, EnumIter)]
enum Asset {
Table,
Id,
AssetName,
#[iden(flatten)]
Creation {
info: CreationInfo,
},
}
#[derive(Copy, Clone, IdenStatic)]
enum CreationInfo {
UserId,
#[iden = "creation_date"]
Date,
}
impl Default for CreationInfo {
fn default() -> Self {
Self::UserId
}
}
fn main() {
// custom ends up being default string which is an empty string
let expected = ["asset", "id", "asset_name", "user_id", "creation_date"];
Asset::iter()
.chain(std::iter::once(Asset::Creation {
info: CreationInfo::Date,
}))
.zip(expected)
.for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
assert_eq!(var.as_str(), exp)
})
}
@@ -0,0 +1,31 @@
use sea_query::{Iden, IdenStatic};
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, IdenStatic, EnumIter)]
enum Asset {
Table,
Id,
AssetName,
#[iden(flatten)]
Creation(CreationInfo),
}
#[derive(Default, Copy, Clone, IdenStatic)]
enum CreationInfo {
#[default]
UserId,
#[iden = "creation_date"]
Date,
}
fn main() {
// custom ends up being default string which is an empty string
let expected = ["asset", "id", "asset_name", "user_id", "creation_date"];
Asset::iter()
.chain(std::iter::once(Asset::Creation(CreationInfo::Date)))
.zip(expected)
.for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
assert_eq!(var.as_str(), exp)
})
}
@@ -0,0 +1,48 @@
use sea_query::{Iden, IntoIden, IdenStatic, MysqlQueryBuilder, PostgresQueryBuilder, QuotedBuilder};
use strum::{EnumIter, IntoEnumIterator};
#[derive(IdenStatic, EnumIter, Copy, Clone)]
// Outer iden attributes overrides what's used for "Table"...
#[iden(rename = "user")]
enum Custom {
Table,
#[iden(rename = "my_id")]
Id,
#[iden(rename = "name")]
FirstName,
#[iden(rename = "surname")]
LastName,
// Custom casing if needed
#[iden(rename = "EM`ail")]
// the tuple value will be ignored
Email(i32),
// Custom method
#[iden(method = "custom_to_string")]
Custom,
}
impl Custom {
pub fn custom_to_string(&self) -> &'static str {
match self {
Self::Custom => "custom",
_ => panic!("not Custom::Custom"),
}
}
}
fn main() {
// custom ends up being default string which is an empty string
let expected = ["user", "my_id", "name", "surname", "EM`ail", "custom"];
Custom::iter()
.map(|var| var.to_string())
.zip(expected)
.for_each(|(iden, exp)| assert_eq!(iden, exp));
let mut string = String::new();
PostgresQueryBuilder.prepare_iden(&Custom::Email(0).into_iden(), &mut string);
assert_eq!(string, "\"EM`ail\"");
let mut string = String::new();
MysqlQueryBuilder.prepare_iden(&Custom::Email(0).into_iden(), &mut string);
assert_eq!(string, "`EM``ail`");
}
@@ -0,0 +1,10 @@
use sea_query::{Iden, IdenStatic};
#[derive(Copy, Clone, IdenStatic)]
#[iden = "another_name"]
pub struct CustomName;
fn main() {
assert_eq!(CustomName.to_string(), "another_name");
assert_eq!(CustomName.as_str(), "another_name")
}
@@ -0,0 +1,20 @@
use sea_query::{Iden, IdenStatic};
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, IdenStatic, EnumIter)]
enum Something {
// ...the Table can also be overwritten like this
#[iden = "something_else"]
Table,
Id,
AssetName,
UserId,
}
fn main() {
let expected = ["something_else", "id", "asset_name", "user_id"];
Something::iter().zip(expected).for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
assert_eq!(var.as_str(), exp)
})
}
@@ -0,0 +1,25 @@
use sea_query::{Iden, IntoIden, IdenStatic, MysqlQueryBuilder, PostgresQueryBuilder, QuotedBuilder};
#[derive(Copy, Clone, IdenStatic)]
pub struct SomeType;
#[derive(Copy, Clone, IdenStatic)]
#[iden(rename = "Hel`lo")]
pub struct SomeTypeWithRename;
fn main() {
assert_eq!(SomeType.to_string(), "some_type");
assert_eq!(SomeTypeWithRename.to_string(), "Hel`lo");
let mut string = String::new();
PostgresQueryBuilder.prepare_iden(&SomeType.into_iden(), &mut string);
assert_eq!(string, "\"some_type\"");
let mut string = String::new();
PostgresQueryBuilder.prepare_iden(&SomeTypeWithRename.into_iden(), &mut string);
assert_eq!(string, "\"Hel`lo\"");
let mut string = String::new();
MysqlQueryBuilder.prepare_iden(&SomeTypeWithRename.into_iden(), &mut string);
assert_eq!(string, "`Hel``lo`");
}
@@ -0,0 +1,28 @@
use sea_query::{Iden, IdenStatic};
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, IdenStatic, EnumIter)]
enum User {
Table,
Id,
FirstName,
LastName,
Email,
}
#[derive(Copy, Clone, IdenStatic, EnumIter)]
enum UserStatic {
Table,
Id,
FirstName,
LastName,
Email,
}
fn main() {
let expected = ["user", "id", "first_name", "last_name", "email"];
User::iter().zip(expected).for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
assert_eq!(var.as_str(), exp)
});
}
@@ -0,0 +1,60 @@
use sea_query::Iden;
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, Iden, EnumIter)]
enum Asset {
Table,
Id,
AssetName,
#[iden(flatten)]
First {
first: FirstLevel,
},
}
#[derive(Copy, Clone, Iden)]
enum FirstLevel {
LevelOne,
#[iden(flatten)]
Second(SecondLevel),
}
#[derive(Copy, Clone, Iden, EnumIter)]
enum SecondLevel {
LevelTwo,
#[iden(flatten)]
Third(LevelThree),
UserId,
}
#[derive(Copy, Clone, Iden, Default)]
struct LevelThree;
impl Default for FirstLevel {
fn default() -> Self {
Self::LevelOne
}
}
fn main() {
// custom ends up being default string which is an empty string
let expected = [
"asset",
"id",
"asset_name",
"level_one",
"level_two",
"level_three",
"user_id",
];
Asset::iter()
.chain(
SecondLevel::iter()
.map(FirstLevel::Second)
.map(|s| Asset::First { first: s }),
)
.zip(expected)
.for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
})
}
@@ -0,0 +1,10 @@
use sea_query_derive::enum_def;
#[enum_def]
pub struct Hello {
pub name: String
}
fn main() {
assert_eq!(format!("{:?}", HelloIden::Name), "Name");
}
@@ -0,0 +1,10 @@
use sea_query_derive::enum_def;
#[enum_def(prefix = "Enum", suffix = "")]
pub struct Hello {
pub name: String
}
fn main() {
assert_eq!(format!("{:?}", EnumHello::Name), "Name");
}
@@ -0,0 +1,10 @@
use sea_query_derive::enum_def;
#[enum_def(prefix = "", suffix = "Def")]
pub struct Hello {
pub name: String
}
fn main() {
assert_eq!(format!("{:?}", HelloDef::Name), "Name");
}
@@ -0,0 +1,19 @@
use sea_query::{Iden, IdenStatic};
use sea_query_derive::enum_def;
use std::convert::AsRef;
#[enum_def(table_name = "HelloTable")]
pub struct Hello {
pub name: String,
}
fn main() {
assert_eq!("HelloTable".to_string(), HelloIden::Table.to_string());
assert_eq!("name".to_string(), HelloIden::Name.to_string());
assert_eq!("HelloTable", HelloIden::Table.as_str());
assert_eq!("name", HelloIden::Name.as_str());
assert_eq!("HelloTable", AsRef::<str>::as_ref(&HelloIden::Table));
assert_eq!("name", AsRef::<str>::as_ref(&HelloIden::Name));
}
@@ -0,0 +1,41 @@
use sea_query::Iden;
use strum::{EnumIter, IntoEnumIterator};
use std::borrow::Cow;
#[derive(Copy, Clone, Iden, EnumIter)]
enum Asset {
Table,
Id,
AssetName,
#[iden(flatten)]
Creation {
info: CreationInfo,
},
}
#[derive(Copy, Clone, Iden)]
enum CreationInfo {
UserId,
#[iden = "creation_date"]
Date,
}
impl Default for CreationInfo {
fn default() -> Self {
Self::UserId
}
}
fn main() {
// custom ends up being default string which is an empty string
let expected = ["asset", "id", "asset_name", "user_id", "creation_date"];
Asset::iter()
.chain(std::iter::once(Asset::Creation {
info: CreationInfo::Date,
}))
.zip(expected)
.for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
assert_eq!(var.quoted(), Cow::Borrowed(exp));
})
}
@@ -0,0 +1,35 @@
use sea_query::Iden;
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, Iden, EnumIter)]
enum Asset {
Table,
Id,
AssetName,
#[iden(flatten)]
Creation(CreationInfo),
}
#[derive(Copy, Clone, Iden)]
enum CreationInfo {
UserId,
#[iden = "creation_date"]
Date,
}
impl Default for CreationInfo {
fn default() -> Self {
Self::UserId
}
}
fn main() {
// custom ends up being default string which is an empty string
let expected = ["asset", "id", "asset_name", "user_id", "creation_date"];
Asset::iter()
.chain(std::iter::once(Asset::Creation(CreationInfo::Date)))
.zip(expected)
.for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
})
}
@@ -0,0 +1,51 @@
use sea_query::{Iden, IntoIden, MysqlQueryBuilder, PostgresQueryBuilder, QuotedBuilder};
use strum::{EnumIter, IntoEnumIterator};
use std::borrow::Cow;
#[derive(Iden, EnumIter)]
// Outer iden attributes overrides what's used for "Table"...
#[iden(rename = "user")]
enum Custom {
Table,
#[iden(rename = "my_id")]
Id,
#[iden(rename = "name")]
FirstName,
#[iden(rename = "surname")]
LastName,
// Custom casing if needed
#[iden(rename = "EM`ail")]
// the tuple value will be ignored
Email(String),
// Custom method
#[iden(method = "custom_to_string")]
Custom(String),
}
impl Custom {
pub fn custom_to_string(&self) -> &str {
match self {
Self::Custom(custom) => custom,
_ => panic!("not Custom::Custom"),
}
}
}
fn main() {
// custom ends up being default string which is an empty string
let expected = ["user", "my_id", "name", "surname", "EM`ail", ""];
Custom::iter()
.map(|var| var.to_string())
.zip(expected)
.for_each(|(iden, exp)| assert_eq!(iden, exp));
let mut string = String::new();
PostgresQueryBuilder.prepare_iden(&Custom::Email("".to_owned()).into_iden(), &mut string);
assert_eq!(string, "\"EM`ail\"");
let mut string = String::new();
MysqlQueryBuilder.prepare_iden(&Custom::Email("".to_owned()).into_iden(), &mut string);
assert_eq!(string, "`EM``ail`");
assert!(matches!(Custom::FirstName.quoted(), Cow::Owned(_)));
}
@@ -0,0 +1,9 @@
use sea_query::Iden;
#[derive(Copy, Clone, Iden)]
#[iden = "another_name"]
pub struct CustomName;
fn main() {
assert_eq!(CustomName.to_string(), "another_name");
}
@@ -0,0 +1,39 @@
use sea_query::Iden;
use strum::{EnumIter, IntoEnumIterator};
#[derive(Iden, EnumIter)]
// Outer iden attributes overrides what's used for "Table"...
#[iden = "user"]
enum Custom {
Table,
#[iden = "my_id"]
Id,
#[iden = "name"]
FirstName,
#[iden = "surname"]
LastName,
// Custom casing if needed
#[iden = "EMail"]
// the tuple value will be ignored
Email(String),
// Custom method
#[method = "custom_to_string"]
Custom(String),
}
impl Custom {
pub fn custom_to_string(&self) -> &str {
match self {
Self::Custom(custom) => custom,
_ => panic!("not Custom::Custom"),
}
}
}
fn main() {
// custom ends up being default string which is an empty string
let expected = ["user", "my_id", "name", "surname", "EMail", ""];
Custom::iter().zip(expected).for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
})
}
@@ -0,0 +1,19 @@
use sea_query::Iden;
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, Iden, EnumIter)]
enum Something {
// ...the Table can also be overwritten like this
#[iden = "something_else"]
Table,
Id,
AssetName,
UserId,
}
fn main() {
let expected = ["something_else", "id", "asset_name", "user_id"];
Something::iter().zip(expected).for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
})
}
@@ -0,0 +1,25 @@
use sea_query::{Iden, IntoIden, MysqlQueryBuilder, PostgresQueryBuilder, QuotedBuilder};
#[derive(Copy, Clone, Iden)]
pub struct SomeType;
#[derive(Copy, Clone, Iden)]
#[iden(rename = "Hel`lo")]
pub struct SomeTypeWithRename;
fn main() {
assert_eq!(SomeType.to_string(), "some_type");
assert_eq!(SomeTypeWithRename.to_string(), "Hel`lo");
let mut string = String::new();
PostgresQueryBuilder.prepare_iden(&SomeType.into_iden(), &mut string);
assert_eq!(string, "\"some_type\"");
let mut string = String::new();
PostgresQueryBuilder.prepare_iden(&SomeTypeWithRename.into_iden(), &mut string);
assert_eq!(string, "\"Hel`lo\"");
let mut string = String::new();
MysqlQueryBuilder.prepare_iden(&SomeTypeWithRename.into_iden(), &mut string);
assert_eq!(string, "`Hel``lo`");
}
@@ -0,0 +1,18 @@
use sea_query::Iden;
use strum::{EnumIter, IntoEnumIterator};
#[derive(Copy, Clone, Iden, EnumIter)]
enum User {
Table,
Id,
FirstName,
LastName,
Email,
}
fn main() {
let expected = ["user", "id", "first_name", "last_name", "email"];
User::iter().zip(expected).for_each(|(var, exp)| {
assert_eq!(var.to_string(), exp);
});
}
@@ -0,0 +1,11 @@
#[test]
fn build_tests() {
let t = trybuild::TestCases::new();
t.compile_fail("./tests/compile-fail/*.rs");
t.compile_fail("./tests/compile-fail/enum_def/*.rs");
// all of these are exactly the same as the examples in `examples/derive.rs`
t.pass("./tests/pass/*.rs");
t.pass("./tests/pass/enum_def/*.rs");
t.pass("./tests/pass-static/*.rs");
}