Skip to content

Commit 3938e83

Browse files
committed
Start default impl.
1 parent 16d1326 commit 3938e83

24 files changed

+439
-334
lines changed

Cargo.lock

Lines changed: 54 additions & 111 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ serde = { version = "1.0.219", features = ["derive"] }
2020
serde_json = "1.0.140"
2121
serde_yaml = "0.9.33"
2222
serde_yml = "0.0.12"
23-
starbase_sandbox = "0.9.3"
24-
syn = "2.0.101"
25-
toml = "0.8.22"
23+
starbase_sandbox = "0.9.4"
24+
syn = "2.0.104"
25+
toml = "0.8.23"
2626
tracing = "0.1.41"
2727
url = "2.5.4"

crates/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ schematic_core = { path = ".", features = [
2626
"tracing",
2727
"validate",
2828
] }
29+
prettyplease = "0.2.35"
2930
syn = { workspace = true, features = ["full", "extra-traits"] }
3031
starbase_sandbox = { workspace = true }
3132

crates/core/src/container.rs

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use crate::args::{PartialArg, SerdeContainerArgs, SerdeRenameArg};
22
use crate::field::Field;
3-
use crate::utils::is_inheritable_attribute;
3+
use crate::utils::{impl_struct_default, is_inheritable_attribute};
44
use crate::variant::Variant;
55
use darling::FromDeriveInput;
66
use proc_macro2::TokenStream;
7-
use quote::{ToTokens, quote};
7+
use quote::{ToTokens, format_ident, quote};
88
use std::rc::Rc;
99
use syn::{Attribute, Data, DeriveInput, ExprPath, Fields, Ident, Visibility};
1010

@@ -172,6 +172,105 @@ impl Container {
172172
#(#meta),*
173173
}
174174
}
175+
176+
pub fn impl_partial(&self) -> TokenStream {
177+
let base_name = &self.ident;
178+
let partial_name = format_ident!("Partial{base_name}");
179+
let context = match self.args.context.as_ref() {
180+
Some(ctx) => quote! { #ctx },
181+
None => quote! { () },
182+
};
183+
184+
let default_values_method = self.impl_partial_default_values();
185+
186+
quote! {
187+
#[automatically_derived]
188+
impl schematic::PartialConfig for #partial_name {
189+
type Context = #context;
190+
191+
#default_values_method
192+
}
193+
194+
#[automatically_derived]
195+
impl schematic::Config for #base_name {
196+
// TODO
197+
}
198+
199+
#[automatically_derived]
200+
impl Default for #base_name {
201+
fn default() -> Self {
202+
<Self as schematic::Config>::from_partial(
203+
<Self as schematic::Config>::default_partial()
204+
)
205+
}
206+
}
207+
}
208+
}
209+
210+
pub fn impl_partial_default_values(&self) -> TokenStream {
211+
let inner = match &self.inner {
212+
ContainerInner::NamedStruct { fields } => {
213+
let mut rows = vec![];
214+
215+
for field in fields {
216+
if let Some(value) = field.impl_partial_default_value() {
217+
let name = field.ident.as_ref().unwrap();
218+
219+
rows.push(quote! {
220+
#name: #value,
221+
});
222+
}
223+
}
224+
225+
if rows.is_empty() {
226+
return quote! {};
227+
}
228+
229+
let default_row = impl_struct_default(rows.len() != fields.len());
230+
231+
quote! {
232+
Ok(Some(Self {
233+
#(#rows)*
234+
#default_row
235+
}))
236+
}
237+
}
238+
ContainerInner::UnnamedStruct { fields } => {
239+
let mut rows = vec![];
240+
let mut all_none = true;
241+
242+
for field in fields {
243+
if let Some(value) = field.impl_partial_default_value() {
244+
all_none = false;
245+
246+
rows.push(quote! {
247+
#value
248+
});
249+
} else {
250+
rows.push(quote! { None })
251+
}
252+
}
253+
254+
if all_none {
255+
return quote! {};
256+
}
257+
258+
quote! {
259+
Ok(Some(Self(
260+
#(#rows),*
261+
)))
262+
}
263+
}
264+
ContainerInner::Enum { .. } => todo!(),
265+
ContainerInner::UnitEnum { .. } => todo!(),
266+
};
267+
268+
quote! {
269+
fn default_values(context: &Self::Context) -> std::result::Result<Option<Self>, schematic::ConfigError> {
270+
#inner
271+
}
272+
}
273+
}
175274
}
176275

