Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit790af54

Browse files
authored
Merge pull request#1008 from JaydenElliott/feature/rename_all_attr
added `rename_all` container attribute for enums and structs
2 parents6f19bb9 +f4b181a commit790af54

File tree

11 files changed

+293
-29
lines changed

11 files changed

+293
-29
lines changed

‎postgres-derive-test/src/composites.rs‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,49 @@ fn name_overrides() {
8989
);
9090
}
9191

92+
#[test]
93+
fnrename_all_overrides(){
94+
#[derive(FromSql,ToSql,Debug,PartialEq)]
95+
#[postgres(name ="inventory_item", rename_all ="SCREAMING_SNAKE_CASE")]
96+
structInventoryItem{
97+
name:String,
98+
supplier_id:i32,
99+
#[postgres(name ="Price")]
100+
price:Option<f64>,
101+
}
102+
103+
letmut conn =Client::connect("user=postgres host=localhost port=5433",NoTls).unwrap();
104+
conn.batch_execute(
105+
"CREATE TYPE pg_temp.inventory_item AS (
106+
\"NAME\" TEXT,
107+
\"SUPPLIER_ID\" INT,
108+
\"Price\" DOUBLE PRECISION
109+
);",
110+
)
111+
.unwrap();
112+
113+
let item =InventoryItem{
114+
name:"foobar".to_owned(),
115+
supplier_id:100,
116+
price:Some(15.50),
117+
};
118+
119+
let item_null =InventoryItem{
120+
name:"foobar".to_owned(),
121+
supplier_id:100,
122+
price:None,
123+
};
124+
125+
test_type(
126+
&mut conn,
127+
"inventory_item",
128+
&[
129+
(item,"ROW('foobar', 100, 15.50)"),
130+
(item_null,"ROW('foobar', 100, NULL)"),
131+
],
132+
);
133+
}
134+
92135
#[test]
93136
fnwrong_name(){
94137
#[derive(FromSql,ToSql,Debug,PartialEq)]

‎postgres-derive-test/src/enums.rs‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,35 @@ fn name_overrides() {
5353
);
5454
}
5555

