usecrate::utils::{self, FieldInfo}; use proc_macro2::Span; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use syn::spanned::Spanned; use syn::{
parse_quote, Data, DeriveInput, Error, Field, Fields, GenericArgument, Ident, Lifetime,
PathArguments, Type, TypePath,
};
pubfn make_varule_impl(ule_name: Ident, mut input: DeriveInput) -> TokenStream2 { if input.generics.type_params().next().is_some()
|| input.generics.const_params().next().is_some()
|| input.generics.lifetimes().count() > 1
{ return Error::new(
input.generics.span(), "#[make_varule] must be applied to a struct without any type or const parameters and at most one lifetime",
)
.to_compile_error();
}
let sp = input.span(); let attrs = match utils::extract_attributes_common(&mut input.attrs, sp, true) {
Ok(val) => val,
Err(e) => return e.to_compile_error(),
};
let lt = input.generics.lifetimes().next();
iflet Some(lt) = lt { if lt.colon_token.is_some() || !lt.bounds.is_empty() { return Error::new(
input.generics.span(), "#[make_varule] must be applied to a struct without lifetime bounds",
)
.to_compile_error();
}
}
let lt = lt.map(|l| &l.lifetime);
let name = &input.ident; let input_span = input.span();
let fields = match input.data {
Data::Struct(refmut s) => &mut s.fields,
_ => { return Error::new(input.span(), "#[make_varule] must be applied to a struct")
.to_compile_error();
}
};
if fields.is_empty() { return Error::new(
input.span(), "#[make_varule] must be applied to a struct with at least one field",
)
.to_compile_error();
}
for field in fields.iter_mut() { match utils::extract_field_attributes(&mut field.attrs) {
Ok(i) => custom_varule_idents.push(i),
Err(e) => return e.to_compile_error(),
}
}
for (i, field) in fields.iter().enumerate() { match UnsizedField::new(field, i, custom_varule_idents[i].clone()) {
Ok(o) => unsized_fields.push(o),
Err(_) => sized_fields.push(FieldInfo::new_for_field(field, i)),
}
}
if unsized_fields.is_empty() { let last_field_index = fields.len() - 1; let last_field = fields.iter().next_back().unwrap();
let e = UnsizedField::new(
last_field,
last_field_index,
custom_varule_idents[last_field_index].clone(),
)
.unwrap_err(); return Error::new(last_field.span(), e).to_compile_error();
}
if unsized_fields[0].field.index != fields.len() - unsized_fields.len()
&& unsized_fields[0].field.field.ident.is_none()
{ return Error::new(
unsized_fields.first().unwrap().field.field.span(), "#[make_varule] requires its unsized fields to be at the end for tuple structs",
)
.to_compile_error();
}
let unsized_field_info = UnsizedFields::new(unsized_fields);
letmut field_inits = crate::ule::make_ule_fields(&sized_fields); let last_field_ule = unsized_field_info.varule_ty();
let setter = unsized_field_info.varule_setter(); let vis = &unsized_field_info.varule_vis();
field_inits.push(quote!(#vis#setter#last_field_ule));
let semi = utils::semi_for(fields); let repr_attr = utils::repr_for(fields); let field_inits = utils::wrap_field_inits(&field_inits, fields); let vis = &input.vis;
let doc = format!( "[`VarULE`](zerovec::ule::VarULE) type for [`{name}`]. See [`{name}`] for documentation."
); let varule_struct: DeriveInput = parse_quote!( #[repr(#repr_attr)] #[doc = #doc] #[allow(missing_docs)] #visstruct#ule_name#field_inits#semi
);
let derived = crate::varule::derive_impl(&varule_struct, unsized_field_info.varule_validator());
let maybe_lt_bound = lt.as_ref().map(|lt| quote!(<#lt>));
let encode_impl = make_encode_impl(
&sized_fields,
&unsized_field_info,
name,
&ule_name,
&maybe_lt_bound,
);
for field in sized_fields { let ty = &field.field.ty;
lengths.push(quote!(::core::mem::size_of::<<#tyas zerovec::ule::AsULE>::ULE>()));
}
let (encoders, remaining_offset) = utils::generate_per_field_offsets(
sized_fields, true,
|field, prev_offset_ident, size_ident| { let ty = &field.field.ty; let accessor = &field.accessor;
quote!( #[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid indices let out = &mut dst[#prev_offset_ident .. #prev_offset_ident + #size_ident]; let unaligned = zerovec::ule::AsULE::to_unaligned(self.#accessor); let unaligned_slice = &[unaligned]; let src = <<#tyas zerovec::ule::AsULE>::ULE as zerovec::ule::ULE>::as_byte_slice(unaligned_slice);
out.copy_from_slice(src);
)
},
);
let last_encode_len = unsized_field_info.encode_len(); let last_encode_write = unsized_field_info.encode_write(quote!(out));
quote!( unsafeimpl#maybe_lt_bound zerovec::ule::EncodeAsVarULE<#ule_name> for#name#maybe_lt_bound { // Safety: unimplemented as the other two are implemented fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R {
unreachable!("other two methods implemented")
}
// Safety: returns the total length of the ULE form by adding up the lengths of each element's ULE forms fn encode_var_ule_len(&self) -> usize { #(#lengths +)* #last_encode_len
}
// Safety: converts each element to ULE form and writes them in sequence fn encode_var_ule_write(&self, mut dst: &>mut [u8]) {
debug_assert_eq!(self.encode_var_ule_len(), dst.len()); #encoders
#[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid remainder let out = &mut dst[#remaining_offset..]; #last_encode_write
}
}
// This second impl exists to allow for using EncodeAsVarULE without cloning // // A blanket impl cannot exist without coherence issues unsafeimpl#maybe_lt_bound zerovec::ule::EncodeAsVarULE<#ule_name> for &'_ #name #maybe_lt_bound { // Safety: unimplemented as the other two are implemented fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R {
unreachable!("other two methods implemented")
}
// Safety: returns the total length of the ULE form by adding up the lengths of each element's ULE forms fn encode_var_ule_len(&self) -> usize {
(**self).encode_var_ule_len()
}
// Safety: converts each element to ULE form and writes them in sequence fn encode_var_ule_write(&self, mut dst: &>mut [u8]) {
(**self).encode_var_ule_write(dst)
}
}
)
}
/// Represents a VarULE-compatible type that would typically /// be found behind a `Cow<'a, _>` in the last field, and is represented /// roughly the same in owned and borrowed versions #[derive(Copy, Clone, Debug)] enum OwnULETy<'a> { /// [T] where T: AsULE<ULE = Self>
Slice(&'a Type), /// str
Str,
}
/// Represents the type of the last field of the struct #[derive(Clone, Debug)] enum UnsizedFieldKind<'a> {
Cow(OwnULETy<'a>),
ZeroVec(&'a Type),
VarZeroVec(&'a Type), /// Custom VarULE type, and the identifier corresponding to the VarULE type
Custom(&'a TypePath, Ident),
// Generally you should be using the above ones for maximum zero-copy, but these will still work
Growable(OwnULETy<'a>),
Boxed(OwnULETy<'a>), Ref(OwnULETy<'a>),
}
impl<'a> UnsizedFields<'a> { fn new(fields: Vec<UnsizedField<'a>>) -> Self {
assert!(!fields.is_empty(), "Must have at least one unsized field"); Self { fields }
}
// Get the corresponding VarULE type that can store all of these fn varule_ty(&self) -> TokenStream2 { ifself.fields.len() == 1 { self.fields[0].kind.varule_ty()
} else {
quote!(zerovec::ule::MultiFieldsULE)
}
}
// Get the accessor field name in the VarULE type fn varule_accessor(&self) -> TokenStream2 { ifself.fields.len() == 1 { self.fields[0].field.accessor.clone()
} elseifself.fields[0].field.field.ident.is_some() {
quote!(unsized_fields)
} else { // first unsized field self.fields[0].field.accessor.clone()
}
}
// Get the setter for this type for use in struct definition/creation syntax fn varule_setter(&self) -> TokenStream2 { ifself.fields.len() == 1 { self.fields[0].field.setter()
} elseifself.fields[0].field.field.ident.is_some() {
quote!(unsized_fields: )
} else {
quote!()
}
}
// Check if the type has a ZeroFrom impl fn has_zf(&self) -> bool { self.fields.iter().all(|f| f.kind.has_zf())
}
// Takes all unsized fields on self and encodes them into a byte slice `out` fn encode_write(&self, out: TokenStream2) -> TokenStream2 { ifself.fields.len() == 1 { self.fields[0].encode_func(quote!(encode_var_ule_write), quote!(#out))
} else { letmut lengths = vec![]; letmut writers = vec![]; for (i, field) inself.fields.iter().enumerate() {
lengths.push(field.encode_func(quote!(encode_var_ule_len), quote!())); let (encodeable_ty, encodeable) = field.encodeable_tokens(); let varule_ty = field.kind.varule_ty();
writers
.push(quote!(multi.set_field_at::<#varule_ty, #encodeable_ty>(#i, #encodeable)))
}
quote!( let lengths = [#(#lengths),*]; letmut multi = zerovec::ule::MultiFieldsULE::new_from_lengths_partially_initialized(&lengths, #out); unsafe { #(#writers;)*
}
)
}
}
// Takes all unsized fields on self and returns the length needed for encoding into a byte slice fn encode_len(&self) -> TokenStream2 { ifself.fields.len() == 1 { self.fields[0].encode_func(quote!(encode_var_ule_len), quote!())
} else { letmut lengths = vec![]; for field inself.fields.iter() {
lengths.push(field.encode_func(quote!(encode_var_ule_len), quote!()));
}
quote!(zerovec::ule::MultiFieldsULE::compute_encoded_len_for(&[#(#lengths),*]))
}
}
/// Constructs ZeroFrom setters for each field of the stack type fn push_zf_setters(&self, lt: &Lifetime, field_inits: &mut Vec<TokenStream2>) { let zerofrom_trait = quote!(zerovec::__zerovec_internal_reexport::ZeroFrom); ifself.fields.len() == 1 { let accessor = self.fields[0].field.accessor.clone(); let setter = self.fields[0].field.setter(); let last_field_ty = &self.fields[0].field.field.ty; let last_field_ule_ty = self.fields[0].kind.varule_ty();
field_inits.push(quote!(#setter <#last_field_tyas#zerofrom_trait <#lt, #last_field_ule_ty>>::zero_from(&other.#accessor) ));
} else { for field inself.fields.iter() { let setter = field.field.setter(); let getter = field.field.getter(); let field_ty = &field.field.field.ty; let field_ule_ty = field.kind.varule_ty();
fn maybe_multi_getters(&self) -> Option<TokenStream2> { ifself.fields.len() == 1 {
None
} else { let multi_accessor = self.varule_accessor(); let field_getters = self.fields.iter().enumerate().map(|(i, field)| { let getter = field.field.getter();
let field_ule_ty = field.kind.varule_ty(); let doc_name = field.field.getter_doc_name(); let doc = format!("Access the VarULE type behind {doc_name}");
quote!( #[doc = #doc] pubfn#getter<'a>(&'a self) -> &'a #field_ule_ty { unsafe { self.#multi_accessor.get_field::<#field_ule_ty>(#i)
}
}
)
});
Some(quote!(#(#field_getters)*))
}
}
/// In case this needs custom validation code, return it /// /// The code will validate a variable known as `last_field_bytes` fn varule_validator(&self) -> Option<TokenStream2> { ifself.fields.len() == 1 {
None
} else { letmut validators = vec![]; for (i, field) inself.fields.iter().enumerate() { let varule_ty = field.kind.varule_ty();
validators.push(quote!(multi.validate_field::<#varule_ty>(#i)?;));
}
Some(quote!( let multi = zerovec::ule::MultiFieldsULE::parse_byte_slice(last_field_bytes)?; unsafe { #(#validators)*
}
))
}
}
}
/// Call `<Self as EncodeAsVarULE<V>>::#method(self.accessor #additional_args)` after adjusting /// Self and self.accessor to be the right types fn encode_func(&self, method: TokenStream2, additional_args: TokenStream2) -> TokenStream2 { let encodeas_trait = quote!(zerovec::ule::EncodeAsVarULE); let (encodeable_ty, encodeable) = self.encodeable_tokens(); let varule_ty = self.kind.varule_ty();
quote!(<#encodeable_tyas#encodeas_trait<#varule_ty>>::#method(#encodeable, #additional_args))
}
/// Returns (encodeable_ty, encodeable) fn encodeable_tokens(&self) -> (TokenStream2, TokenStream2) { let accessor = self.field.accessor.clone(); let value = quote!(self.#accessor); let encodeable = self.kind.encodeable_value(value); let encodeable_ty = self.kind.encodeable_ty();
(encodeable_ty, encodeable)
}
}
impl<'a> UnsizedFieldKind<'a> { /// Construct a UnsizedFieldKind for the type of a UnsizedFieldKind if possible fn new(
ty: &'a Type,
custom_varule_ident: Option<Ident>,
) -> Result<UnsizedFieldKind<'a>, String> { static PATH_TYPE_IDENTITY_ERROR: &str = "Can only automatically detect corresponding VarULE types for path types \
that are Cow, ZeroVec, VarZeroVec, Box, String, or Vec"; static PATH_TYPE_GENERICS_ERROR: &str = "Can only automatically detect corresponding VarULE types for path \
types with at most one lifetime and at most one generic parameter. VarZeroVecFormat
types are not currently supported"; match *ty { Type::Reference(ref tyref) => OwnULETy::new(&tyref.elem, "reference").map(UnsizedFieldKind::Ref), Type::Path(ref typath) => { iflet Some(custom_varule_ident) = custom_varule_ident { return Ok(UnsizedFieldKind::Custom(typath, custom_varule_ident));
} if typath.path.segments.len() != 1 { return Err("Can only automatically detect corresponding VarULE types for \
path types with a single path segment".into());
} let segment = typath.path.segments.first().unwrap(); match segment.arguments {
PathArguments::None => { if segment.ident == "String" {
Ok(UnsizedFieldKind::Growable(OwnULETy::Str))
} else {
Err(PATH_TYPE_IDENTITY_ERROR.into())
}
}
PathArguments::AngleBracketed(ref params) => { // At most one lifetime and exactly one generic parameter letmut lifetime = None; letmut generic = None; for param in ¶ms.args { match param {
GenericArgument::Lifetime(ref lt) if lifetime.is_none() => {
lifetime = Some(lt)
}
GenericArgument::Type(ref ty) if generic.is_none() => {
generic = Some(ty)
}
_ => return Err(PATH_TYPE_GENERICS_ERROR.into()),
}
}
// Must be exactly one generic parameter // (we've handled the zero generics case already) let generic = iflet Some(g) = generic {
g
} else { return Err(PATH_TYPE_GENERICS_ERROR.into());
};
let ident = segment.ident.to_string();
if lifetime.is_some() { match &*ident { "ZeroVec" => Ok(UnsizedFieldKind::ZeroVec(generic)), "VarZeroVec" => Ok(UnsizedFieldKind::VarZeroVec(generic)), "Cow" => OwnULETy::new(generic, "Cow").map(UnsizedFieldKind::Cow),
_ => Err(PATH_TYPE_IDENTITY_ERROR.into()),
}
} else { match &*ident { "Vec" => Ok(UnsizedFieldKind::Growable(OwnULETy::Slice(generic))), "Box" => OwnULETy::new(generic, "Box").map(UnsizedFieldKind::Boxed),
_ => Err(PATH_TYPE_IDENTITY_ERROR.into()),
}
}
}
_ => Err("Can only automatically detect corresponding VarULE types for path types \
with none or angle bracketed generics".into()),
}
}
_ => Err("Can only automatically detect corresponding VarULE types for path and reference types".into()),
}
} /// Get the tokens for the corresponding VarULE type fn varule_ty(&self) -> TokenStream2 { match *self { Self::Ref(ref inner)
| Self::Cow(ref inner)
| Self::Boxed(ref inner)
| Self::Growable(ref inner) => { let inner_ule = inner.varule_ty();
quote!(#inner_ule)
} Self::Custom(_, ref name) => quote!(#name), Self::ZeroVec(ref inner) => quote!(zerovec::ZeroSlice<#inner>), Self::VarZeroVec(ref inner) => quote!(zerovec::VarZeroSlice<#inner>),
}
}
// Takes expr `value` and returns it as a value that can be encoded via EncodeAsVarULE fn encodeable_value(&self, value: TokenStream2) -> TokenStream2 { match *self { Self::Ref(_) | Self::Cow(_) | Self::Growable(_) | Self::Boxed(_) => quote!(&*#value),
/// Returns the EncodeAsVarULE type this can be represented as, the same returned by encodeable_value() fn encodeable_ty(&self) -> TokenStream2 { match *self { Self::Ref(ref inner)
| Self::Cow(ref inner)
| Self::Growable(ref inner)
| Self::Boxed(ref inner) => inner.varule_ty(),
impl<'a> OwnULETy<'a> { fn new(ty: &'a Type, context: &str) -> Result<Self, String> { match *ty { Type::Slice(ref slice) => Ok(OwnULETy::Slice(&slice.elem)), Type::Path(ref typath) => { if typath.path.is_ident("str") {
Ok(OwnULETy::Str)
} else {
Err(format!("Cannot automatically detect corresponding VarULE type for non-str path type inside a {context}"))
}
}
_ => Err(format!("Cannot automatically detect corresponding VarULE type for non-slice/path type inside a {context}")),
}
}
/// Get the tokens for the corresponding VarULE type fn varule_ty(&self) -> TokenStream2 { match *self {
OwnULETy::Slice(s) => quote!([#s]),
OwnULETy::Str => quote!(str),
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-06-20)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.