177276
impl ToTokens for Container {

crates/core/src/field.rs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ use crate::field_value::FieldValue;
44
use crate::utils::{preserve_str_literal, to_type_string};
55
use darling::{FromAttributes, FromMeta};
66
use proc_macro2::TokenStream;
7-
use quote::{ToTokens, quote};
7+
use quote::ToTokens;
88
use std::ops::Deref;
99
use std::rc::Rc;
10-
use syn::{
11-
Attribute, Expr, ExprPath, Field as NativeField, FieldMutability, Ident, Visibility, parse_str,
12-
};
10+
use syn::{Attribute, Expr, ExprPath, Field as NativeField, FieldMutability, Ident, Visibility};
1311

1412
// #[setting(nested)]
1513
#[derive(Debug)]
@@ -183,10 +181,6 @@ impl Field {
183181

184182
// nested
185183
if self.args.nested.is_some() {
186-
if self.args.default.is_some() {
187-
panic!("Cannot use `default` with `nested`.");
188-
}
189-
190184
#[cfg(feature = "env")]
191185
if self.args.env.is_some() {
192186
panic!("Cannot use `env` with `nested`, use `env_prefix` instead?");
@@ -203,25 +197,31 @@ impl Field {
203197
}
204198
}
205199

206-
impl ToTokens for Field {
207-
fn to_tokens(&self, tokens: &mut TokenStream) {
208-
let mut value = self.value.ty_string.clone();
200+
// impl ToTokens for Field {
201+
// fn to_tokens(&self, tokens: &mut TokenStream) {
202+
// let mut value = self.value.ty_string.clone();
209203

210-
if let Some(nested_ident) = &self.value.nested_ident {
211-
let ident = nested_ident.to_string();
204+
// if let Some(nested_ident) = &self.value.nested_ident {
205+
// let ident = nested_ident.to_string();
212206

213-
value = value.replace(&ident, &format!("<{ident} as schematic::Config>::Partial"));
214-
}
207+
// value = value.replace(&ident, &format!("<{ident} as schematic::Config>::Partial"));
208+
// }
215209

216-
if !self.value.is_outer_option_wrapped() {
217-
value = format!("Option<{value}>");
218-
}
210+
// if !self.value.is_outer_option_wrapped() {
211+
// value = format!("Option<{value}>");
212+
// }
213+
214+
// let key = self.ident.as_ref().unwrap();
215+
// let value: TokenStream = parse_str(&value).unwrap();
219216

220-
let key = self.ident.as_ref().unwrap();
221-
let value: TokenStream = parse_str(&value).unwrap();
217+
// tokens.extend(quote! {
218+
// pub #key: #value,
219+
// });
220+
// }
221+
// }
222222

223-
tokens.extend(quote! {
224-
pub #key: #value,
225-
});
223+
impl Field {
224+
pub fn impl_partial_default_value(&self) -> Option<TokenStream> {
225+
self.value.impl_partial_default_value(&self.args)
226226
}
227227
}

crates/core/src/field_value.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use crate::field::FieldNestedArg;
1+
use crate::field::{FieldArgs, FieldNestedArg};
22
use crate::utils::to_type_string;
3-
use quote::ToTokens;
4-
use syn::{GenericArgument, Ident, PathArguments, PathSegment, Type};
3+
use proc_macro2::TokenStream;
4+
use quote::{ToTokens, format_ident, quote};
5+
use syn::{Expr, GenericArgument, Ident, Lit, PathArguments, PathSegment, Type};
56

67
#[derive(Debug, PartialEq)]
78
pub enum Layer {
@@ -82,6 +83,66 @@ impl FieldValue {
8283
.first()
8384
.is_some_and(|wrapper| *wrapper == Layer::Option)
8485
}
86+
87+
pub fn impl_partial_default_value(&self, field_args: &FieldArgs) -> Option<TokenStream> {
88+
if self.is_outer_option_wrapped() {
89+
return None;
90+
};
91+
92+
// Extract the inner value first
93+
let mut value = if let Some(nested_ident) = &self.nested_ident {
94+
if field_args.default.is_some() {
95+
panic!("Cannot use `default` with `nested`.");
96+
}
97+
98+
quote! {
99+
<#nested_ident as schematic::PartialConfig>::default_values(content)?
100+
}
101+
} else if let Some(expr) = &field_args.default {
102+
match expr {
103+
Expr::Array(_) | Expr::Call(_) | Expr::Macro(_) | Expr::Tuple(_) => {
104+
quote! { #expr }
105+
}
106+
Expr::Path(func) => {
107+
quote! { schematic::internal::handle_default_result(#func(context))? }
108+
}
109+
Expr::Lit(lit) => match &lit.lit {
110+
Lit::Str(string) => quote! {
111+
schematic::internal::handle_default_result(std::convert::TryFrom::try_from(#string))?
112+
},
113+
other => quote! { #other },
114+
},
115+
invalid => {
116+
panic!(
117+
"Unsupported default value ({invalid:?}). May only provide literals, primitives, arrays, or tuples."
118+
);
119+
}
120+
}
121+
} else {
122+
quote! {
123+
Default::default()
124+
}
125+
};
126+
127+
// Then wrap with each layer
128+
for layer in self.layers.iter().rev() {
129+
value = match layer {
130+
Layer::Arc => quote! { Arc::new(#value) },
131+
Layer::Box => quote! { Box::new(#value) },
132+
Layer::Option => quote! { Some(#value) },
133+
Layer::Rc => quote! { Rc::new(#value) },
134+
Layer::Map(name) | Layer::Set(name) | Layer::Vec(name) | Layer::Unknown(name) => {
135+
let collection = format_ident!("{name}");
136+
137+
quote! { #collection::default() }
138+
}
139+
};
140+
}
141+
142+
Some(quote! {
143+
Some(#value)
144+
})
145+
}
85146
}
86147

87148
fn extract_type_information(

crates/core/src/utils.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use proc_macro2::TokenStream;
2+
use quote::quote;
23
use syn::{Attribute, Expr, Meta, Path};
34

45
pub fn get_meta_path(meta: &Meta) -> &Path {
@@ -36,3 +37,11 @@ pub fn to_type_string(ts: TokenStream) -> String {
3637
.replace("> ", ">")
3738
.replace(" >", ">")
3839
}
40+
41+
pub fn impl_struct_default(show: bool) -> TokenStream {
42+
if show {
43+
quote! { ..Default::default() }
44+
} else {
45+
quote! {}
46+
}
47+
}

0 commit comments

Comments
 (0)