difftreelog
style fix clippy warnings
in: master
5 files changed
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -15,12 +15,12 @@
#[derive(Clone, Copy)]
pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);
impl<'l> CallLocation<'l> {
- pub fn new(loc: &'l ExprLocation) -> Self {
+ pub const fn new(loc: &'l ExprLocation) -> Self {
Self(Some(loc))
}
}
impl CallLocation<'static> {
- pub fn native() -> Self {
+ pub const fn native() -> Self {
Self(None)
}
}
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -20,7 +20,7 @@
Val::Arr(a) => {
let mut out = Vec::with_capacity(a.len());
for item in a.iter() {
- out.push((&item?).try_into()?);
+ out.push(item?.try_into()?);
}
Self::Array(out)
}
@@ -29,8 +29,8 @@
for key in o.fields() {
out.insert(
(&key as &str).into(),
- (&o.get(key)?
- .expect("key is present in fields, so value should exist"))
+ o.get(key)?
+ .expect("key is present in fields, so value should exist")
.try_into()?,
);
}
@@ -40,6 +40,13 @@
})
}
}
+impl TryFrom<Val> for Value {
+ type Error = LocError;
+
+ fn try_from(value: Val) -> Result<Self, Self::Error> {
+ <Self as TryFrom<&Val>>::try_from(&value)
+ }
+}
impl TryFrom<&Value> for Val {
type Error = LocError;
@@ -68,3 +75,10 @@
})
}
}
+impl TryFrom<Value> for Val {
+ type Error = LocError;
+
+ fn try_from(value: Value) -> Result<Self, Self::Error> {
+ <Self as TryFrom<&Value>>::try_from(&value)
+ }
+}
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -442,7 +442,7 @@
fn try_from(value: Val) -> Result<Self, Self::Error> {
<Self as Typed>::TYPE.check(&value)?;
match value {
- Val::Func(FuncVal::Normal(desc)) => Ok(desc.clone()),
+ Val::Func(FuncVal::Normal(desc)) => Ok(desc),
Val::Func(_) => throw!(RuntimeError("expected normal function, not builtin".into())),
_ => unreachable!(),
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -126,15 +126,6 @@
}
}
-impl PartialEq for FuncVal {
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (Self::Normal(a), Self::Normal(b)) => a == b,
- (Self::StaticBuiltin(an), Self::StaticBuiltin(bn)) => std::ptr::eq(*an, *bn),
- (..) => false,
- }
- }
-}
impl FuncVal {
pub fn args_len(&self) -> usize {
match self {
@@ -353,13 +344,13 @@
}
impl Val {
- pub fn as_bool(&self) -> Option<bool> {
+ pub const fn as_bool(&self) -> Option<bool> {
match self {
Val::Bool(v) => Some(*v),
_ => None,
}
}
- pub fn as_null(&self) -> Option<()> {
+ pub const fn as_null(&self) -> Option<()> {
match self {
Val::Null => Some(()),
_ => None,
@@ -371,7 +362,7 @@
_ => None,
}
}
- pub fn as_num(&self) -> Option<f64> {
+ pub const fn as_num(&self) -> Option<f64> {
match self {
Val::Num(n) => Some(*n),
_ => None,
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth1use proc_macro2::TokenStream;2use quote::quote;3use syn::{4 parenthesized,5 parse::{Parse, ParseStream},6 parse_macro_input,7 punctuated::Punctuated,8 spanned::Spanned,9 token::Comma,10 Attribute, DeriveInput, Error, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, Path,11 PathArguments, Result, ReturnType, Token, Type,12};1314fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>15where16 Ident: PartialEq<I>,17{18 let attrs = attrs19 .iter()20 .filter(|a| a.path.is_ident(&ident))21 .collect::<Vec<_>>();22 if attrs.len() > 1 {23 return Err(Error::new(24 attrs[1].span(),25 "this attribute may be specified only once",26 ));27 } else if attrs.is_empty() {28 return Ok(None);29 }30 let attr = attrs[0];31 let attr = attr.parse_args::<A>()?;3233 Ok(Some(attr))34}3536fn path_is(path: &Path, needed: &str) -> bool {37 path.leading_colon.is_none()38 && path.segments.len() >= 139 && path.segments.iter().last().unwrap().ident == needed40}4142fn type_is_path<'ty>(ty: &'ty Type, needed: &str) -> Option<&'ty PathArguments> {43 match ty {44 Type::Path(path) if path.qself.is_none() && path_is(&path.path, needed) => {45 let args = &path.path.segments.iter().last().unwrap().arguments;46 Some(args)47 }48 _ => None,49 }50}5152fn extract_type_from_option(ty: &Type) -> Result<Option<&Type>> {53 Ok(if let Some(args) = type_is_path(ty, "Option") {54 // It should have only on angle-bracketed param ("<String>"):55 let generic_arg = match args {56 PathArguments::AngleBracketed(params) => params.args.iter().next().unwrap(),57 _ => return Err(Error::new(args.span(), "missing option generic")),58 };59 // This argument must be a type:60 match generic_arg {61 GenericArgument::Type(ty) => Some(ty),62 _ => {63 return Err(Error::new(64 generic_arg.span(),65 "option generic should be a type",66 ))67 }68 }69 } else {70 None71 })72}7374struct Field {75 name: Ident,76 _colon: Token![:],77 ty: Type,78}79impl Parse for Field {80 fn parse(input: ParseStream) -> syn::Result<Self> {81 Ok(Self {82 name: input.parse()?,83 _colon: input.parse()?,84 ty: input.parse()?,85 })86 }87}8889mod kw {90 syn::custom_keyword!(fields);91 syn::custom_keyword!(rename);92 syn::custom_keyword!(flatten);93}9495struct EmptyAttr;96impl Parse for EmptyAttr {97 fn parse(_input: ParseStream) -> Result<Self> {98 Ok(Self)99 }100}101102struct BuiltinAttrs {103 fields: Vec<Field>,104}105impl Parse for BuiltinAttrs {106 fn parse(input: ParseStream) -> syn::Result<Self> {107 if input.is_empty() {108 return Ok(Self { fields: Vec::new() });109 }110 input.parse::<kw::fields>()?;111 let fields;112 parenthesized!(fields in input);113 let p = Punctuated::<Field, Comma>::parse_terminated(&fields)?;114 Ok(Self {115 fields: p.into_iter().collect(),116 })117 }118}119120enum ArgInfo {121 Normal {122 ty: Type,123 is_option: bool,124 name: String,125 // ident: Ident,126 },127 Lazy {128 is_option: bool,129 name: String,130 },131 Location,132 This,133}134135impl ArgInfo {136 fn parse(arg: &FnArg) -> Result<Self> {137 let typed = match arg {138 FnArg::Receiver(_) => unreachable!(),139 FnArg::Typed(a) => a,140 };141 let ident = match &typed.pat as &Pat {142 Pat::Ident(i) => i.ident.clone(),143 _ => {144 return Err(Error::new(145 typed.pat.span(),146 "arg should be plain identifier",147 ))148 }149 };150 let ty = &typed.ty as &Type;151 if type_is_path(&ty, "CallLocation").is_some() {152 return Ok(Self::Location);153 } else if type_is_path(&ty, "Self").is_some() {154 return Ok(Self::This);155 } else if type_is_path(&ty, "LazyVal").is_some() {156 return Ok(Self::Lazy {157 is_option: false,158 name: ident.to_string(),159 });160 }161162 let (is_option, ty) = if let Some(ty) = extract_type_from_option(&ty)? {163 if type_is_path(&ty, "LazyVal").is_some() {164 return Ok(Self::Lazy {165 is_option: true,166 name: ident.to_string(),167 });168 }169170 (true, ty.clone())171 } else {172 (false, ty.clone())173 };174175 Ok(Self::Normal {176 ty,177 is_option,178 name: ident.to_string(),179 // ident,180 })181 }182}183184#[proc_macro_attribute]185pub fn builtin(186 attr: proc_macro::TokenStream,187 item: proc_macro::TokenStream,188) -> proc_macro::TokenStream {189 let attr = parse_macro_input!(attr as BuiltinAttrs);190 let item: ItemFn = parse_macro_input!(item);191192 match builtin_inner(attr, item) {193 Ok(v) => v.into(),194 Err(e) => e.into_compile_error().into(),195 }196}197198fn builtin_inner(attr: BuiltinAttrs, fun: ItemFn) -> syn::Result<TokenStream> {199 let result = match fun.sig.output {200 ReturnType::Default => {201 return Err(Error::new(202 fun.sig.span(),203 "builtin should return something",204 ))205 }206 ReturnType::Type(_, ref ty) => ty.clone(),207 };208209 let args = fun210 .sig211 .inputs212 .iter()213 .map(|a| ArgInfo::parse(a))214 .collect::<Result<Vec<_>>>()?;215216 let params_desc = args.iter().flat_map(|a| match a {217 ArgInfo::Normal {218 is_option, name, ..219 }220 | ArgInfo::Lazy { is_option, name } => Some(quote! {221 BuiltinParam {222 name: std::borrow::Cow::Borrowed(#name),223 has_default: #is_option,224 }225 }),226 ArgInfo::Location => None,227 ArgInfo::This => None,228 });229230 let pass = args.iter().map(|a| match a {231 ArgInfo::Normal {232 ty,233 is_option,234 name,235 // ident,236 } => {237 let eval = quote! {::jrsonnet_evaluator::push_description_frame(238 || format!("argument <{}> evaluation", #name),239 || <#ty>::try_from(value.evaluate()?),240 )?};241 if *is_option {242 quote! {if let Some(value) = parsed.get(#name) {243 Some(#eval)244 } else {245 None246 }}247 } else {248 quote! {{249 let value = parsed.get(#name).expect("args shape is checked");250 #eval251 }}252 }253 }254 ArgInfo::Lazy { is_option, name } => {255 if *is_option {256 quote! {if let Some(value) = parsed.get(#name) {257 Some(value.clone())258 } else {259 None260 }}261 } else {262 quote! {263 parsed.get(#name).expect("args shape is correct").clone()264 }265 }266 }267 ArgInfo::Location => quote! {location},268 ArgInfo::This => quote! {self},269 });270271 let fields = attr.fields.iter().map(|field| {272 let name = &field.name;273 let ty = &field.ty;274 quote! {275 pub #name: #ty,276 }277 });278279 let name = &fun.sig.ident;280 let vis = &fun.vis;281 let static_ext = if attr.fields.is_empty() {282 quote! {283 impl #name {284 pub const INST: &'static dyn StaticBuiltin = &#name {};285 }286 impl StaticBuiltin for #name {}287 }288 } else {289 quote! {}290 };291 let static_derive_copy = if attr.fields.is_empty() {292 quote! {, Copy}293 } else {294 quote! {}295 };296297 Ok(quote! {298 #fun299 #[doc(hidden)]300 #[allow(non_camel_case_types)]301 #[derive(Clone, gcmodule::Trace #static_derive_copy)]302 #vis struct #name {303 #(#fields)*304 }305 const _: () = {306 use ::jrsonnet_evaluator::{307 function::{Builtin, CallLocation, StaticBuiltin, BuiltinParam, ArgsLike, parse_builtin_call},308 error::Result, Context,309 parser::ExprLocation,310 };311 const PARAMS: &'static [BuiltinParam] = &[312 #(#params_desc),*313 ];314315 #static_ext316 impl Builtin for #name317 where318 Self: 'static319 {320 fn name(&self) -> &str {321 stringify!(#name)322 }323 fn params(&self) -> &[BuiltinParam] {324 PARAMS325 }326 fn call(&self, context: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {327 let parsed = parse_builtin_call(context, &PARAMS, args, false)?;328329 let result: #result = #name(#(#pass),*);330 let result = result?;331 result.try_into()332 }333 }334 };335 })336}337338#[derive(Default)]339struct TypedAttr {340 rename: Option<String>,341 flatten: bool,342}343impl Parse for TypedAttr {344 fn parse(input: ParseStream) -> syn::Result<Self> {345 let mut out = Self::default();346 loop {347 let lookahead = input.lookahead1();348 if lookahead.peek(kw::rename) {349 input.parse::<kw::rename>()?;350 input.parse::<Token![=]>()?;351 let name = input.parse::<LitStr>()?;352 if out.rename.is_some() {353 return Err(Error::new(354 name.span(),355 "rename attribute may only be specified once",356 ));357 }358 out.rename = Some(name.value());359 } else if lookahead.peek(kw::flatten) {360 input.parse::<kw::flatten>()?;361 out.flatten = true;362 } else if input.is_empty() {363 break;364 } else {365 return Err(lookahead.error());366 }367 if input.peek(Token![,]) {368 input.parse::<Token![,]>()?;369 } else {370 break;371 }372 }373 // input.parse::<kw::rename>()?;374 // input.parse::<Token![=]>()?;375 // let rename = input.parse::<LitStr>()?.value();376 Ok(out)377 }378}379380struct TypedField<'f>(&'f syn::Field, TypedAttr);381impl<'f> TypedField<'f> {382 fn try_new(field: &'f syn::Field) -> Result<Self> {383 let attr =384 parse_attr::<TypedAttr, _>(&field.attrs, "typed")?.unwrap_or_else(Default::default);385 if field.ident.is_none() {386 return Err(Error::new(387 field.span(),388 "this field should appear in output object, but it has no visible name",389 ));390 }391 Ok(Self(field, attr))392 }393 fn ident(&self) -> Ident {394 self.0395 .ident396 .clone()397 .expect("constructor disallows fields without name")398 }399 /// None if this field is flattened in jsonnet output400 fn name(&self) -> Option<String> {401 if self.1.flatten {402 return None;403 }404 Some(405 self.1406 .rename407 .clone()408 .unwrap_or_else(|| self.ident().to_string()),409 )410 }411412 fn expand_field(&self) -> Option<TokenStream> {413 if self.is_option() {414 return None;415 }416 let name = self.name()?;417 let ty = &self.0.ty;418 Some(quote! {419 (#name, <#ty>::TYPE)420 })421 }422 fn expand_parse(&self) -> TokenStream {423 let ident = self.ident();424 let ty = &self.0.ty;425 if self.1.flatten {426 // optional flatten is handled in same way as serde427 return if self.is_option() {428 quote! {429 #ident: <#ty>::parse(&obj).ok(),430 }431 } else {432 quote! {433 #ident: <#ty>::parse(&obj)?,434 }435 };436 };437438 let name = self.name().unwrap();439 let value = if let Some(ty) = self.as_option() {440 quote! {441 if let Some(value) = obj.get(#name.into())? {442 Some(<#ty>::try_from(vakue)?)443 } else {444 None445 }446 }447 } else {448 quote! {449 <#ty>::try_from(obj.get(#name.into())?.ok_or_else(|| Error::NoSuchField(#name.into()))?)?450 }451 };452453 quote! {454 #ident: #value,455 }456 }457 fn expand_serialize(&self) -> TokenStream {458 let ident = self.ident();459 if let Some(name) = self.name() {460 if self.is_option() {461 quote! {462 if let Some(value) = self.#ident {463 out.member(#name.into()).value(value.try_into()?);464 }465 }466 } else {467 quote! {468 out.member(#name.into()).value(self.#ident.try_into()?);469 }470 }471 } else {472 if self.is_option() {473 quote! {474 if let Some(value) = self.#ident {475 value.serialize(out)?;476 }477 }478 } else {479 quote! {480 self.#ident.serialize(out)?;481 }482 }483 }484 }485486 fn as_option(&self) -> Option<&Type> {487 extract_type_from_option(&self.0.ty).unwrap()488 }489 fn is_option(&self) -> bool {490 self.as_option().is_some()491 }492}493494#[proc_macro_derive(Typed, attributes(typed))]495pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {496 let input = parse_macro_input!(item as DeriveInput);497498 match derive_typed_inner(input) {499 Ok(v) => v.into(),500 Err(e) => e.to_compile_error().into(),501 }502}503504fn derive_typed_inner(input: DeriveInput) -> Result<TokenStream> {505 let data = match &input.data {506 syn::Data::Struct(s) => s,507 _ => return Err(Error::new(input.span(), "only structs supported")),508 };509510 let ident = &input.ident;511 let fields = data512 .fields513 .iter()514 .map(TypedField::try_new)515 .collect::<Result<Vec<_>>>()?;516517 let typed = {518 let fields = fields519 .iter()520 .flat_map(TypedField::expand_field)521 .collect::<Vec<_>>();522 let len = fields.len();523 quote! {524 const ITEMS: [(&'static str, &'static ComplexValType); #len] = [525 #(#fields,)*526 ];527 impl Typed for #ident {528 const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);529 }530 }531 };532533 let fields_parse = fields.iter().map(TypedField::expand_parse);534 let fields_serialize = fields.iter().map(TypedField::expand_serialize);535536 Ok(quote! {537 const _: () = {538 use ::jrsonnet_evaluator::{539 typed::{ComplexValType, Typed, TypedObj, CheckType},540 Val,541 error::{LocError, Error},542 ObjValueBuilder, ObjValue,543 };544545 #typed546547 impl #ident {548 fn serialize(self, out: &mut ObjValueBuilder) -> Result<(), LocError> {549 #(#fields_serialize)*550551 Ok(())552 }553 fn parse(obj: &ObjValue) -> Result<Self, LocError> {554 Ok(Self {555 #(#fields_parse)*556 })557 }558 }559560 impl TryFrom<Val> for #ident {561 type Error = LocError;562 fn try_from(value: Val) -> Result<Self, Self::Error> {563 let obj = value.as_obj().expect("shape is correct");564 Self::parse(&obj)565 }566 }567 impl TryInto<Val> for #ident {568 type Error = LocError;569 fn try_into(self) -> Result<Val, Self::Error> {570 let mut out = ObjValueBuilder::new();571 self.serialize(&mut out)?;572 Ok(Val::Obj(out.build()))573 }574 }575 ()576 };577 })578}1use proc_macro2::TokenStream;2use quote::quote;3use syn::{4 parenthesized,5 parse::{Parse, ParseStream},6 parse_macro_input,7 punctuated::Punctuated,8 spanned::Spanned,9 token::Comma,10 Attribute, DeriveInput, Error, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, Path,11 PathArguments, Result, ReturnType, Token, Type,12};1314fn parse_attr<A: Parse, I>(attrs: &[Attribute], ident: I) -> Result<Option<A>>15where16 Ident: PartialEq<I>,17{18 let attrs = attrs19 .iter()20 .filter(|a| a.path.is_ident(&ident))21 .collect::<Vec<_>>();22 if attrs.len() > 1 {23 return Err(Error::new(24 attrs[1].span(),25 "this attribute may be specified only once",26 ));27 } else if attrs.is_empty() {28 return Ok(None);29 }30 let attr = attrs[0];31 let attr = attr.parse_args::<A>()?;3233 Ok(Some(attr))34}3536fn path_is(path: &Path, needed: &str) -> bool {37 path.leading_colon.is_none()38 && !path.segments.is_empty()39 && path.segments.iter().last().unwrap().ident == needed40}4142fn type_is_path<'ty>(ty: &'ty Type, needed: &str) -> Option<&'ty PathArguments> {43 match ty {44 Type::Path(path) if path.qself.is_none() && path_is(&path.path, needed) => {45 let args = &path.path.segments.iter().last().unwrap().arguments;46 Some(args)47 }48 _ => None,49 }50}5152fn extract_type_from_option(ty: &Type) -> Result<Option<&Type>> {53 Ok(if let Some(args) = type_is_path(ty, "Option") {54 // It should have only on angle-bracketed param ("<String>"):55 let generic_arg = match args {56 PathArguments::AngleBracketed(params) => params.args.iter().next().unwrap(),57 _ => return Err(Error::new(args.span(), "missing option generic")),58 };59 // This argument must be a type:60 match generic_arg {61 GenericArgument::Type(ty) => Some(ty),62 _ => {63 return Err(Error::new(64 generic_arg.span(),65 "option generic should be a type",66 ))67 }68 }69 } else {70 None71 })72}7374struct Field {75 name: Ident,76 _colon: Token![:],77 ty: Type,78}79impl Parse for Field {80 fn parse(input: ParseStream) -> syn::Result<Self> {81 Ok(Self {82 name: input.parse()?,83 _colon: input.parse()?,84 ty: input.parse()?,85 })86 }87}8889mod kw {90 syn::custom_keyword!(fields);91 syn::custom_keyword!(rename);92 syn::custom_keyword!(flatten);93}9495struct EmptyAttr;96impl Parse for EmptyAttr {97 fn parse(_input: ParseStream) -> Result<Self> {98 Ok(Self)99 }100}101102struct BuiltinAttrs {103 fields: Vec<Field>,104}105impl Parse for BuiltinAttrs {106 fn parse(input: ParseStream) -> syn::Result<Self> {107 if input.is_empty() {108 return Ok(Self { fields: Vec::new() });109 }110 input.parse::<kw::fields>()?;111 let fields;112 parenthesized!(fields in input);113 let p = Punctuated::<Field, Comma>::parse_terminated(&fields)?;114 Ok(Self {115 fields: p.into_iter().collect(),116 })117 }118}119120enum ArgInfo {121 Normal {122 ty: Box<Type>,123 is_option: bool,124 name: String,125 // ident: Ident,126 },127 Lazy {128 is_option: bool,129 name: String,130 },131 Location,132 This,133}134135impl ArgInfo {136 fn parse(arg: &FnArg) -> Result<Self> {137 let typed = match arg {138 FnArg::Receiver(_) => unreachable!(),139 FnArg::Typed(a) => a,140 };141 let ident = match &typed.pat as &Pat {142 Pat::Ident(i) => i.ident.clone(),143 _ => {144 return Err(Error::new(145 typed.pat.span(),146 "arg should be plain identifier",147 ))148 }149 };150 let ty = &typed.ty;151 if type_is_path(ty, "CallLocation").is_some() {152 return Ok(Self::Location);153 } else if type_is_path(ty, "Self").is_some() {154 return Ok(Self::This);155 } else if type_is_path(ty, "LazyVal").is_some() {156 return Ok(Self::Lazy {157 is_option: false,158 name: ident.to_string(),159 });160 }161162 let (is_option, ty) = if let Some(ty) = extract_type_from_option(ty)? {163 if type_is_path(ty, "LazyVal").is_some() {164 return Ok(Self::Lazy {165 is_option: true,166 name: ident.to_string(),167 });168 }169170 (true, Box::new(ty.clone()))171 } else {172 (false, ty.clone())173 };174175 Ok(Self::Normal {176 ty,177 is_option,178 name: ident.to_string(),179 // ident,180 })181 }182}183184#[proc_macro_attribute]185pub fn builtin(186 attr: proc_macro::TokenStream,187 item: proc_macro::TokenStream,188) -> proc_macro::TokenStream {189 let attr = parse_macro_input!(attr as BuiltinAttrs);190 let item: ItemFn = parse_macro_input!(item);191192 match builtin_inner(attr, item) {193 Ok(v) => v.into(),194 Err(e) => e.into_compile_error().into(),195 }196}197198fn builtin_inner(attr: BuiltinAttrs, fun: ItemFn) -> syn::Result<TokenStream> {199 let result = match fun.sig.output {200 ReturnType::Default => {201 return Err(Error::new(202 fun.sig.span(),203 "builtin should return something",204 ))205 }206 ReturnType::Type(_, ref ty) => ty.clone(),207 };208209 let args = fun210 .sig211 .inputs212 .iter()213 .map(ArgInfo::parse)214 .collect::<Result<Vec<_>>>()?;215216 let params_desc = args.iter().flat_map(|a| match a {217 ArgInfo::Normal {218 is_option, name, ..219 }220 | ArgInfo::Lazy { is_option, name } => Some(quote! {221 BuiltinParam {222 name: std::borrow::Cow::Borrowed(#name),223 has_default: #is_option,224 }225 }),226 ArgInfo::Location => None,227 ArgInfo::This => None,228 });229230 let pass = args.iter().map(|a| match a {231 ArgInfo::Normal {232 ty,233 is_option,234 name,235 // ident,236 } => {237 let eval = quote! {::jrsonnet_evaluator::push_description_frame(238 || format!("argument <{}> evaluation", #name),239 || <#ty>::try_from(value.evaluate()?),240 )?};241 if *is_option {242 quote! {if let Some(value) = parsed.get(#name) {243 Some(#eval)244 } else {245 None246 }}247 } else {248 quote! {{249 let value = parsed.get(#name).expect("args shape is checked");250 #eval251 }}252 }253 }254 ArgInfo::Lazy { is_option, name } => {255 if *is_option {256 quote! {if let Some(value) = parsed.get(#name) {257 Some(value.clone())258 } else {259 None260 }}261 } else {262 quote! {263 parsed.get(#name).expect("args shape is correct").clone()264 }265 }266 }267 ArgInfo::Location => quote! {location},268 ArgInfo::This => quote! {self},269 });270271 let fields = attr.fields.iter().map(|field| {272 let name = &field.name;273 let ty = &field.ty;274 quote! {275 pub #name: #ty,276 }277 });278279 let name = &fun.sig.ident;280 let vis = &fun.vis;281 let static_ext = if attr.fields.is_empty() {282 quote! {283 impl #name {284 pub const INST: &'static dyn StaticBuiltin = &#name {};285 }286 impl StaticBuiltin for #name {}287 }288 } else {289 quote! {}290 };291 let static_derive_copy = if attr.fields.is_empty() {292 quote! {, Copy}293 } else {294 quote! {}295 };296297 Ok(quote! {298 #fun299 #[doc(hidden)]300 #[allow(non_camel_case_types)]301 #[derive(Clone, gcmodule::Trace #static_derive_copy)]302 #vis struct #name {303 #(#fields)*304 }305 const _: () = {306 use ::jrsonnet_evaluator::{307 function::{Builtin, CallLocation, StaticBuiltin, BuiltinParam, ArgsLike, parse_builtin_call},308 error::Result, Context,309 parser::ExprLocation,310 };311 const PARAMS: &'static [BuiltinParam] = &[312 #(#params_desc),*313 ];314315 #static_ext316 impl Builtin for #name317 where318 Self: 'static319 {320 fn name(&self) -> &str {321 stringify!(#name)322 }323 fn params(&self) -> &[BuiltinParam] {324 PARAMS325 }326 fn call(&self, context: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {327 let parsed = parse_builtin_call(context, &PARAMS, args, false)?;328329 let result: #result = #name(#(#pass),*);330 let result = result?;331 result.try_into()332 }333 }334 };335 })336}337338#[derive(Default)]339struct TypedAttr {340 rename: Option<String>,341 flatten: bool,342}343impl Parse for TypedAttr {344 fn parse(input: ParseStream) -> syn::Result<Self> {345 let mut out = Self::default();346 loop {347 let lookahead = input.lookahead1();348 if lookahead.peek(kw::rename) {349 input.parse::<kw::rename>()?;350 input.parse::<Token![=]>()?;351 let name = input.parse::<LitStr>()?;352 if out.rename.is_some() {353 return Err(Error::new(354 name.span(),355 "rename attribute may only be specified once",356 ));357 }358 out.rename = Some(name.value());359 } else if lookahead.peek(kw::flatten) {360 input.parse::<kw::flatten>()?;361 out.flatten = true;362 } else if input.is_empty() {363 break;364 } else {365 return Err(lookahead.error());366 }367 if input.peek(Token![,]) {368 input.parse::<Token![,]>()?;369 } else {370 break;371 }372 }373 // input.parse::<kw::rename>()?;374 // input.parse::<Token![=]>()?;375 // let rename = input.parse::<LitStr>()?.value();376 Ok(out)377 }378}379380struct TypedField<'f>(&'f syn::Field, TypedAttr);381impl<'f> TypedField<'f> {382 fn try_new(field: &'f syn::Field) -> Result<Self> {383 let attr = parse_attr::<TypedAttr, _>(&field.attrs, "typed")?.unwrap_or_default();384 if field.ident.is_none() {385 return Err(Error::new(386 field.span(),387 "this field should appear in output object, but it has no visible name",388 ));389 }390 Ok(Self(field, attr))391 }392 fn ident(&self) -> Ident {393 self.0394 .ident395 .clone()396 .expect("constructor disallows fields without name")397 }398 /// None if this field is flattened in jsonnet output399 fn name(&self) -> Option<String> {400 if self.1.flatten {401 return None;402 }403 Some(404 self.1405 .rename406 .clone()407 .unwrap_or_else(|| self.ident().to_string()),408 )409 }410411 fn expand_field(&self) -> Option<TokenStream> {412 if self.is_option() {413 return None;414 }415 let name = self.name()?;416 let ty = &self.0.ty;417 Some(quote! {418 (#name, <#ty>::TYPE)419 })420 }421 fn expand_parse(&self) -> TokenStream {422 let ident = self.ident();423 let ty = &self.0.ty;424 if self.1.flatten {425 // optional flatten is handled in same way as serde426 return if self.is_option() {427 quote! {428 #ident: <#ty>::parse(&obj).ok(),429 }430 } else {431 quote! {432 #ident: <#ty>::parse(&obj)?,433 }434 };435 };436437 let name = self.name().unwrap();438 let value = if let Some(ty) = self.as_option() {439 quote! {440 if let Some(value) = obj.get(#name.into())? {441 Some(<#ty>::try_from(vakue)?)442 } else {443 None444 }445 }446 } else {447 quote! {448 <#ty>::try_from(obj.get(#name.into())?.ok_or_else(|| Error::NoSuchField(#name.into()))?)?449 }450 };451452 quote! {453 #ident: #value,454 }455 }456 fn expand_serialize(&self) -> TokenStream {457 let ident = self.ident();458 if let Some(name) = self.name() {459 if self.is_option() {460 quote! {461 if let Some(value) = self.#ident {462 out.member(#name.into()).value(value.try_into()?);463 }464 }465 } else {466 quote! {467 out.member(#name.into()).value(self.#ident.try_into()?);468 }469 }470 } else if self.is_option() {471 quote! {472 if let Some(value) = self.#ident {473 value.serialize(out)?;474 }475 }476 } else {477 quote! {478 self.#ident.serialize(out)?;479 }480 }481 }482483 fn as_option(&self) -> Option<&Type> {484 extract_type_from_option(&self.0.ty).unwrap()485 }486 fn is_option(&self) -> bool {487 self.as_option().is_some()488 }489}490491#[proc_macro_derive(Typed, attributes(typed))]492pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {493 let input = parse_macro_input!(item as DeriveInput);494495 match derive_typed_inner(input) {496 Ok(v) => v.into(),497 Err(e) => e.to_compile_error().into(),498 }499}500501fn derive_typed_inner(input: DeriveInput) -> Result<TokenStream> {502 let data = match &input.data {503 syn::Data::Struct(s) => s,504 _ => return Err(Error::new(input.span(), "only structs supported")),505 };506507 let ident = &input.ident;508 let fields = data509 .fields510 .iter()511 .map(TypedField::try_new)512 .collect::<Result<Vec<_>>>()?;513514 let typed = {515 let fields = fields516 .iter()517 .flat_map(TypedField::expand_field)518 .collect::<Vec<_>>();519 let len = fields.len();520 quote! {521 const ITEMS: [(&'static str, &'static ComplexValType); #len] = [522 #(#fields,)*523 ];524 impl Typed for #ident {525 const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);526 }527 }528 };529530 let fields_parse = fields.iter().map(TypedField::expand_parse);531 let fields_serialize = fields.iter().map(TypedField::expand_serialize);532533 Ok(quote! {534 const _: () = {535 use ::jrsonnet_evaluator::{536 typed::{ComplexValType, Typed, TypedObj, CheckType},537 Val,538 error::{LocError, Error},539 ObjValueBuilder, ObjValue,540 };541542 #typed543544 impl #ident {545 fn serialize(self, out: &mut ObjValueBuilder) -> Result<(), LocError> {546 #(#fields_serialize)*547548 Ok(())549 }550 fn parse(obj: &ObjValue) -> Result<Self, LocError> {551 Ok(Self {552 #(#fields_parse)*553 })554 }555 }556557 impl TryFrom<Val> for #ident {558 type Error = LocError;559 fn try_from(value: Val) -> Result<Self, Self::Error> {560 let obj = value.as_obj().expect("shape is correct");561 Self::parse(&obj)562 }563 }564 impl TryInto<Val> for #ident {565 type Error = LocError;566 fn try_into(self) -> Result<Val, Self::Error> {567 let mut out = ObjValueBuilder::new();568 self.serialize(&mut out)?;569 Ok(Val::Obj(out.build()))570 }571 }572 ()573 };574 })575}