Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+498
View File
@@ -0,0 +1,498 @@
//! Proc macro for `#[derive(SettingsValue)]`.
//!
//! Generates `SettingsValue` implementations:
//! - **Enums**: variant names are converted to snake_case. Data-carrying
//! variants recursively call `to_file_value` on their inner data.
//! - **Structs**: each field is serialized/deserialized by recursively calling
//! the trait methods. Field names use the Rust identifier (already snake_case)
//! unless overridden by `#[serde(rename = "...")]`.
//! Newtype structs (`struct Foo(T)`) delegate to the inner type.
extern crate proc_macro;
use convert_case::{Case, Casing};
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, Lit, Meta, MetaNameValue, parse_macro_input};
/// Derive macro that generates a `SettingsValue` implementation.
///
/// # Enums
///
/// Unit variants are serialized as snake_case JSON strings. Data-carrying
/// variants (tuple or struct) are serialized as a single-key JSON object
/// `{ "snake_case_variant": <recursive value> }`.
///
/// # Structs
///
/// Named-field structs are serialized into a JSON object where each field value
/// is produced by calling `to_file_value()` recursively. Field names default to
/// the Rust field name but respect `#[serde(rename = "...")]`.
///
/// Newtype structs (`struct Foo(T)`) delegate to the inner type's
/// `SettingsValue` impl.
///
/// Fields marked `#[serde(skip)]` are excluded from serialization and
/// populated via `Default` during deserialization. Fields with
/// `#[serde(default)]` fall back to `Default` when absent in the JSON.
#[proc_macro_derive(SettingsValue, attributes(serde))]
pub fn derive_settings_value(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
// Read container-level #[serde(rename_all = "...")] if present.
let container_rename_all = get_serde_rename_all(&input.attrs);
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let expanded = match &input.data {
Data::Enum(data_enum) => {
let to_arms = data_enum.variants.iter().map(|variant| {
let variant_ident = &variant.ident;
let cfg_attrs = get_cfg_attrs(&variant.attrs);
let file_name = file_variant_name(variant_ident, &variant.attrs, container_rename_all.as_deref());
match &variant.fields {
Fields::Unit => {
quote! {
#(#cfg_attrs)*
#name::#variant_ident => {
serde_json::Value::String(#file_name.to_string())
}
}
}
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
quote! {
#(#cfg_attrs)*
#name::#variant_ident(inner) => {
let mut obj = serde_json::Map::new();
obj.insert(
#file_name.to_string(),
::settings_value::SettingsValue::to_file_value(inner),
);
serde_json::Value::Object(obj)
}
}
}
Fields::Unnamed(fields) => {
let field_bindings: Vec<_> = (0..fields.unnamed.len())
.map(|i| quote::format_ident!("f{i}"))
.collect();
quote! {
#(#cfg_attrs)*
#name::#variant_ident(#(#field_bindings),*) => {
let arr = serde_json::Value::Array(vec![
#(::settings_value::SettingsValue::to_file_value(#field_bindings)),*
]);
let mut obj = serde_json::Map::new();
obj.insert(#file_name.to_string(), arr);
serde_json::Value::Object(obj)
}
}
}
Fields::Named(fields) => {
let field_idents: Vec<_> = fields.named.iter()
.map(|f| f.ident.as_ref().unwrap())
.collect();
let field_keys: Vec<_> = fields.named.iter()
.map(|f| {
let ident = f.ident.as_ref().unwrap();
get_serde_rename(&f.attrs).unwrap_or_else(|| ident.to_string())
})
.collect();
quote! {
#(#cfg_attrs)*
#name::#variant_ident { #(#field_idents),* } => {
let mut inner_obj = serde_json::Map::new();
#(
inner_obj.insert(
#field_keys.to_string(),
::settings_value::SettingsValue::to_file_value(#field_idents),
);
)*
let mut obj = serde_json::Map::new();
obj.insert(#file_name.to_string(), serde_json::Value::Object(inner_obj));
serde_json::Value::Object(obj)
}
}
}
}
});
let from_arms = data_enum.variants.iter().map(|variant| {
let variant_ident = &variant.ident;
let cfg_attrs = get_cfg_attrs(&variant.attrs);
let file_name = file_variant_name(variant_ident, &variant.attrs, container_rename_all.as_deref());
match &variant.fields {
Fields::Unit => {
quote! {
#(#cfg_attrs)*
serde_json::Value::String(s) if s == #file_name => {
Some(#name::#variant_ident)
}
}
}
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
let ty = &fields.unnamed.first().unwrap().ty;
quote! {
#(#cfg_attrs)*
serde_json::Value::Object(obj) if obj.contains_key(#file_name) => {
let inner_val = obj.get(#file_name)?;
let inner = <#ty as ::settings_value::SettingsValue>::from_file_value(inner_val)?;
Some(#name::#variant_ident(inner))
}
}
}
Fields::Unnamed(fields) => {
let field_types: Vec<_> = fields.unnamed.iter()
.map(|f| &f.ty)
.collect();
let field_indices: Vec<_> = (0..fields.unnamed.len())
.map(syn::Index::from)
.collect();
quote! {
#(#cfg_attrs)*
serde_json::Value::Object(obj) if obj.contains_key(#file_name) => {
let arr = obj.get(#file_name)?.as_array()?;
Some(#name::#variant_ident(
#(
<#field_types as ::settings_value::SettingsValue>::from_file_value(arr.get(#field_indices)?)?
),*
))
}
}
}
Fields::Named(fields) => {
let field_idents: Vec<_> = fields.named.iter()
.map(|f| f.ident.as_ref().unwrap())
.collect();
let field_keys: Vec<_> = fields.named.iter()
.map(|f| {
let ident = f.ident.as_ref().unwrap();
get_serde_rename(&f.attrs).unwrap_or_else(|| ident.to_string())
})
.collect();
let field_types: Vec<_> = fields.named.iter()
.map(|f| &f.ty)
.collect();
quote! {
#(#cfg_attrs)*
serde_json::Value::Object(obj) if obj.contains_key(#file_name) => {
let inner_obj = obj.get(#file_name)?.as_object()?;
Some(#name::#variant_ident {
#(
#field_idents: <#field_types as ::settings_value::SettingsValue>::from_file_value(inner_obj.get(#field_keys)?)?,
)*
})
}
}
}
}
});
quote! {
impl #impl_generics ::settings_value::SettingsValue for #name #ty_generics #where_clause {
fn to_file_value(&self) -> serde_json::Value {
match self {
#(#to_arms)*
}
}
fn from_file_value(value: &serde_json::Value) -> Option<Self> {
match value {
#(#from_arms)*
_ => None,
}
}
}
}
}
Data::Struct(data_struct) => match &data_struct.fields {
Fields::Named(fields) => derive_named_struct(
name,
fields,
&input.attrs,
&impl_generics,
&ty_generics,
where_clause,
),
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
// Newtype struct: delegate to inner type.
let inner_ty = &fields.unnamed.first().unwrap().ty;
quote! {
impl #impl_generics ::settings_value::SettingsValue for #name #ty_generics #where_clause {
fn to_file_value(&self) -> serde_json::Value {
::settings_value::SettingsValue::to_file_value(&self.0)
}
fn from_file_value(value: &serde_json::Value) -> Option<Self> {
Some(Self(<#inner_ty as ::settings_value::SettingsValue>::from_file_value(value)?))
}
}
}
}
_ => {
return syn::Error::new_spanned(
&input.ident,
"SettingsValue derive only supports structs with named fields or newtype structs",
)
.to_compile_error()
.into();
}
},
Data::Union(_) => {
return syn::Error::new_spanned(
&input.ident,
"SettingsValue cannot be derived for unions",
)
.to_compile_error()
.into();
}
};
expanded.into()
}
fn derive_named_struct(
name: &syn::Ident,
fields: &syn::FieldsNamed,
struct_attrs: &[syn::Attribute],
impl_generics: &syn::ImplGenerics,
ty_generics: &syn::TypeGenerics,
where_clause: Option<&syn::WhereClause>,
) -> proc_macro2::TokenStream {
// When the struct has `#[serde(default)]`, all fields fall back to
// `Self::default()` field values (matching serde behaviour). This does
// not require individual field types to implement Default.
let struct_has_default = has_serde_default(struct_attrs);
let non_skipped_fields: Vec<_> = fields
.named
.iter()
.filter(|f| !has_serde_skip(&f.attrs))
.collect();
let skipped_fields: Vec<_> = fields
.named
.iter()
.filter(|f| has_serde_skip(&f.attrs))
.collect();
let to_inserts = non_skipped_fields.iter().map(|f| {
let ident = f.ident.as_ref().unwrap();
let key = get_serde_rename(&f.attrs).unwrap_or_else(|| ident.to_string());
quote! {
obj.insert(
#key.to_string(),
::settings_value::SettingsValue::to_file_value(&self.#ident),
);
}
});
let from_fields = non_skipped_fields.iter().map(|f| {
let ident = f.ident.as_ref().unwrap();
let ty = &f.ty;
let key = get_serde_rename(&f.attrs).unwrap_or_else(|| ident.to_string());
let field_has_default = has_serde_default(&f.attrs);
if struct_has_default {
// Struct has #[serde(default)]: use Self::default() when the field
// is absent, but propagate failure (return None) when the field is
// present but cannot be parsed. This matches serde semantics where
// #[serde(default)] only applies to missing fields, not to fields
// with invalid values.
quote! {
#ident: match obj.get(#key) {
Some(v) => <#ty as ::settings_value::SettingsValue>::from_file_value(v)?,
None => __struct_default.#ident,
},
}
} else if field_has_default {
// Field has #[serde(default)]: same semantics — default when
// absent, fail when present but unparseable.
quote! {
#ident: match obj.get(#key) {
Some(v) => <#ty as ::settings_value::SettingsValue>::from_file_value(v)?,
None => Default::default(),
},
}
} else if is_option_type(ty) {
quote! {
#ident: match obj.get(#key) {
Some(v) => <#ty as ::settings_value::SettingsValue>::from_file_value(v)?,
None => None,
},
}
} else {
quote! {
#ident: <#ty as ::settings_value::SettingsValue>::from_file_value(obj.get(#key)?)?,
}
}
});
let skipped_field_defaults = skipped_fields.iter().map(|f| {
let ident = f.ident.as_ref().unwrap();
quote! { #ident: Default::default(), }
});
let struct_default_binding = struct_has_default.then(|| {
quote! { let __struct_default = <#name #ty_generics as Default>::default(); }
});
quote! {
impl #impl_generics ::settings_value::SettingsValue for #name #ty_generics #where_clause {
fn to_file_value(&self) -> serde_json::Value {
let mut obj = serde_json::Map::new();
#(#to_inserts)*
serde_json::Value::Object(obj)
}
fn from_file_value(value: &serde_json::Value) -> Option<Self> {
let obj = value.as_object()?;
#struct_default_binding
Some(Self {
#(#from_fields)*
#(#skipped_field_defaults)*
})
}
}
}
}
/// Computes the file-format name for an enum variant.
///
/// Priority: `#[serde(rename = "...")]` > container `rename_all` > snake_case
/// of the Rust variant name.
fn file_variant_name(
ident: &syn::Ident,
attrs: &[syn::Attribute],
container_rename_all: Option<&str>,
) -> String {
// Explicit per-variant rename takes priority.
if let Some(renamed) = get_serde_rename(attrs) {
return renamed.to_case(Case::Snake);
}
let base = ident.to_string();
// Apply container rename_all first, then convert to snake_case.
if let Some(rename_all) = container_rename_all {
// If the container already uses snake_case or lowercase, apply that
// directly. Otherwise use the variant name as-is (PascalCase) and
// convert to snake_case.
match rename_all {
"snake_case" => return base.to_case(Case::Snake),
"camelCase" => return base.to_case(Case::Camel).to_case(Case::Snake),
"SCREAMING_SNAKE_CASE" => return base.to_case(Case::UpperSnake).to_case(Case::Snake),
"lowercase" => return base.to_lowercase(),
_ => {}
}
}
// Default: PascalCase → snake_case
base.to_case(Case::Snake)
}
/// Collects all `#[cfg(...)]` attributes so they can be propagated onto
/// generated match arms.
fn get_cfg_attrs(attrs: &[syn::Attribute]) -> Vec<&syn::Attribute> {
attrs.iter().filter(|a| a.path().is_ident("cfg")).collect()
}
/// Reads `#[serde(rename = "...")]` from field/variant attributes.
fn get_serde_rename(attrs: &[syn::Attribute]) -> Option<String> {
for attr in attrs {
if !attr.path().is_ident("serde") {
continue;
}
if let Ok(nested) = attr
.parse_args_with(syn::punctuated::Punctuated::<Meta, syn::Token![,]>::parse_terminated)
{
for meta in &nested {
if let Meta::NameValue(MetaNameValue {
path,
value: syn::Expr::Lit(expr_lit),
..
}) = meta
&& path.is_ident("rename")
&& let Lit::Str(s) = &expr_lit.lit
{
return Some(s.value());
}
}
}
}
None
}
/// Reads `#[serde(rename_all = "...")]` from container attributes.
fn get_serde_rename_all(attrs: &[syn::Attribute]) -> Option<String> {
for attr in attrs {
if !attr.path().is_ident("serde") {
continue;
}
if let Ok(nested) = attr
.parse_args_with(syn::punctuated::Punctuated::<Meta, syn::Token![,]>::parse_terminated)
{
for meta in &nested {
if let Meta::NameValue(MetaNameValue {
path,
value: syn::Expr::Lit(expr_lit),
..
}) = meta
&& path.is_ident("rename_all")
&& let Lit::Str(s) = &expr_lit.lit
{
return Some(s.value());
}
}
}
}
None
}
/// Returns `true` if the field has `#[serde(skip)]`.
fn has_serde_skip(attrs: &[syn::Attribute]) -> bool {
has_serde_flag(attrs, "skip")
}
/// Returns `true` if the field has `#[serde(default)]`.
fn has_serde_default(attrs: &[syn::Attribute]) -> bool {
has_serde_flag(attrs, "default")
}
/// Returns `true` if the type looks like `Option<...>`.
///
/// This is a best-effort heuristic — it checks whether the last segment of
/// the type path is `Option`. It won't detect type aliases but covers the
/// vast majority of real-world usage.
fn is_option_type(ty: &syn::Type) -> bool {
if let syn::Type::Path(type_path) = ty
&& let Some(segment) = type_path.path.segments.last()
{
return segment.ident == "Option";
}
false
}
/// Returns `true` if any `#[serde(...)]` attribute contains the given flag.
fn has_serde_flag(attrs: &[syn::Attribute], flag: &str) -> bool {
for attr in attrs {
if !attr.path().is_ident("serde") {
continue;
}
if let Ok(nested) = attr
.parse_args_with(syn::punctuated::Punctuated::<Meta, syn::Token![,]>::parse_terminated)
{
for meta in &nested {
if let Meta::Path(path) = meta
&& path.is_ident(flag)
{
return true;
}
}
}
}
false
}