56+
#[test]
57+
fnrename_all_overrides(){
58+
#[derive(Debug,ToSql,FromSql,PartialEq)]
59+
#[postgres(name ="mood", rename_all ="snake_case")]
60+
enumMood{
61+
VerySad,
62+
#[postgres(name ="okay")]
63+
Ok,
64+
VeryHappy,
65+
}
66+
67+
letmut conn =Client::connect("user=postgres host=localhost port=5433",NoTls).unwrap();
68+
conn.execute(
69+
"CREATE TYPE pg_temp.mood AS ENUM ('very_sad', 'okay', 'very_happy')",
70+
&[],
71+
)
72+
.unwrap();
73+
74+
test_type(
75+
&mut conn,
76+
"mood",
77+
&[
78+
(Mood::VerySad,"'very_sad'"),
79+
(Mood::Ok,"'okay'"),
80+
(Mood::VeryHappy,"'very_happy'"),
81+
],
82+
);
83+
}
84+
5685
#[test]
5786
fnwrong_name(){
5887
#[derive(Debug,ToSql,FromSql,PartialEq)]

‎postgres-derive/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ test = false
1515
syn ="2.0"
1616
proc-macro2 ="1.0"
1717
quote ="1.0"
18+
heck ="0.4"

‎postgres-derive/src/case.rs‎

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#[allow(deprecated, unused_imports)]
2+
use std::ascii::AsciiExt;
3+
4+
use heck::{
5+
ToKebabCase,ToLowerCamelCase,ToShoutyKebabCase,ToShoutySnakeCase,ToSnakeCase,ToTrainCase,
6+
ToUpperCamelCase,
7+
};
8+
9+
useself::RenameRule::*;
10+
11+
/// The different possible ways to change case of fields in a struct, or variants in an enum.
12+
#[allow(clippy::enum_variant_names)]
13+
#[derive(Copy,Clone,PartialEq)]
14+
pubenumRenameRule{
15+
/// Rename direct children to "lowercase" style.
16+
LowerCase,
17+
/// Rename direct children to "UPPERCASE" style.
18+
UpperCase,
19+
/// Rename direct children to "PascalCase" style, as typically used for
20+
/// enum variants.
21+
PascalCase,
22+
/// Rename direct children to "camelCase" style.
23+
CamelCase,
24+
/// Rename direct children to "snake_case" style, as commonly used for
25+
/// fields.
26+
SnakeCase,
27+
/// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly
28+
/// used for constants.
29+
ScreamingSnakeCase,
30+
/// Rename direct children to "kebab-case" style.
31+
KebabCase,
32+
/// Rename direct children to "SCREAMING-KEBAB-CASE" style.
33+
ScreamingKebabCase,
34+
35+
/// Rename direct children to "Train-Case" style.
36+
TrainCase,
37+
}
38+
39+
pubconstRENAME_RULES:&[&str] =&[
40+
"lowercase",
41+
"UPPERCASE",
42+
"PascalCase",
43+
"camelCase",
44+
"snake_case",
45+
"SCREAMING_SNAKE_CASE",
46+
"kebab-case",
47+
"SCREAMING-KEBAB-CASE",
48+
"Train-Case",
49+
];
50+
51+
implRenameRule{
52+
pubfnfrom_str(rule:&str) ->Option<RenameRule>{
53+
match rule{
54+
"lowercase" =>Some(LowerCase),
55+
"UPPERCASE" =>Some(UpperCase),
56+
"PascalCase" =>Some(PascalCase),
57+
"camelCase" =>Some(CamelCase),
58+
"snake_case" =>Some(SnakeCase),
59+
"SCREAMING_SNAKE_CASE" =>Some(ScreamingSnakeCase),
60+
"kebab-case" =>Some(KebabCase),
61+
"SCREAMING-KEBAB-CASE" =>Some(ScreamingKebabCase),
62+
"Train-Case" =>Some(TrainCase),
63+
_ =>None,
64+
}
65+
}
66+
/// Apply a renaming rule to an enum or struct field, returning the version expected in the source.
67+
pubfnapply_to_field(&self,variant:&str) ->String{
68+
match*self{
69+
LowerCase => variant.to_lowercase(),
70+
UpperCase => variant.to_uppercase(),
71+
PascalCase => variant.to_upper_camel_case(),
72+
CamelCase => variant.to_lower_camel_case(),
73+
SnakeCase => variant.to_snake_case(),
74+
ScreamingSnakeCase => variant.to_shouty_snake_case(),
75+
KebabCase => variant.to_kebab_case(),
76+
ScreamingKebabCase => variant.to_shouty_kebab_case(),
77+
TrainCase => variant.to_train_case(),
78+
}
79+
}
80+
}
81+
82+
#[test]
83+
fnrename_field(){
84+
for&(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab)in&[
85+
(
86+
"Outcome","outcome","OUTCOME","outcome","outcome","OUTCOME","outcome","OUTCOME",
87+
),
88+
(
89+
"VeryTasty",
90+
"verytasty",
91+
"VERYTASTY",
92+
"veryTasty",
93+
"very_tasty",
94+
"VERY_TASTY",
95+
"very-tasty",
96+
"VERY-TASTY",
97+
),
98+
("A","a","A","a","a","A","a","A"),
99+
("Z42","z42","Z42","z42","z42","Z42","z42","Z42"),
100+
]{
101+
assert_eq!(LowerCase.apply_to_field(original), lower);
102+
assert_eq!(UpperCase.apply_to_field(original), upper);
103+
assert_eq!(PascalCase.apply_to_field(original), original);
104+
assert_eq!(CamelCase.apply_to_field(original), camel);
105+
assert_eq!(SnakeCase.apply_to_field(original), snake);
106+
assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
107+
assert_eq!(KebabCase.apply_to_field(original), kebab);
108+
assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
109+
}
110+
}

‎postgres-derive/src/composites.rs‎

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use syn::{
44
TypeParamBound,
55
};
66

7-
usecrate::overrides::Overrides;
7+
usecrate::{case::RenameRule,overrides::Overrides};
88

99
pubstructField{
1010
pubname:String,
@@ -13,18 +13,26 @@ pub struct Field {
1313
}
1414

1515
implField{
16-
pubfnparse(raw:&syn::Field) ->Result<Field,Error>{
17-
let overrides =Overrides::extract(&raw.attrs)?;
18-
16+
pubfnparse(raw:&syn::Field,rename_all:Option<RenameRule>) ->Result<Field,Error>{
17+
let overrides =Overrides::extract(&raw.attrs,false)?;
1918
let ident = raw.ident.as_ref().unwrap().clone();
20-
Ok(Field{
21-
name: overrides.name.unwrap_or_else(||{
19+
20+
// field level name override takes precendence over container level rename_all override
21+
let name =match overrides.name{
22+
Some(n) => n,
23+
None =>{
2224
let name = ident.to_string();
23-
match name.strip_prefix("r#"){
24-
Some(name) => name.to_string(),
25-
None => name,
25+
let stripped = name.strip_prefix("r#").map(String::from).unwrap_or(name);
26+
27+
match rename_all{
28+
Some(rule) => rule.apply_to_field(&stripped),
29+
None => stripped,
2630
}
27-
}),
31+
}
32+
};
33+
34+
Ok(Field{
35+
name,
2836
ident,
2937
type_: raw.ty.clone(),
3038
})

‎postgres-derive/src/enums.rs‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use syn::{Error,Fields,Ident};
22

3-
usecrate::overrides::Overrides;
3+
usecrate::{case::RenameRule,overrides::Overrides};
44

55
pubstructVariant{
66
pubident:Ident,
77
pubname:String,
88
}
99

1010
implVariant{
11-
pubfnparse(raw:&syn::Variant) ->Result<Variant,Error>{
11+
pubfnparse(raw:&syn::Variant,rename_all:Option<RenameRule>) ->Result<Variant,Error>{
1212
match raw.fields{
1313
Fields::Unit =>{}
1414
_ =>{
@@ -18,11 +18,16 @@ impl Variant {
1818
))
1919
}
2020
}
21+
let overrides =Overrides::extract(&raw.attrs,false)?;
2122

22-
let overrides =Overrides::extract(&raw.attrs)?;
23+
// variant level name override takes precendence over container level rename_all override
24+
let name = overrides.name.unwrap_or_else(||match rename_all{
25+
Some(rule) => rule.apply_to_field(&raw.ident.to_string()),
26+
None => raw.ident.to_string(),
27+
});
2328
Ok(Variant{
2429
ident: raw.ident.clone(),
25-
name: overrides.name.unwrap_or_else(|| raw.ident.to_string()),
30+
name,
2631
})
2732
}
2833
}

‎postgres-derive/src/fromsql.rs‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@ use crate::enums::Variant;
1515
usecrate::overrides::Overrides;
1616

1717
pubfnexpand_derive_fromsql(input:DeriveInput) ->Result<TokenStream,Error>{
18-
let overrides =Overrides::extract(&input.attrs)?;
18+
let overrides =Overrides::extract(&input.attrs,true)?;
1919

20-
if overrides.name.is_some() && overrides.transparent{
20+
if(overrides.name.is_some() || overrides.rename_all.is_some()) && overrides.transparent{
2121
returnErr(Error::new_spanned(
2222
&input,
23-
"#[postgres(transparent)] is not allowed with #[postgres(name =\"...\")]",
23+
"#[postgres(transparent)] is not allowed with #[postgres(name =\"...\")] or #[postgres(rename_all =\"...\")]",
2424
));
2525
}
2626

27-
let name = overrides.name.unwrap_or_else(|| input.ident.to_string());
27+
let name = overrides
28+
.name
29+
.clone()
30+
.unwrap_or_else(|| input.ident.to_string());
2831

2932
let(accepts_body, to_sql_body) =if overrides.transparent{
3033
match input.data{
@@ -51,7 +54,7 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result<TokenStream, Error> {
5154
let variants = data
5255
.variants
5356
.iter()
54-
.map(Variant::parse)
57+
.map(|variant|Variant::parse(variant, overrides.rename_all))
5558
.collect::<Result<Vec<_>,_>>()?;
5659
(
5760
accepts::enum_body(&name,&variants),
@@ -75,7 +78,7 @@ pub fn expand_derive_fromsql(input: DeriveInput) -> Result<TokenStream, Error> {
7578
let fields = fields
7679
.named
7780
.iter()
78-
.map(Field::parse)
81+
.map(|field|Field::parse(field, overrides.rename_all))
7982
.collect::<Result<Vec<_>,_>>()?;
8083
(
8184
accepts::composite_body(&name,"FromSql",&fields),

‎postgres-derive/src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use proc_macro::TokenStream;
77
use syn::parse_macro_input;
88

99
mod accepts;
10+
mod case;
1011
mod composites;
1112
mod enums;
1213
mod fromsql;

‎postgres-derive/src/overrides.rs‎

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
use syn::punctuated::Punctuated;
22
use syn::{Attribute,Error,Expr,ExprLit,Lit,Meta,Token};
33

4+
usecrate::case::{RenameRule,RENAME_RULES};
5+
46
pubstructOverrides{
57
pubname:Option<String>,
8+
pubrename_all:Option<RenameRule>,
69
pubtransparent:bool,
710
}
811

912
implOverrides{
10-
pubfnextract(attrs:&[Attribute]) ->Result<Overrides,Error>{
13+
pubfnextract(attrs:&[Attribute],container_attr:bool) ->Result<Overrides,Error>{
1114
letmut overrides =Overrides{
1215
name:None,
16+
rename_all:None,
1317
transparent:false,
1418
};
1519

@@ -28,7 +32,15 @@ impl Overrides {
2832
for itemin nested{
2933
match item{
3034
Meta::NameValue(meta) =>{
31-
if !meta.path.is_ident("name"){
35+
let name_override = meta.path.is_ident("name");
36+
let rename_all_override = meta.path.is_ident("rename_all");
37+
if !container_attr && rename_all_override{
38+
returnErr(Error::new_spanned(
39+
&meta.path,
40+
"rename_all is a container attribute",
41+
));
42+
}
43+
if !name_override && !rename_all_override{
3244
returnErr(Error::new_spanned(&meta.path,"unknown override"));
3345
}
3446

@@ -41,7 +53,25 @@ impl Overrides {
4153
}
4254
};
4355

44-
overrides.name =Some(value);
56+
if name_override{
57+
overrides.name =Some(value);
58+
}elseif rename_all_override{
59+
let rename_rule =RenameRule::from_str(&value).ok_or_else(||{
60+
Error::new_spanned(
61+
&meta.value,
62+
format!(
63+
"invalid rename_all rule, expected one of: {}",
64+
RENAME_RULES
65+
.iter()
66+
.map(|rule| format!("\"{}\"", rule))
67+
.collect::<Vec<_>>()
68+
.join(", ")
69+
),
70+
)
71+
})?;
72+
73+
overrides.rename_all =Some(rename_rule);
74+
}
4575
}
4676
Meta::Path(path) =>{
4777
if !path.is_ident("transparent"){

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp