diff --git a/DESIGN.md b/DESIGN.md index d362e58..83c159a 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -225,6 +225,37 @@ let view = OwnedView::::decode(bytes)?; println!("name: {}", view.name); // Deref, zero-copy, 'static + Send ``` +**Generated code layout — parallel trees per kind:** + +Ancillary generated types live in **kind-namespaced parallel trees** at the package root, not interleaved with the owned messages. The kind segment is outermost; for kinds that modify other kinds (only `view` today), the modifier wraps the modified: + +```text +:::: # owned messages / enums +:::::: # ancillary kind +:::::::: # modifier wrapping kind (view of oneofs) +``` + +Current kinds are `view`, `oneofs`, and `ext`; only `view` is a modifier. Concretely, for proto package `my.pkg` containing message `Foo` with oneof `bar` and extension `baz`: + +| Item | Generated path | +|----------------------------|---------------------------------------------| +| Owned message struct | `my::pkg::Foo` | +| View message struct | `my::pkg::view::FooView<'a>` | +| Oneof enum (owned) | `my::pkg::oneofs::foo::Bar` | +| Oneof enum (view) | `my::pkg::view::oneofs::foo::Bar<'a>` | +| File-level extension const | `my::pkg::ext::BAZ` | + +Two things to note: + +- The **owner module** in the `oneofs::` tree (`foo` above) is snake-cased from the owner message name. The oneof enum keeps its PascalCase proto name (`Bar`) — no `Kind` suffix, no `View` suffix on view-of-oneof enums. The tree prefix (`oneofs::` vs `view::oneofs::`) disambiguates. +- **View message structs keep the `View` suffix** (`FooView<'a>`) even though they live in `view::`. This is a deliberate exception: users routinely import the owned type and the view type together (`use pkg::{Foo, FooView}`) and a bare `View` would shadow too commonly. + +This layout has three structural benefits: + +1. **Collision-free.** A oneof whose owner name matches an existing nested type / enum / extension can't fight over the package-root namespace, because each lives in a different kind tree. The codegen has no reserved-name escape hatch; it simply emits verbatim proto names. +2. **Predictable.** Every proto package emits exactly five sibling files per `.proto`: `.rs`, `.__view.rs`, `.__ext.rs`, `.__oneofs.rs`, `.__view_oneofs.rs` — empty-bodied when the kind has no content. `buffa-build` and the packaging plugin stitch these into `pub mod view { … pub mod oneofs { … } }`, `pub mod ext { … }`, and `pub mod oneofs { … }` wrappers per package. +3. **Feature-gatable.** View and extension support are codegen options; disabling them simply leaves those trees empty. The structural layout is stable regardless. + ### 3. MessageField\ — Ergonomic Optional Messages Prost uses `Option>` for optional message fields, which creates unwrapping ceremony everywhere: diff --git a/benchmarks/buffa/benches/protobuf.rs b/benchmarks/buffa/benches/protobuf.rs index 3394106..deab176 100644 --- a/benchmarks/buffa/benches/protobuf.rs +++ b/benchmarks/buffa/benches/protobuf.rs @@ -2,8 +2,12 @@ use buffa::{Message, MessageView}; use criterion::{criterion_group, criterion_main, Criterion, Throughput}; use serde::{de::DeserializeOwned, Serialize}; +use bench_buffa::bench::view::{ + AnalyticsEventView, ApiResponseView, LogRecordView, MediaFrameView, +}; use bench_buffa::bench::*; use bench_buffa::benchmarks::BenchmarkDataset; +use bench_buffa::proto3::view::GoogleMessage1View; fn load_dataset(data: &[u8]) -> BenchmarkDataset { BenchmarkDataset::decode_from_slice(data).expect("failed to decode dataset") @@ -180,7 +184,7 @@ fn bench_google_message1_view(c: &mut Criterion) { group.bench_function("decode_view", |b| { b.iter(|| { for payload in &dataset.payload { - let view = bench_buffa::proto3::GoogleMessage1View::decode_view(payload).unwrap(); + let view = GoogleMessage1View::decode_view(payload).unwrap(); criterion::black_box(&view); } }); diff --git a/benchmarks/buffa/src/lib.rs b/benchmarks/buffa/src/lib.rs index eb88176..8de253d 100644 --- a/benchmarks/buffa/src/lib.rs +++ b/benchmarks/buffa/src/lib.rs @@ -1,5 +1,27 @@ //! Generated protobuf types for buffa benchmarks. +macro_rules! include_generated { + ($stem:literal) => { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".rs")); + #[allow(non_camel_case_types, unused_imports, dead_code)] + pub mod view { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__view.rs")); + #[allow(non_camel_case_types, unused_imports, dead_code)] + pub mod oneofs { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__view_oneofs.rs")); + } + } + #[allow(non_camel_case_types, unused_imports, dead_code)] + pub mod ext { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__ext.rs")); + } + #[allow(non_camel_case_types, unused_imports, dead_code)] + pub mod oneofs { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__oneofs.rs")); + } + }; +} + #[allow( clippy::derivable_impls, clippy::enum_variant_names, @@ -10,7 +32,7 @@ dead_code )] pub mod bench { - include!(concat!(env!("OUT_DIR"), "/bench_messages.rs")); + include_generated!("bench_messages"); } #[allow( @@ -23,7 +45,7 @@ pub mod bench { dead_code )] pub mod benchmarks { - include!(concat!(env!("OUT_DIR"), "/benchmarks.rs")); + include_generated!("benchmarks"); } #[allow( @@ -36,5 +58,5 @@ pub mod benchmarks { dead_code )] pub mod proto3 { - include!(concat!(env!("OUT_DIR"), "/benchmark_message1_proto3.rs")); + include_generated!("benchmark_message1_proto3"); } diff --git a/benchmarks/gen-datasets/src/main.rs b/benchmarks/gen-datasets/src/main.rs index c4849ff..a0e7096 100644 --- a/benchmarks/gen-datasets/src/main.rs +++ b/benchmarks/gen-datasets/src/main.rs @@ -8,6 +8,28 @@ use rand::{Rng, SeedableRng}; use std::fs; use std::path::Path; +macro_rules! include_generated { + ($stem:literal) => { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".rs")); + #[allow(non_camel_case_types, unused_imports, dead_code)] + pub mod view { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__view.rs")); + #[allow(non_camel_case_types, unused_imports, dead_code)] + pub mod oneofs { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__view_oneofs.rs")); + } + } + #[allow(non_camel_case_types, unused_imports, dead_code)] + pub mod ext { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__ext.rs")); + } + #[allow(non_camel_case_types, unused_imports, dead_code)] + pub mod oneofs { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__oneofs.rs")); + } + }; +} + #[allow( clippy::derivable_impls, clippy::enum_variant_names, @@ -18,7 +40,7 @@ use std::path::Path; dead_code )] mod proto { - include!(concat!(env!("OUT_DIR"), "/bench_messages.rs")); + include_generated!("bench_messages"); } #[allow( clippy::derivable_impls, @@ -30,12 +52,12 @@ mod proto { dead_code )] mod dataset_proto { - include!(concat!(env!("OUT_DIR"), "/benchmarks.rs")); + include_generated!("benchmarks"); } -use proto::analytics_event::property::ValueOneof as Value; use proto::analytics_event::{Nested, Property}; use proto::log_record::Context; +use proto::oneofs::analytics_event::property::Value; use proto::*; const NUM_PAYLOADS: usize = 50; diff --git a/buffa-build/src/lib.rs b/buffa-build/src/lib.rs index fde11e4..7aa315e 100644 --- a/buffa-build/src/lib.rs +++ b/buffa-build/src/lib.rs @@ -589,29 +589,18 @@ impl Config { let generated = buffa_codegen::generate(&fds.file, &files_to_generate, &self.codegen_config)?; - // Build a map from generated file name to proto package for the - // module tree generator. - let file_to_package: std::collections::HashMap = fds - .file - .iter() - .map(|fd| { - let proto_name = fd.name.as_deref().unwrap_or(""); - let rs_name = buffa_codegen::proto_path_to_rust_module(proto_name); - let package = fd.package.as_deref().unwrap_or("").to_string(); - (rs_name, package) - }) - .collect(); - - // Write output files and collect (name, package) pairs. - let mut output_entries: Vec<(String, String)> = Vec::new(); + // Write output files and collect (name, package, kind) entries. + // Package/kind now come directly from `GeneratedFile`, so no + // separate lookup table is needed. + let mut output_entries: Vec<(String, String, buffa_codegen::GeneratedFileKind)> = + Vec::new(); for file in generated { let path = out_dir.join(&file.name); if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } write_if_changed(&path, file.content.as_bytes())?; - let package = file_to_package.get(&file.name).cloned().unwrap_or_default(); - output_entries.push((file.name, package)); + output_entries.push((file.name, file.package, file.kind)); } // Generate the include file if requested. @@ -824,7 +813,11 @@ fn proto_relative_name(file: &Path, includes: &[PathBuf]) -> String { /// `include!` directives use bare sibling paths (`include!("foo.bar.rs")`) /// instead of the `env!("OUT_DIR")` prefix, so the include file works when /// checked into the source tree and referenced via `mod`. -fn generate_include_file(entries: &[(String, String)], relative: bool) -> String { +fn generate_include_file( + entries: &[(String, String, buffa_codegen::GeneratedFileKind)], + relative: bool, +) -> String { + use buffa_codegen::GeneratedFileKind; use std::collections::BTreeMap; use std::fmt::Write; @@ -834,12 +827,16 @@ fn generate_include_file(entries: &[(String, String)], relative: bool) -> String #[derive(Default)] struct ModNode { - files: Vec, + owned_files: Vec, + view_files: Vec, + ext_files: Vec, + oneofs_files: Vec, + view_oneofs_files: Vec, children: BTreeMap, } let mut root = ModNode::default(); - for (file_name, package) in entries { + for (file_name, package, kind) in entries { let pkg_parts: Vec<&str> = if package.is_empty() { vec![] } else { @@ -849,25 +846,92 @@ fn generate_include_file(entries: &[(String, String)], relative: bool) -> String for seg in &pkg_parts { node = node.children.entry(seg.to_string()).or_default(); } - node.files.push(file_name.clone()); + match kind { + GeneratedFileKind::Owned => node.owned_files.push(file_name.clone()), + GeneratedFileKind::View => node.view_files.push(file_name.clone()), + GeneratedFileKind::Ext => node.ext_files.push(file_name.clone()), + GeneratedFileKind::Oneofs => node.oneofs_files.push(file_name.clone()), + GeneratedFileKind::ViewOneofs => node.view_oneofs_files.push(file_name.clone()), + } } let mut out = String::new(); writeln!(out, "// @generated by buffa-build. DO NOT EDIT.").unwrap(); writeln!(out).unwrap(); + fn write_include(out: &mut String, indent: &str, file: &str, relative: bool) { + use std::fmt::Write as _; + if relative { + writeln!(out, r#"{indent}include!("{file}");"#).unwrap(); + } else { + writeln!( + out, + r#"{indent}include!(concat!(env!("OUT_DIR"), "/{file}"));"# + ) + .unwrap(); + } + } + fn emit(out: &mut String, node: &ModNode, depth: usize, relative: bool) { let indent = " ".repeat(depth); - for file in &node.files { - if relative { - writeln!(out, r#"{indent}include!("{file}");"#).unwrap(); - } else { + for file in &node.owned_files { + write_include(out, &indent, file, relative); + } + // `pub mod view { … pub mod oneofs { … } }` — the view-oneofs + // modifier wraps the oneofs kind (DESIGN.md → "Generated code + // layout"). Multi-file packages coalesce into one wrapper each. + let emit_view = !node.view_files.is_empty() || !node.view_oneofs_files.is_empty(); + if emit_view { + writeln!( + out, + "{indent}#[allow(non_camel_case_types, dead_code, unused_imports, \ + clippy::derivable_impls, clippy::match_single_binding)]" + ) + .unwrap(); + writeln!(out, "{indent}pub mod view {{").unwrap(); + for file in &node.view_files { + write_include(out, &format!("{indent} "), file, relative); + } + if !node.view_oneofs_files.is_empty() { writeln!( out, - r#"{indent}include!(concat!(env!("OUT_DIR"), "/{file}"));"# + "{indent} #[allow(non_camel_case_types, dead_code, unused_imports, \ + clippy::derivable_impls, clippy::match_single_binding)]" ) .unwrap(); + writeln!(out, "{indent} pub mod oneofs {{").unwrap(); + for file in &node.view_oneofs_files { + write_include(out, &format!("{indent} "), file, relative); + } + writeln!(out, "{indent} }}").unwrap(); + } + writeln!(out, "{indent}}}").unwrap(); + } + if !node.ext_files.is_empty() { + writeln!( + out, + "{indent}#[allow(non_camel_case_types, dead_code, unused_imports, \ + clippy::derivable_impls, clippy::match_single_binding)]" + ) + .unwrap(); + writeln!(out, "{indent}pub mod ext {{").unwrap(); + for file in &node.ext_files { + write_include(out, &format!("{indent} "), file, relative); } + writeln!(out, "{indent}}}").unwrap(); + } + if !node.oneofs_files.is_empty() { + writeln!( + out, + "{indent}#[allow(non_camel_case_types, dead_code, unused_imports, \ + clippy::derivable_impls, clippy::match_single_binding)]" + ) + .unwrap(); + writeln!(out, "{indent}pub mod oneofs {{").unwrap(); + for file in &node.oneofs_files { + write_include(out, &format!("{indent} "), file, relative); + } + writeln!(out, "{indent}}}").unwrap(); } for (name, child) in &node.children { let escaped = escape_mod_name(name); @@ -938,9 +1002,18 @@ mod tests { #[test] fn include_file_out_dir_mode_uses_env_var() { + use buffa_codegen::GeneratedFileKind; let entries = vec![ - ("foo.bar.rs".to_string(), "foo".to_string()), - ("root.rs".to_string(), String::new()), + ( + "foo.bar.rs".to_string(), + "foo".to_string(), + GeneratedFileKind::Owned, + ), + ( + "root.rs".to_string(), + String::new(), + GeneratedFileKind::Owned, + ), ]; let out = generate_include_file(&entries, false); assert!( @@ -956,9 +1029,18 @@ mod tests { #[test] fn include_file_relative_mode_uses_sibling_paths() { + use buffa_codegen::GeneratedFileKind; let entries = vec![ - ("foo.bar.rs".to_string(), "foo".to_string()), - ("root.rs".to_string(), String::new()), + ( + "foo.bar.rs".to_string(), + "foo".to_string(), + GeneratedFileKind::Owned, + ), + ( + "root.rs".to_string(), + String::new(), + GeneratedFileKind::Owned, + ), ]; let out = generate_include_file(&entries, true); assert!( @@ -980,9 +1062,18 @@ mod tests { // Two files in the same depth-2 package: verifies the relative flag // propagates through recursive emit() calls and both files land in // the same innermost mod. + use buffa_codegen::GeneratedFileKind; let entries = vec![ - ("a.b.one.rs".to_string(), "a.b".to_string()), - ("a.b.two.rs".to_string(), "a.b".to_string()), + ( + "a.b.one.rs".to_string(), + "a.b".to_string(), + GeneratedFileKind::Owned, + ), + ( + "a.b.two.rs".to_string(), + "a.b".to_string(), + GeneratedFileKind::Owned, + ), ]; let out = generate_include_file(&entries, true); // Both includes should appear once, at the same depth-2 indent, diff --git a/buffa-codegen/src/context.rs b/buffa-codegen/src/context.rs index 5663af0..c1df812 100644 --- a/buffa-codegen/src/context.rs +++ b/buffa-codegen/src/context.rs @@ -173,6 +173,17 @@ impl<'a> CodeGenContext<'a> { self.type_map.get(proto_fqn).map(|s| s.as_str()) } + /// Look up the proto package of a fully-qualified protobuf type name. + /// + /// `proto_fqn` uses dotted form without the leading dot + /// (e.g. `"google.protobuf.Timestamp"`). Returns `None` for types + /// not present in the compilation set (extern types fall through to + /// the fallback path at call sites — view-path resolution uses this + /// to distinguish top-level vs nested targets). + pub(crate) fn package_of(&self, proto_fqn: &str) -> Option<&str> { + self.package_of.get(proto_fqn).map(|s| s.as_str()) + } + /// Look up the source comment for a protobuf element by FQN. /// /// `fqn` uses the same dotted form as `proto_fqn` throughout codegen @@ -361,6 +372,13 @@ pub(crate) struct MessageScope<'a> { /// sits inside. Controls the count of `super::` prefixes in type /// references via [`CodeGenContext::rust_type_relative`]. pub nesting: usize, + /// `true` when the scope is emitting code into the top-level `view::` + /// module (the view tree at package scope). `false` for owned-type + /// scopes AND for nested-message views, which continue to live inside + /// the owner's message sub-module as `FooView<'a>` (the pre-namespace + /// layout). Threaded so view-path helpers can choose between the + /// `super::view::FooView<'a>` and legacy `FooView<'a>` rewrites. + pub in_view_tree: bool, } impl<'a> MessageScope<'a> { @@ -372,14 +390,18 @@ impl<'a> MessageScope<'a> { proto_fqn, features, nesting: self.nesting + 1, + in_view_tree: self.in_view_tree, } } /// Return a copy of this scope with the nesting depth incremented by 1. /// - /// Use this when generating code that will live inside the message's own - /// `pub mod` (e.g. oneof view enums) without changing the identity - /// (`proto_fqn`, `features`). + /// Previously used for oneof-view-enum emission (one module deeper + /// than the view struct). The `oneofs::` tree lift takes emission + /// two levels deeper (into `view::oneofs::`) rather than + /// one, so view.rs now constructs the scope explicitly; this + /// helper remains as a general utility for future consumers. + #[allow(dead_code)] pub fn deeper(&self) -> MessageScope<'a> { MessageScope { nesting: self.nesting + 1, diff --git a/buffa-codegen/src/impl_message.rs b/buffa-codegen/src/impl_message.rs index 4e4e8a5..51df7f6 100644 --- a/buffa-codegen/src/impl_message.rs +++ b/buffa-codegen/src/impl_message.rs @@ -315,8 +315,11 @@ pub fn generate_message_impl( .map(|f| repeated_merge_arm(ctx, f, proto_fqn, features, preserve_unknown_fields)) .collect::, _>>()?; - // Collect oneof compute/write/merge tokens. - let mod_ident = crate::message::make_field_ident(&crate::oneof::to_snake_case(rust_name)); + // Collect oneof compute/write/merge tokens. The oneof enum lives + // in the parallel `oneofs::` tree; `oneofs_prefix` is the token + // stream path from the current emission scope (owned message + // impl) to the oneofs sub-module for this message. + let oneofs_prefix = crate::message::oneofs_path_prefix(current_package, proto_fqn, nesting); let mut oneof_compute_stmts: Vec = Vec::new(); let mut oneof_write_stmts: Vec = Vec::new(); let mut oneof_merge_arms: Vec = Vec::new(); @@ -326,7 +329,7 @@ pub fn generate_message_impl( enum_ident, oneof_name, fields, - &mod_ident, + &oneofs_prefix, proto_fqn, features, preserve_unknown_fields, @@ -2119,14 +2122,16 @@ fn generate_oneof_impls( enum_ident: &proc_macro2::Ident, oneof_name: &str, fields: &[&FieldDescriptorProto], - mod_ident: &proc_macro2::Ident, + oneofs_prefix: &TokenStream, proto_fqn: &str, features: &ResolvedFeatures, preserve_unknown_fields: bool, ) -> Result<(TokenStream, TokenStream, Vec), CodeGenError> { let field_ident = make_field_ident(oneof_name); - // Module-qualified path: the oneof enum lives in the message's module. - let qualified_enum: TokenStream = quote! { #mod_ident::#enum_ident }; + // Module-qualified path: the oneof enum lives in the parallel + // `oneofs::` tree at `::`. `oneofs_prefix` + // already ends with `::`. + let qualified_enum: TokenStream = quote! { #oneofs_prefix #enum_ident }; let mut size_arms: Vec = Vec::new(); let mut write_arms: Vec = Vec::new(); diff --git a/buffa-codegen/src/impl_text.rs b/buffa-codegen/src/impl_text.rs index c942015..fb28332 100644 --- a/buffa-codegen/src/impl_text.rs +++ b/buffa-codegen/src/impl_text.rs @@ -30,7 +30,7 @@ use crate::impl_message::{ is_supported_field_type, }; use crate::message::{is_closed_enum, is_map_field, make_field_ident}; -use crate::oneof::{is_boxed_variant, to_snake_case}; +use crate::oneof::is_boxed_variant; use crate::CodeGenError; /// Generate `impl ::buffa::text::TextFormat for #name_ident { ... }`. @@ -98,7 +98,8 @@ pub(crate) fn generate_text_impl( } let name_ident = format_ident!("{}", rust_name); - let mod_ident = make_field_ident(&to_snake_case(rust_name)); + // Path prefix to this message's oneof enums in the `oneofs::` tree. + let oneofs_prefix = crate::message::oneofs_path_prefix(current_package, proto_fqn, nesting); // ── field grouping (mirrors generate_message_impl) ────────────────────── @@ -163,7 +164,7 @@ pub(crate) fn generate_text_impl( let oneof_encode: Vec<_> = oneof_groups .iter() .map(|(name, enum_ident, fields)| { - oneof_encode_stmt(ctx, enum_ident, name, fields, &mod_ident, features) + oneof_encode_stmt(ctx, enum_ident, name, fields, &oneofs_prefix, features) }) .collect::>()?; let map_encode: Vec<_> = map_fields @@ -188,7 +189,7 @@ pub(crate) fn generate_text_impl( enum_ident, name, fields, - &mod_ident, + &oneofs_prefix, current_package, proto_fqn, features, @@ -723,11 +724,11 @@ fn oneof_encode_stmt( enum_ident: &proc_macro2::Ident, oneof_name: &str, fields: &[&FieldDescriptorProto], - mod_ident: &proc_macro2::Ident, + oneofs_prefix: &TokenStream, parent_features: &ResolvedFeatures, ) -> Result { let field_ident = make_field_ident(oneof_name); - let qualified: TokenStream = quote! { #mod_ident::#enum_ident }; + let qualified: TokenStream = quote! { #oneofs_prefix #enum_ident }; let mut arms: Vec = Vec::new(); for field in fields { @@ -782,14 +783,14 @@ fn oneof_merge_arms( enum_ident: &proc_macro2::Ident, oneof_name: &str, fields: &[&FieldDescriptorProto], - mod_ident: &proc_macro2::Ident, + oneofs_prefix: &TokenStream, current_package: &str, proto_fqn: &str, parent_features: &ResolvedFeatures, nesting: usize, ) -> Result, CodeGenError> { let field_ident = make_field_ident(oneof_name); - let qualified: TokenStream = quote! { #mod_ident::#enum_ident }; + let qualified: TokenStream = quote! { #oneofs_prefix #enum_ident }; let mut arms: Vec = Vec::new(); for field in fields { diff --git a/buffa-codegen/src/lib.rs b/buffa-codegen/src/lib.rs index b709aa9..babb285 100644 --- a/buffa-codegen/src/lib.rs +++ b/buffa-codegen/src/lib.rs @@ -41,12 +41,69 @@ use proc_macro2::TokenStream; use quote::quote; /// Result of generating Rust code for a single `.proto` file. -#[derive(Debug)] +/// +/// Buffa's generated-code layout places ancillary types in **kind-namespaced +/// parallel trees** at each package root (see DESIGN.md → "Generated code +/// layout"). The kind segment is outermost; for kinds that modify other +/// kinds (only `view` today), the modifier wraps the modified: +/// +/// - `pkg::` — owned types at package scope (messages, nested +/// modules, package-level enums). +/// - `pkg::view::::View` — views mirroring the owned +/// proto-message path. Views retain the `View` suffix (the one +/// documented exception to the tree-disambiguates-ident rule — +/// message + view are commonly co-imported into the same scope). +/// - `pkg::oneofs::::` — oneof enums, lifted out of +/// the owned tree into a dedicated parallel tree. +/// - `pkg::view::oneofs::::` — views of oneof enums +/// (the `view` modifier wraps the `oneofs` kind). +/// - `pkg::ext::` — file-level extension consts + `register_types`. +/// +/// Each proto file produces up to FIVE sibling output files, which +/// [`generate_module_tree`] stitches into the tree above: +/// +/// - `.rs` — owned tree contents, emitted at package scope. +/// - `.__view.rs` — view tree contents (top-level view structs +/// and nested-view sub-modules), destined for `pub mod view`. +/// - `.__ext.rs` — ext tree contents (extension consts + register +/// fn), destined for `pub mod ext`. +/// - `.__oneofs.rs` — owned oneof enums grouped by owner, destined +/// for `pub mod oneofs`. +/// - `.__view_oneofs.rs` — view-of-oneof enums, destined for +/// `pub mod view::oneofs`. +/// +/// The [`kind`](Self::kind) field identifies which of the five a file is. +/// Files missing content for a particular kind (e.g. a proto with no +/// oneofs) still emit an empty sibling so downstream tooling +/// ([`protoc-gen-buffa-packaging`], `buffa-build`) can use stable +/// `include!` paths without probing the filesystem. +#[derive(Debug, Clone)] pub struct GeneratedFile { - /// The output file path (e.g., "my_package.rs"). + /// The output file path (e.g., "my_package.rs", "my_package.__view.rs"). pub name: String, /// The generated Rust source code. pub content: String, + /// Which stream this file represents. + pub kind: GeneratedFileKind, + /// Proto-package dotted name (e.g. `"google.protobuf"`, or empty + /// for no package). Used by [`generate_module_tree`] to group + /// sibling files into the right `pub mod ` target. + pub package: String, +} + +/// Which sub-tree within a proto-file's generated output a file represents. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GeneratedFileKind { + /// Owned structs, enums, nested modules — emitted at package scope. + Owned, + /// View tree contents — to be placed inside a package-level `pub mod view`. + View, + /// Extension consts + `register_types` — placed inside `pub mod ext`. + Ext, + /// Oneof enums — placed inside `pub mod oneofs`. + Oneofs, + /// Views of oneof enums — placed inside `pub mod view { pub mod oneofs }`. + ViewOneofs, } /// Configuration for code generation. @@ -129,12 +186,14 @@ pub struct CodeGenConfig { pub generate_text: bool, /// Whether to emit the file-level `register_types(&mut TypeRegistry)` fn. /// - /// Default `true`. Set to `false` when multiple generated files are - /// `include!`d into the same namespace (the identically-named fns would - /// collide) — e.g. `buffa-types`' WKTs, which hand-roll - /// `register_wkt_types` instead. The per-message `__*_JSON_ANY` / - /// `__*_TEXT_ANY` consts are still emitted; only the aggregating fn - /// is suppressed. + /// Default `true`. Emitted inside `pub mod ext { ... }` at the + /// package root alongside file-level extension consts (see the + /// [`ext` sub-module][GeneratedFileKind::Ext] of the generated + /// output). Set to `false` when a consumer hand-rolls its own + /// registration fn — e.g. `buffa-types` provides `register_wkt_types` + /// instead. The per-message `__*_JSON_ANY` / `__*_TEXT_ANY` consts + /// are still emitted at package scope; only the aggregating fn is + /// suppressed. pub emit_register_fn: bool, /// Custom attributes to inject on generated types (messages and enums). /// @@ -250,24 +309,94 @@ pub fn generate( .find(|f| f.name.as_deref() == Some(file_name.as_str())) .ok_or_else(|| CodeGenError::FileNotFound(file_name.clone()))?; - let content = generate_file(&ctx, file_desc)?; - let rust_filename = proto_path_to_rust_module(file_name); + let FileOutputs { + owned, + view, + ext, + oneofs, + view_oneofs, + } = generate_file(&ctx, file_desc)?; + let owned_stem = proto_path_to_rust_module(file_name); + let stem = owned_stem + .strip_suffix(".rs") + .unwrap_or(&owned_stem) + .to_string(); + let package = file_desc.package.as_deref().unwrap_or("").to_string(); output.push(GeneratedFile { - name: rust_filename, - content, + name: owned_stem.clone(), + content: owned, + kind: GeneratedFileKind::Owned, + package: package.clone(), + }); + // Always emit the sibling files, even when empty. Keeping them + // unconditional means `protoc-gen-buffa-packaging` (which can't + // tell whether a given file has view / ext / oneofs content) and + // `buffa-build` both generate stable `include!(...)` directives + // safely — an empty file expands to no tokens. + let empty_stream_header = |label: &str| -> String { + let source_line = file_desc + .name + .as_ref() + .map_or(String::new(), |n| format!("// source: {n}\n")); + format!( + "// @generated by protoc-gen-buffa. DO NOT EDIT. ({label}, empty)\n{source_line}\n" + ) + }; + output.push(GeneratedFile { + name: format!("{stem}.__view.rs"), + content: view.unwrap_or_else(|| empty_stream_header("view:: module contents")), + kind: GeneratedFileKind::View, + package: package.clone(), + }); + output.push(GeneratedFile { + name: format!("{stem}.__ext.rs"), + content: ext.unwrap_or_else(|| empty_stream_header("ext:: module contents")), + kind: GeneratedFileKind::Ext, + package: package.clone(), + }); + output.push(GeneratedFile { + name: format!("{stem}.__oneofs.rs"), + content: oneofs.unwrap_or_else(|| empty_stream_header("oneofs:: module contents")), + kind: GeneratedFileKind::Oneofs, + package: package.clone(), + }); + output.push(GeneratedFile { + name: format!("{stem}.__view_oneofs.rs"), + content: view_oneofs + .unwrap_or_else(|| empty_stream_header("view::oneofs:: module contents")), + kind: GeneratedFileKind::ViewOneofs, + package, }); } Ok(output) } +/// Five output streams produced by [`generate_file`] for a single proto. +struct FileOutputs { + owned: String, + view: Option, + ext: Option, + oneofs: Option, + view_oneofs: Option, +} + /// Generate a module tree that assembles generated `.rs` files into /// nested `pub mod` blocks matching the protobuf package hierarchy. /// -/// Each entry is a `(file_name, package)` pair where `package` is the -/// dot-separated protobuf package name (e.g., `"google.api"`). The module -/// tree is built from the **package** hierarchy so that `super::`-based -/// cross-package references resolve correctly. +/// Each entry is a `ModuleTreeEntry` that records the file name, its +/// proto package, and which stream it belongs to +/// ([`GeneratedFileKind`]). Within each package module, sibling files +/// are coalesced into: +/// +/// - The package scope itself (owned files `include!`d directly). +/// - A single `pub mod view { … }` wrapper that `include!`s every +/// view-stream file in the package. +/// - A single `pub mod ext { … }` wrapper likewise. +/// +/// This lets multi-file-per-package setups (e.g. `google.rpc` has +/// `status.rs` + `error_details.rs`) share one `view::` and one `ext::` +/// module per package instead of colliding on per-file wrappers. /// /// `include_prefix` is prepended to file names in `include!` directives. /// Use `""` for relative paths or `concat!(env!("OUT_DIR"), "/")` style @@ -279,7 +408,7 @@ pub fn generate( /// is consumed via `include!` (inner attributes are not valid in that /// context). pub fn generate_module_tree( - entries: &[(&str, &str)], + entries: &[ModuleTreeEntry<'_>], include_prefix: &str, emit_inner_allow: bool, ) -> String { @@ -290,24 +419,36 @@ pub fn generate_module_tree( #[derive(Default)] struct ModNode { - files: Vec, + owned_files: Vec, + view_files: Vec, + ext_files: Vec, + oneofs_files: Vec, + view_oneofs_files: Vec, children: BTreeMap, } let mut root = ModNode::default(); - for (file_name, package) in entries { - let pkg_parts: Vec<&str> = if package.is_empty() { + for entry in entries { + let pkg_parts: Vec<&str> = if entry.package.is_empty() { vec![] } else { - package.split('.').collect() + entry.package.split('.').collect() }; let mut node = &mut root; for seg in &pkg_parts { node = node.children.entry(seg.to_string()).or_default(); } - node.files.push(file_name.to_string()); + match entry.kind { + GeneratedFileKind::Owned => node.owned_files.push(entry.file_name.to_string()), + GeneratedFileKind::View => node.view_files.push(entry.file_name.to_string()), + GeneratedFileKind::Ext => node.ext_files.push(entry.file_name.to_string()), + GeneratedFileKind::Oneofs => node.oneofs_files.push(entry.file_name.to_string()), + GeneratedFileKind::ViewOneofs => { + node.view_oneofs_files.push(entry.file_name.to_string()) + } + } } let mut out = String::new(); @@ -324,10 +465,52 @@ pub fn generate_module_tree( fn emit(out: &mut String, node: &ModNode, depth: usize, prefix: &str, lints: &str) { let indent = " ".repeat(depth); - for file in &node.files { + for file in &node.owned_files { writeln!(out, r#"{indent}include!("{prefix}{file}");"#).unwrap(); } + // `pub mod view { … }` wraps both the ordinary view files and + // (when present) an inner `pub mod oneofs { … }` for view-of- + // oneof enums — the `view` modifier wraps the `oneofs` kind, + // following the generated-code layout rule. Multi-file + // packages emit one `pub mod view` per package regardless of + // how many source `.proto`s contributed content. + let emit_view_wrapper = !node.view_files.is_empty() || !node.view_oneofs_files.is_empty(); + if emit_view_wrapper { + writeln!(out, "{indent}#[allow({lints})]").unwrap(); + writeln!(out, "{indent}pub mod view {{").unwrap(); + for file in &node.view_files { + writeln!(out, r#"{indent} include!("{prefix}{file}");"#).unwrap(); + } + if !node.view_oneofs_files.is_empty() { + writeln!(out, "{indent} #[allow({lints})]").unwrap(); + writeln!(out, "{indent} pub mod oneofs {{").unwrap(); + for file in &node.view_oneofs_files { + writeln!(out, r#"{indent} include!("{prefix}{file}");"#).unwrap(); + } + writeln!(out, "{indent} }}").unwrap(); + } + writeln!(out, "{indent}}}").unwrap(); + } + + if !node.ext_files.is_empty() { + writeln!(out, "{indent}#[allow({lints})]").unwrap(); + writeln!(out, "{indent}pub mod ext {{").unwrap(); + for file in &node.ext_files { + writeln!(out, r#"{indent} include!("{prefix}{file}");"#).unwrap(); + } + writeln!(out, "{indent}}}").unwrap(); + } + + if !node.oneofs_files.is_empty() { + writeln!(out, "{indent}#[allow({lints})]").unwrap(); + writeln!(out, "{indent}pub mod oneofs {{").unwrap(); + for file in &node.oneofs_files { + writeln!(out, r#"{indent} include!("{prefix}{file}");"#).unwrap(); + } + writeln!(out, "{indent}}}").unwrap(); + } + for (name, child) in &node.children { let escaped = escape_mod_ident(name); writeln!(out, "{indent}#[allow({lints})]").unwrap(); @@ -342,6 +525,17 @@ pub fn generate_module_tree( out } +/// Per-file entry passed to [`generate_module_tree`]. +#[derive(Debug, Clone, Copy)] +pub struct ModuleTreeEntry<'a> { + /// Name (or relative path) of the `.rs` file to `include!`. + pub file_name: &'a str, + /// Proto package, dotted form, or `""` for no package. + pub package: &'a str, + /// Which sub-tree the file belongs to. + pub kind: GeneratedFileKind, +} + /// Check that no fields in the file use the `__buffa_` reserved prefix. fn check_reserved_field_names(file: &FileDescriptorProto) -> Result<(), CodeGenError> { fn check_message( @@ -424,63 +618,24 @@ fn check_module_name_conflicts(file: &FileDescriptorProto) -> Result<(), CodeGen check_siblings(&file.message_type, package) } -/// Check that no message named `FooView` collides with the generated view -/// type for a sibling message `Foo`. -fn check_view_name_conflicts(file: &FileDescriptorProto) -> Result<(), CodeGenError> { - use std::collections::HashSet; - - fn check_siblings( - messages: &[crate::generated::descriptor::DescriptorProto], - scope: &str, - ) -> Result<(), CodeGenError> { - // Collect all message names at this level. - let names: HashSet<&str> = messages.iter().filter_map(|m| m.name.as_deref()).collect(); - - // For each message Foo, check if FooView also exists. - for msg in messages { - let name = msg.name.as_deref().unwrap_or(""); - let view_name = format!("{}View", name); - if names.contains(view_name.as_str()) { - return Err(CodeGenError::ViewNameConflict { - scope: scope.to_string(), - owned_msg: name.to_string(), - view_msg: view_name, - }); - } - } - - // Recurse into nested messages. - for msg in messages { - let name = msg.name.as_deref().unwrap_or(""); - let child_scope = if scope.is_empty() { - name.to_string() - } else { - format!("{}.{}", scope, name) - }; - check_siblings(&msg.nested_type, &child_scope)?; - } - - Ok(()) - } - - let package = file.package.as_deref().unwrap_or(""); - check_siblings(&file.message_type, package) -} - /// Generate Rust source for a single `.proto` file. fn generate_file( ctx: &context::CodeGenContext, file: &FileDescriptorProto, -) -> Result { +) -> Result { // Validate descriptors before generating code. check_reserved_field_names(file)?; check_module_name_conflicts(file)?; - if ctx.config.generate_views { - check_view_name_conflicts(file)?; - } let resolver = imports::ImportResolver::for_file(file); - let mut tokens = resolver.generate_use_block(); + let mut owned_tokens = resolver.generate_use_block(); + let mut view_tokens = TokenStream::new(); + // Oneof-tree accumulators. Each top-level message contributes one + // `pub mod { ... }` wrapper to each of these streams + // (containing direct oneof enums and recursive sub-modules for + // nested messages' oneofs). + let mut oneofs_tokens = TokenStream::new(); + let mut view_oneofs_tokens = TokenStream::new(); let current_package = file.package.as_deref().unwrap_or(""); let features = crate::features::for_file(file); for enum_type in &file.enum_type { @@ -490,7 +645,7 @@ fn generate_file( } else { format!("{}.{}", current_package, enum_rust_name) }; - tokens.extend(enumeration::generate_enum( + owned_tokens.extend(enumeration::generate_enum( ctx, enum_type, enum_rust_name, @@ -512,7 +667,12 @@ fn generate_file( } else { format!("{}.{}", current_package, top_level_name) }; - let (msg_top, msg_mod, msg_reg) = message::generate_message( + let message::MessageOutput { + top_level: msg_top, + mod_items: msg_mod, + oneof_items: msg_oneofs, + registry_paths: msg_reg, + } = message::generate_message( ctx, message_type, current_package, @@ -521,7 +681,7 @@ fn generate_file( &features, &resolver, )?; - tokens.extend(msg_top); + owned_tokens.extend(msg_top); // Nested extension const paths are relative to the message's module // scope; prefix with `::` for the package-level view. let mod_name = crate::oneof::to_snake_case(top_level_name); @@ -537,8 +697,40 @@ fn generate_file( reg.json_any.extend(msg_reg.json_any); reg.text_any.extend(msg_reg.text_any); - let view_mod = if ctx.config.generate_views { - let (view_top, view_mod) = view::generate_view( + if !msg_mod.is_empty() { + owned_tokens.extend(quote! { + pub mod #mod_ident { + #[allow(unused_imports)] + use super::*; + #msg_mod + } + }); + } + + // Wrap this message's oneof items in `pub mod ` for + // the oneofs:: tree. Empty when neither this message nor any + // of its descendants declares a oneof. + if !msg_oneofs.is_empty() { + oneofs_tokens.extend(quote! { + pub mod #mod_ident { + #[allow(unused_imports)] + use super::*; + #msg_oneofs + } + }); + } + + // Generate view items for this top-level message (and recursively + // for all of its non-map nested messages). The returned streams + // are the raw contents that belong inside `pub mod view { … }` + // and `pub mod view { pub mod oneofs { … } }` at the package + // level — `generate_module_tree` coalesces every file's view + // stream into a single wrapper per package. + if ctx.config.generate_views { + let view::ViewOutput { + items: view_items, + oneof_items: view_oneof_items, + } = view::generate_view( ctx, message_type, current_package, @@ -546,77 +738,144 @@ fn generate_file( &proto_fqn, &features, )?; - tokens.extend(view_top); - view_mod - } else { - TokenStream::new() - }; - - // Combine message and view module items into a single `pub mod`. - if !msg_mod.is_empty() || !view_mod.is_empty() { - tokens.extend(quote! { - pub mod #mod_ident { - #[allow(unused_imports)] - use super::*; - #msg_mod - #view_mod - } - }); + view_tokens.extend(view_items); + if !view_oneof_items.is_empty() { + view_oneofs_tokens.extend(quote! { + pub mod #mod_ident { + #[allow(unused_imports)] + use super::*; + #view_oneof_items + } + }); + } } } + // File-level extensions. The emitted tokens expect to live inside + // `pub mod ext { ... }` at the package level (one hop below package + // root), so type references inside them use nesting=1. let (file_ext_tokens, file_ext_json, file_ext_text) = extension::generate_extensions( ctx, &file.extension, current_package, - 0, + 1, &features, current_package, )?; - tokens.extend(file_ext_tokens); - for id in file_ext_json { - reg.json_ext.push(quote! { #id }); - } - for id in file_ext_text { - reg.text_ext.push(quote! { #id }); - } - - // `register_types(&mut TypeRegistry)` — one call per entry, split by - // format. Only emitted when at least one entry exists. Lines are - // gated at codegen time by `generate_json` / `generate_text`; the - // corresponding `register_*` methods on `TypeRegistry` are feature-gated - // in buffa, so a flag/feature mismatch surfaces as a compile error. - if ctx.config.emit_register_fn && !reg.is_empty() { - let json_any = ®.json_any; - let json_ext = ®.json_ext; - let text_any = ®.text_any; - let text_ext = ®.text_ext; - tokens.extend(quote! { - /// Register this file's `Any` type entries and extension entries - /// (JSON and/or text, per codegen config) with the given registry. - pub fn register_types(reg: &mut ::buffa::type_registry::TypeRegistry) { - #( reg.register_json_any(#json_any); )* - #( reg.register_json_ext(#json_ext); )* - #( reg.register_text_any(#text_any); )* - #( reg.register_text_ext(#text_ext); )* + // File-level ext consts live in the ext:: module itself — bare idents + // from register_types (no super::). Nested-in-message ext consts are + // already in `reg.json_ext` / `reg.text_ext` with `mod_ident :: CONST` + // form and need `super::` to climb to package level. + let file_ext_json_refs: Vec = + file_ext_json.into_iter().map(|id| quote! { #id }).collect(); + let file_ext_text_refs: Vec = + file_ext_text.into_iter().map(|id| quote! { #id }).collect(); + + let mut ext_tokens = TokenStream::new(); + let emit_ext = ctx.config.emit_register_fn + && (!reg.is_empty() + || !file_ext_tokens.is_empty() + || !file_ext_json_refs.is_empty() + || !file_ext_text_refs.is_empty()); + if emit_ext { + let json_any: Vec = + reg.json_any.iter().map(|p| quote! { super::#p }).collect(); + let text_any: Vec = + reg.text_any.iter().map(|p| quote! { super::#p }).collect(); + // Nested-in-message ext consts: climb out of ext:: to package scope. + let nested_json_ext: Vec = + reg.json_ext.iter().map(|p| quote! { super::#p }).collect(); + let nested_text_ext: Vec = + reg.text_ext.iter().map(|p| quote! { super::#p }).collect(); + let file_json_ext = &file_ext_json_refs; + let file_text_ext = &file_ext_text_refs; + let has_any_reg = + !reg.is_empty() || !file_ext_json_refs.is_empty() || !file_ext_text_refs.is_empty(); + let register_fn = if has_any_reg { + quote! { + /// Register this file's `Any` type entries and extension + /// entries (JSON and/or text, per codegen config) with the + /// given registry. See also the [`ext`] module for the + /// individual `Extension` consts the generated code uses. + pub fn register_types(reg: &mut ::buffa::type_registry::TypeRegistry) { + #( reg.register_json_any(#json_any); )* + #( reg.register_json_ext(#nested_json_ext); )* + #( reg.register_json_ext(#file_json_ext); )* + #( reg.register_text_any(#text_any); )* + #( reg.register_text_ext(#nested_text_ext); )* + #( reg.register_text_ext(#file_text_ext); )* + } } + } else { + quote! {} + }; + // Raw contents only — no `pub mod ext { … }` wrapper. The wrapper + // is inserted by `generate_module_tree` (or by hand-written + // stitching, as in `buffa-types/src/lib.rs`). + ext_tokens.extend(quote! { + #[allow(unused_imports)] + use super::*; + #file_ext_tokens + #register_fn }); + } else if !file_ext_tokens.is_empty() { + // emit_register_fn=false path: keep extensions at package scope so + // existing consumers (e.g. the hand-written `register_wkt_types`) + // that reference them by module-local name continue to work. The + // ext items get inlined into the owned stream. + owned_tokens.extend(file_ext_tokens); } - // Parse the token stream into a syn::File and format with prettyplease. - // Regular `//` comments cannot appear inside quote! blocks, so the file - // header is prepended as a raw string after formatting. + Ok(FileOutputs { + owned: render_stream(owned_tokens, file, "package-level owned items")?, + view: if view_tokens.is_empty() { + None + } else { + Some(render_stream(view_tokens, file, "view:: module contents")?) + }, + ext: if ext_tokens.is_empty() { + None + } else { + Some(render_stream(ext_tokens, file, "ext:: module contents")?) + }, + oneofs: if oneofs_tokens.is_empty() { + None + } else { + Some(render_stream( + oneofs_tokens, + file, + "oneofs:: module contents", + )?) + }, + view_oneofs: if view_oneofs_tokens.is_empty() { + None + } else { + Some(render_stream( + view_oneofs_tokens, + file, + "view::oneofs:: module contents", + )?) + }, + }) +} + +/// Format an accumulated `TokenStream` into Rust source with the +/// standard `@generated` header. Shared by the owned, view, and ext +/// streams so each file gets the same preamble. +fn render_stream( + tokens: TokenStream, + file: &FileDescriptorProto, + stream_label: &str, +) -> Result { let syntax_tree = syn::parse2::(tokens).map_err(|e| CodeGenError::InvalidSyntax(e.to_string()))?; let formatted = prettyplease::unparse(&syntax_tree); - let source_line = file .name .as_ref() .map_or(String::new(), |n| format!("// source: {n}\n")); - Ok(format!( - "// @generated by protoc-gen-buffa. DO NOT EDIT.\n{source_line}\n{formatted}" + "// @generated by protoc-gen-buffa. DO NOT EDIT. ({stream_label})\n{source_line}\n{formatted}" )) } @@ -677,32 +936,6 @@ pub enum CodeGenError { name_b: String, module_name: String, }, - /// The `{Name}Oneof` identifier buffa would emit for a oneof collides - /// with another name in the parent message's Rust module (a nested - /// message, a nested enum, or — when view generation is enabled — a - /// `{Name}OneofView`-equivalent sibling). Resolve by renaming the - /// oneof or the colliding nested type in the `.proto`. - #[error( - "name conflict in '{scope}': oneof '{oneof_name}' would emit as \ - '{attempted}', but that name already names another item in the \ - enclosing scope" - )] - OneofNameConflict { - scope: String, - oneof_name: String, - attempted: String, - }, - /// A message named `FooView` collides with the generated view type for - /// message `Foo`. - #[error( - "name conflict in '{scope}': message '{view_msg}' collides with \ - the generated view type for message '{owned_msg}'" - )] - ViewNameConflict { - scope: String, - owned_msg: String, - view_msg: String, - }, /// The input contains a message with `option message_set_wire_format = true` /// but [`CodeGenConfig::allow_message_set`] was not set. #[error( diff --git a/buffa-codegen/src/message.rs b/buffa-codegen/src/message.rs index 9feb806..a6f41f3 100644 --- a/buffa-codegen/src/message.rs +++ b/buffa-codegen/src/message.rs @@ -11,6 +11,62 @@ use crate::features::ResolvedFeatures; use crate::impl_message::{is_explicit_presence_scalar, is_real_oneof_member}; use crate::CodeGenError; +/// Build the Rust path prefix to THIS message's oneof-enum sub-module in +/// the parallel `oneofs::` tree, relative to the current owned-tree scope. +/// +/// Owned-tree scope at module depth `nesting` (counted from the package +/// root) wants to reference an oneof enum `Kind` living at +/// `::oneofs::::::Kind`, where +/// `::` mirrors the proto nesting of the current +/// message. +/// +/// Emitted prefix has the form `super::super::...::oneofs::::::` +/// with one `super::` per `nesting` level (to climb out to package scope) +/// plus `oneofs::` plus the snake_cased owner chain plus the current +/// message's own snake_cased module name. Trailing `::` is included so +/// callers can append the enum ident directly. +/// +/// Examples: +/// +/// ```ignore +/// // Top-level `Foo` at package scope (nesting=0): +/// oneofs_path_prefix(scope with nesting=0, proto_fqn="pkg.Foo") → `oneofs::foo::` +/// +/// // Nested `Outer.Inner` at depth 1 (inside `pub mod outer`): +/// oneofs_path_prefix(scope with nesting=1, proto_fqn="pkg.Outer.Inner") → `super::oneofs::outer::inner::` +/// ``` +pub(crate) fn oneofs_path_prefix( + current_package: &str, + proto_fqn: &str, + nesting: usize, +) -> TokenStream { + // `super::super::...::` — one hop per module level we sit below the + // package root. `syn::parse_str` handles the trailing `::` fine. + let supers = "super::".repeat(nesting); + let supers_tokens: TokenStream = syn::parse_str(&supers).unwrap_or_default(); + + // Snake-cased path of owner messages + this message's own name, + // derived from proto_fqn by stripping the package prefix. + let within_pkg = if current_package.is_empty() { + proto_fqn + } else { + proto_fqn + .strip_prefix(current_package) + .and_then(|s| s.strip_prefix('.')) + .unwrap_or(proto_fqn) + }; + let snake_segments: Vec = within_pkg + .split('.') + .filter(|s| !s.is_empty()) + .map(|name| { + let ident = crate::idents::make_field_ident(&crate::oneof::to_snake_case(name)); + quote! { #ident :: } + }) + .collect(); + + quote! { #supers_tokens oneofs:: #(#snake_segments)* } +} + /// Qualified paths to the per-message / per-extension registry `const` items /// emitted alongside the structs, bubbled up to the file-level /// `register_types` fn. Each vec is relative to the scope where the @@ -49,13 +105,35 @@ impl RegistryPaths { /// `proto_fqn` is the fully-qualified proto type name without a leading dot /// (e.g. `google.protobuf.Timestamp`, `my.package.Outer.Inner`). It is used /// to emit the `TYPE_URL` constant. -/// Returns `(top_level_items, module_items, registry_paths)` where -/// `top_level_items` contains the struct, its impls, and the custom -/// deserialize; `module_items` contains nested types and oneof enums to be -/// placed in `pub mod `; and `registry_paths` collects qualified paths -/// to the per-message `__*_JSON_ANY` / `__*_TEXT_ANY` consts (relative to -/// the struct's scope) and per-extension `__*_JSON_EXT` / `__*_TEXT_EXT` -/// consts (relative to the `module_items` scope) for `register_types`. +/// Returns `MessageOutput` covering the four streams produced for a +/// single proto message: +/// +/// - `top_level`: struct + impls + custom deserialize, emitted at the +/// parent scope (file-level for top-level messages, owner-module for +/// nested messages). +/// - `mod_items`: nested types (enums, sub-messages, their sub-modules) +/// and nested-extension consts — placed inside `pub mod ` at the +/// parent scope. +/// - `oneof_items`: oneof enum definitions for this message AND its +/// nested-message descendants, pre-structured as the contents of this +/// message's own sub-module inside the parallel `oneofs::` tree. +/// Placed inside `pub mod { ... }` at the oneofs-tree scope. +/// - `registry_paths`: qualified paths to the per-message +/// `__*_JSON_ANY` / `__*_TEXT_ANY` consts (relative to the struct's +/// scope) and per-extension `__*_JSON_EXT` / `__*_TEXT_EXT` consts +/// (relative to `mod_items` scope) for `register_types`. +pub(crate) struct MessageOutput { + pub top_level: TokenStream, + pub mod_items: TokenStream, + /// Contents of this message's own sub-module within the `oneofs::` + /// parallel tree — i.e. direct oneof enum definitions for this + /// message plus recursive `pub mod { ... }` wrappers + /// for each nested-message's oneofs. Empty when this message and + /// all of its descendants declare no oneofs. + pub oneof_items: TokenStream, + pub registry_paths: RegistryPaths, +} + pub fn generate_message( ctx: &CodeGenContext, msg: &DescriptorProto, @@ -64,13 +142,15 @@ pub fn generate_message( proto_fqn: &str, features: &ResolvedFeatures, resolver: &crate::imports::ImportResolver, -) -> Result<(TokenStream, TokenStream, RegistryPaths), CodeGenError> { +) -> Result { let scope = MessageScope { ctx, current_package, proto_fqn, features, nesting: 0, + // `generate_message` emits owned code at package scope. + in_view_tree: false, }; generate_message_with_nesting(scope, msg, rust_name, resolver) } @@ -80,13 +160,14 @@ fn generate_message_with_nesting( msg: &DescriptorProto, rust_name: &str, resolver: &crate::imports::ImportResolver, -) -> Result<(TokenStream, TokenStream, RegistryPaths), CodeGenError> { +) -> Result { let MessageScope { ctx, current_package, proto_fqn, features, nesting, + .. } = scope; let name_ident = format_ident!("{}", rust_name); @@ -172,12 +253,19 @@ fn generate_message_with_nesting( // Compute oneof enum identifiers for all non-synthetic oneofs up front. // Sequential allocation prevents sibling oneofs from claiming the same // suffixed name (see `resolve_oneof_idents`). - let oneof_idents = - crate::oneof::resolve_oneof_idents(msg, proto_fqn, ctx.config.generate_views)?; + let oneof_idents = crate::oneof::resolve_oneof_idents(msg, proto_fqn)?; + + // Path prefix to the owned oneof enums for this message, sitting in + // the parallel `oneofs::` tree at + // `::oneofs::::::Kind`. Used by both the + // struct field types and — via the shared prefix — by the + // `impl_message` / `impl_text` generators that reference the enum + // from within nested-owner scopes. + let oneofs_prefix = oneofs_path_prefix(current_package, proto_fqn, nesting); // One `Option` field in the struct per non-synthetic oneof. - // Oneof enums live inside the message's module, so the type path is - // `mod_name::EnumName`. + // Owned oneof enums live at `pkg::oneofs::::::Kind`, + // reached via `oneofs_prefix`. let oneof_serde_attr = if ctx.config.generate_json { quote! { #[serde(flatten)] } } else { @@ -194,7 +282,7 @@ fn generate_message_with_nesting( let opt = resolver.option(); let tokens = quote! { #oneof_serde_attr - pub #field_ident: #opt<#mod_ident::#enum_ident>, + pub #field_ident: #opt<#oneofs_prefix #enum_ident>, }; Some((tokens, field_ident)) }) @@ -302,9 +390,13 @@ fn generate_message_with_nesting( let needs_custom_deserialize = ctx.config.generate_json && (has_real_oneofs || (has_extension_ranges && ctx.config.preserve_unknown_fields)); - // Oneof enum definitions — emitted inside the message's module. - // Pass the file-level package as current_package, since - // nesting=1 in the oneof codegen handles the module depth. + // Oneof enum definitions. The enums live in the parallel + // `pkg::oneofs::::::Kind` tree, one level + // deeper than the previous layout (which embedded them as + // `pkg::::Kind`). Bumping the nesting argument by ONE + // absorbs that extra hop for variant-type path resolution: + // `generate_oneof_enum` internally adds 1 more to account for the + // enum sitting inside its own `pub mod ` wrapper. let oneof_enums = msg .oneof_decl .iter() @@ -320,7 +412,7 @@ fn generate_message_with_nesting( features, resolver, &oneof_idents, - nesting, + nesting + 1, ) }) .collect::, _>>()?; @@ -426,7 +518,7 @@ fn generate_message_with_nesting( scope, msg, &name_ident, - &mod_ident, + &oneofs_prefix, resolver, has_extension_ranges, &oneof_idents, @@ -491,6 +583,11 @@ fn generate_message_with_nesting( if let Some(id) = &text_any_ident { reg_paths.text_any.push(quote! { #id }); } + // Oneof-tree accumulator for descendants. Nested-message oneof + // sub-modules end up as sibling `pub mod { ... }` + // blocks inside THIS message's own oneof-tree module. + let mut nested_oneof_items = TokenStream::new(); + let non_map_nested: Vec<&DescriptorProto> = msg .nested_type .iter() @@ -501,7 +598,13 @@ fn generate_message_with_nesting( .unwrap_or(false) }) .collect(); - for (nested_desc, (top, mod_items, nested_reg)) in non_map_nested.iter().zip(nested_msgs) { + for (nested_desc, nested_out) in non_map_nested.iter().zip(nested_msgs) { + let MessageOutput { + top_level: top, + mod_items, + oneof_items: nested_oneofs, + registry_paths: nested_reg, + } = nested_out; nested_items.extend(top); let nested_name = nested_desc.name.as_deref().unwrap_or(""); let nested_mod = make_field_ident(&crate::oneof::to_snake_case(nested_name)); @@ -522,34 +625,32 @@ fn generate_message_with_nesting( reg_paths.text_any.push(quote! { #mod_ident :: #p }); } - // Also generate views for nested messages if enabled. - // view_top (struct + impls) goes alongside the owned struct in the - // parent module; view_mod (oneof view enums) goes in the sub-module. - let view_mod_items = if ctx.config.generate_views { - let nested_name = nested_desc.name.as_deref().unwrap_or(""); - let nested_fqn = format!("{}.{}", proto_fqn, nested_name); - let (view_top, view_mod) = crate::view::generate_view_with_nesting( - MessageScope { - proto_fqn: &nested_fqn, - nesting: nesting + 1, - ..scope - }, - nested_desc, - nested_name, - )?; - nested_items.extend(view_top); - view_mod - } else { - quote! {} - }; + // Nested-message views are emitted into the parallel `view::` tree + // by `view::generate_view` — `message.rs` only handles the owned + // tree. Sub-module only needs owned `mod_items` (nested-of-nested + // messages, nested extensions; oneof enums now live in the + // separate oneofs:: tree). - if !mod_items.is_empty() || !view_mod_items.is_empty() { + if !mod_items.is_empty() { nested_items.extend(quote! { pub mod #nested_mod { #[allow(unused_imports)] use super::*; #mod_items - #view_mod_items + } + }); + } + + // Merge the nested message's oneof items into THIS message's + // oneofs sub-module, under the nested's own `pub mod ` + // wrapper. Result is a mirror of the proto nesting in the + // `oneofs::` tree. + if !nested_oneofs.is_empty() { + nested_oneof_items.extend(quote! { + pub mod #nested_mod { + #[allow(unused_imports)] + use super::*; + #nested_oneofs } }); } @@ -578,15 +679,24 @@ fn generate_message_with_nesting( reg_paths.text_ext.push(quote! { #id }); } - // Module items: nested enums, nested message structs + sub-modules, - // and oneof enums. + // Module items for the owned tree: nested enums, nested message + // structs + sub-modules, and nested extensions. Oneof enums now + // live in the parallel `oneofs::` tree — collected separately into + // `oneof_items`. let mod_items = quote! { #(#nested_enums)* #nested_items - #(#oneof_enums)* #nested_extensions }; + // Oneof-tree sub-module contents for THIS message: direct oneof + // enums + recursive nested-message wrappers. Wrapped in + // `pub mod ` by the caller (the oneofs file emitter). + let oneof_items = quote! { + #(#oneof_enums)* + #nested_oneof_items + }; + // Generate a manual Debug impl that excludes internal __buffa_ fields. let struct_name_str = name_ident.to_string(); let debug_field_names: Vec = @@ -645,7 +755,12 @@ fn generate_message_with_nesting( #text_any_const }; - Ok((top_level, mod_items, reg_paths)) + Ok(MessageOutput { + top_level, + mod_items, + oneof_items, + registry_paths: reg_paths, + }) } // ── Custom Deserialize for messages with oneofs ────────────────────────────── @@ -666,7 +781,7 @@ fn generate_custom_deserialize( scope: MessageScope<'_>, msg: &DescriptorProto, name_ident: &proc_macro2::Ident, - mod_ident: &proc_macro2::Ident, + oneofs_prefix: &TokenStream, resolver: &crate::imports::ImportResolver, has_extension_ranges: bool, oneof_idents: &std::collections::HashMap, @@ -703,7 +818,7 @@ fn generate_custom_deserialize( oneof, current_package, proto_fqn, - mod_ident, + oneofs_prefix, features, resolver, oneof_idents, @@ -901,7 +1016,7 @@ fn custom_deser_oneof_group( oneof: &crate::generated::descriptor::OneofDescriptorProto, current_package: &str, proto_fqn: &str, - mod_ident: &proc_macro2::Ident, + oneofs_prefix: &TokenStream, features: &ResolvedFeatures, resolver: &crate::imports::ImportResolver, oneof_idents: &std::collections::HashMap, @@ -920,9 +1035,10 @@ fn custom_deser_oneof_group( let var_ident = format_ident!("__oneof_{}", oneof_name); let field_ident = make_field_ident(oneof_name); - // Oneof enum lives in the message's module. + // Oneof enum lives in the parallel `oneofs::` tree, reached via + // `oneofs_prefix` from the current emission scope. let var_decl = - quote! { let mut #var_ident: ::core::option::Option<#mod_ident::#enum_ident> = None; }; + quote! { let mut #var_ident: ::core::option::Option<#oneofs_prefix #enum_ident> = None; }; let mut arms = Vec::new(); for field in &msg.field { @@ -948,8 +1064,9 @@ fn custom_deser_oneof_group( scalar_or_message_type_nested(ctx, field, current_package, nesting, features, resolver)? }; - // Module-qualified path for the oneof enum (lives in the message's module). - let qualified_enum: TokenStream = quote! { #mod_ident::#enum_ident }; + // Module-qualified path for the owned oneof enum — lives at + // `<…>::oneofs::::::Kind`, reached via `oneofs_prefix`. + let qualified_enum: TokenStream = quote! { #oneofs_prefix #enum_ident }; let arm = crate::oneof::oneof_variant_deser_arm(&crate::oneof::OneofVariantDeserInput { variant_ident: &variant_ident, variant_type: &variant_type, @@ -1033,6 +1150,7 @@ fn classify_field( proto_fqn, features, nesting, + .. } = scope; let label = field.label.unwrap_or_default(); let field_type = crate::impl_message::effective_type(ctx, field, features); @@ -1335,6 +1453,8 @@ pub(crate) fn scalar_or_message_type_nested( proto_fqn: "", features, nesting, + // Shared owned/type resolver — only used for owned-side path work. + in_view_tree: false, }; match crate::impl_message::effective_type(ctx, field, features) { Type::TYPE_MESSAGE | Type::TYPE_GROUP => resolve_message_type(scope, field), diff --git a/buffa-codegen/src/oneof.rs b/buffa-codegen/src/oneof.rs index 35f68db..a74f36d 100644 --- a/buffa-codegen/src/oneof.rs +++ b/buffa-codegen/src/oneof.rs @@ -479,92 +479,41 @@ pub(crate) fn oneof_variant_deser_arm(input: &OneofVariantDeserInput<'_>) -> Tok } } -/// Collect the names already claimed in a message's Rust module that a -/// oneof enum must not collide with: nested message names, nested enum -/// names, and — when view generation is enabled — each nested message's -/// `{name}View` struct (emitted in the same module). -fn reserved_names_for_msg( - msg: &DescriptorProto, - generate_views: bool, -) -> std::collections::HashSet { - let mut reserved = std::collections::HashSet::new(); - for nested in &msg.nested_type { - if let Some(name) = &nested.name { - reserved.insert(name.clone()); - if generate_views { - reserved.insert(format!("{name}View")); - } - } - } - for nested_enum in &msg.enum_type { - if let Some(name) = &nested_enum.name { - reserved.insert(name.clone()); - } - } - reserved -} - -/// Build the Rust identifier for a oneof enum. -/// -/// With module-based nesting the enum lives inside the owning message's -/// module (`pub mod msg_name { pub enum FooOneof { ... } }`), so no -/// message prefix is needed. The enum name is always -/// `{PascalCase(oneof_name)}Oneof` regardless of whether siblings would -/// collide — uniform naming makes the generated type discoverable from -/// the `.proto` alone and prevents source-breaking renames when nested -/// types are added later. +/// Collect the names already claimed in a message's Rust sub-module that a +/// oneof enum must not collide with. /// -/// # Errors +/// Oneof enums live in the owner message's sub-module (`parent::Kind` sits +/// alongside nested message structs at `parent::SomeNested` inside the +/// same `pub mod parent`). The reserved set is therefore just the nested +/// message + nested enum names. /// -/// Returns [`CodeGenError::OneofNameConflict`] when a nested type or a -/// prior oneof in the same message already claims the suffixed name -/// (e.g. a nested message literally named `FooOneof` alongside -/// `oneof foo`). Users resolve these by renaming in the `.proto`. -fn oneof_enum_ident( - oneof_name: &str, - reserved: &std::collections::HashSet, - views_enabled: bool, - scope: &str, -) -> Result { - let pascal = to_pascal_case(oneof_name); - let name = format!("{pascal}Oneof"); - if reserved.contains(&name) || (views_enabled && reserved.contains(&format!("{name}View"))) { - return Err(CodeGenError::OneofNameConflict { - scope: scope.to_string(), - oneof_name: oneof_name.to_string(), - attempted: name, - }); - } - Ok(format_ident!("{}", name)) -} - /// Compute oneof enum identifiers for all non-synthetic oneofs in a message. /// -/// Every oneof enum is named `{PascalCase(oneof_name)}Oneof`; the reserved -/// set is grown after each allocation so two sibling oneofs cannot both -/// claim the same name (which could happen if the user declared e.g. -/// `oneof foo` alongside `oneof foo` — disallowed by protoc — or via a -/// hand-crafted descriptor). +/// Under the "Generated code layout" rule (see DESIGN.md), owned oneof +/// enums live in the parallel `pkg::oneofs::::Kind` tree — a +/// different Rust module from the owner message's own sub-module +/// `pkg::::…`, which holds nested-message structs and nested +/// enums. The two trees are structurally disjoint, so the oneof-vs- +/// nested-type name collisions that the pre-namespace codegen had to +/// guard against are impossible under the new layout. /// -/// `scope` is the parent message's fully-qualified proto name, used only -/// in error diagnostics. `generate_views` must match -/// [`CodeGenContext::config.generate_views`](crate::context::CodeGenContext); -/// when true, nested `{n}View` names are added to the reserved set so the -/// view-side oneof enum (`{Name}OneofView`) also avoids collisions. +/// The one residual collision the check used to cover — two sibling +/// oneofs whose PascalCase'd names collide — is disallowed by `protoc` +/// itself (two `oneof foo` declarations in the same message are a +/// syntax error). The check would only fire on hand-crafted descriptor +/// sets that bypass protoc; we rely on upstream validation and do no +/// additional checking here. +/// +/// `_scope` is retained for API stability (callers already pass the +/// parent message's FQN). It's unused now that no collision diagnostic +/// can fire. /// /// Returns a map from oneof declaration index to its Rust enum `Ident`. /// Synthetic oneofs (proto3 `optional`) are omitted. -/// -/// # Errors -/// -/// Propagates [`CodeGenError::OneofNameConflict`] from -/// [`oneof_enum_ident`]. pub(crate) fn resolve_oneof_idents( msg: &DescriptorProto, - scope: &str, - generate_views: bool, + _scope: &str, ) -> Result, CodeGenError> { - let mut reserved = reserved_names_for_msg(msg, generate_views); let mut result = std::collections::HashMap::new(); for (idx, oneof) in msg.oneof_decl.iter().enumerate() { let has_real_fields = msg.field.iter().any(|f| { @@ -574,12 +523,7 @@ pub(crate) fn resolve_oneof_idents( continue; } if let Some(oneof_name) = &oneof.name { - let ident = oneof_enum_ident(oneof_name, &reserved, generate_views, scope)?; - let owned = ident.to_string(); - if generate_views { - reserved.insert(format!("{owned}View")); - } - reserved.insert(owned); + let ident = format_ident!("{}", to_pascal_case(oneof_name)); result.insert(idx, ident); } } diff --git a/buffa-codegen/src/tests/comments.rs b/buffa-codegen/src/tests/comments.rs index d469c8c..203444c 100644 --- a/buffa-codegen/src/tests/comments.rs +++ b/buffa-codegen/src/tests/comments.rs @@ -154,7 +154,7 @@ fn test_oneof_comment_in_generated_code() { ) .expect("generation should succeed"); - let content = &result[0].content; + let content = all_content(&result); assert!( content.contains("The event payload variant."), "oneof doc comment should appear, got:\n{content}" @@ -204,9 +204,13 @@ fn test_view_gets_same_comment_as_message() { let result = generate(&[file], &["view_comment.proto".to_string()], &config) .expect("generation should succeed"); - let content = &result[0].content; - // The comment should appear on both the owned struct and the view struct - let count = content.matches("A greeter message.").count(); + // The comment should appear on both the owned struct and the view + // struct. Under PR 1 they live in separate sibling files (`.rs` + + // `.__view.rs`), so count across all outputs for this proto. + let count: usize = result + .iter() + .map(|f| f.content.matches("A greeter message.").count()) + .sum(); assert!( count >= 2, "comment should appear on both Greeter and GreeterView, found {count} occurrence(s)" diff --git a/buffa-codegen/src/tests/custom_attributes.rs b/buffa-codegen/src/tests/custom_attributes.rs index 09786ea..cfc6fe7 100644 --- a/buffa-codegen/src/tests/custom_attributes.rs +++ b/buffa-codegen/src/tests/custom_attributes.rs @@ -351,7 +351,7 @@ fn test_type_attribute_reaches_oneof_enum() { vec![], ); let files = generate(&[file], &["oo.proto".to_string()], &config).expect("should generate"); - let content = &files[0].content; + let content = all_content(&files); assert!( content.contains("#[derive(Hash)]"), "type_attribute should reach oneof enum: {content}" @@ -371,7 +371,7 @@ fn test_field_attribute_reaches_oneof_variant() { vec![], ); let files = generate(&[file], &["oo.proto".to_string()], &config).expect("should generate"); - let content = &files[0].content; + let content = all_content(&files); assert!( content.contains("only_a"), "field_attribute should reach oneof variant: {content}" diff --git a/buffa-codegen/src/tests/generation.rs b/buffa-codegen/src/tests/generation.rs index c2b02f7..0b4be73 100644 --- a/buffa-codegen/src/tests/generation.rs +++ b/buffa-codegen/src/tests/generation.rs @@ -55,12 +55,26 @@ fn test_empty_file() { &CodeGenConfig::default(), ); let files = result.expect("empty file should generate without error"); - assert_eq!(files.len(), 1); - assert_eq!(files[0].name, "empty.rs"); - assert!( - files[0].content.contains("@generated by protoc-gen-buffa"), - "missing header comment" - ); + // Five sibling files per proto: owned `.rs`, view `.__view.rs`, + // ext `.__ext.rs`, oneofs `.__oneofs.rs`, view_oneofs + // `.__view_oneofs.rs`. The ancillary siblings are empty-bodied + // when the proto has no relevant content, but they're still + // produced so generate_module_tree can emit stable `include!` + // paths without probing the filesystem. + assert_eq!(files.len(), 5); + let names: Vec<&str> = files.iter().map(|f| f.name.as_str()).collect(); + assert!(names.contains(&"empty.rs")); + assert!(names.contains(&"empty.__view.rs")); + assert!(names.contains(&"empty.__ext.rs")); + assert!(names.contains(&"empty.__oneofs.rs")); + assert!(names.contains(&"empty.__view_oneofs.rs")); + for f in &files { + assert!( + f.content.contains("@generated by protoc-gen-buffa"), + "missing header comment on {}", + f.name + ); + } } #[test] @@ -568,7 +582,7 @@ fn test_message_oneof() { &CodeGenConfig::default(), ) .expect("oneof message should generate"); - let content = &files[0].content; + let content = all_content(&files); assert!( content.contains("pub struct WithOneof"), "missing struct: {content}" @@ -577,10 +591,11 @@ fn test_message_oneof() { content.contains("pub kind:"), "missing oneof field: {content}" ); - assert!( - content.contains("pub mod with_oneof"), - "missing message module: {content}" - ); + // `pub mod with_oneof` is the owner sub-module for nested messages; + // it's now absent because this message has no nested types. The + // oneof enum lives in the `oneofs::` tree, which provides its own + // `pub mod with_oneof` wrapper inside `pub mod oneofs { … }` — we + // only check for the enum itself. assert!( content.contains("pub enum Kind"), "missing oneof enum: {content}" @@ -1212,7 +1227,8 @@ fn editions_delimited_message_encoding() { &CodeGenConfig::default(), ) .expect("generate"); - let content = &files[0].content; + let content = crate::tests::all_content(&files); + let content = &content; // Field 3 (delim_child): inherits DELIMITED → StartGroup/EndGroup. assert!( diff --git a/buffa-codegen/src/tests/json_codegen.rs b/buffa-codegen/src/tests/json_codegen.rs index 0b038c9..e15c24e 100644 --- a/buffa-codegen/src/tests/json_codegen.rs +++ b/buffa-codegen/src/tests/json_codegen.rs @@ -200,7 +200,7 @@ fn test_json_oneof_field_is_flattened() { let files = generate(&[file], &["oneof_json.proto".to_string()], &json_config()) .expect("should generate"); - let content = &files[0].content; + let content = all_content(&files); // The oneof field uses flatten so its variants appear as top-level JSON fields. assert!( content.contains("serde(flatten)"), @@ -426,17 +426,21 @@ fn test_register_types_emitted_with_json_any_only() { }); let files = generate(&[file], &["reg.proto".to_string()], &json_config()).expect("should generate"); - let content = &files[0].content; + let content = all_content(&files); assert!( content.contains("pub fn register_types(reg: &mut ::buffa::type_registry::TypeRegistry)"), "missing register_types fn: {content}" ); + // register_types lives inside `pub mod ext { ... }` at the package + // level (wrapped by generate_module_tree) — inside the generated + // `.__ext.rs` file it references Any consts with `super::` to climb + // out of ext:: to package scope. assert!( - content.contains("reg.register_json_any(__FOO_JSON_ANY)"), + content.contains("reg.register_json_any(super::__FOO_JSON_ANY)"), "missing Foo JSON Any registration: {content}" ); assert!( - content.contains("reg.register_json_any(__BAR_JSON_ANY)"), + content.contains("reg.register_json_any(super::__BAR_JSON_ANY)"), "missing Bar JSON Any registration: {content}" ); // No generate_text → no register_text_* calls in the body. @@ -461,13 +465,14 @@ fn test_register_types_includes_nested_message_any_entries() { }); let files = generate(&[file], &["nested_any.proto".to_string()], &json_config()) .expect("should generate"); - let content = &files[0].content; + let content = all_content(&files); + // From inside `ext::`, package-scope Any consts need a `super::`. assert!( - content.contains("reg.register_json_any(__OUTER_JSON_ANY)"), + content.contains("reg.register_json_any(super::__OUTER_JSON_ANY)"), "missing top-level Outer: {content}" ); assert!( - content.contains("reg.register_json_any(outer::__INNER_JSON_ANY)"), + content.contains("reg.register_json_any(super::outer::__INNER_JSON_ANY)"), "missing nested Inner path: {content}" ); } @@ -511,7 +516,7 @@ fn test_text_any_emitted_independent_of_json() { ..Default::default() }; let files = generate(&[file], &["textonly.proto".to_string()], &cfg).expect("should generate"); - let content = &files[0].content; + let content = all_content(&files); assert!( content.contains("pub const __MSG_TEXT_ANY: ::buffa::type_registry::TextAnyEntry"), "missing TEXT_ANY const: {content}" @@ -525,7 +530,7 @@ fn test_text_any_emitted_independent_of_json() { "JSON_ANY must be absent with generate_json off: {content}" ); assert!( - content.contains("reg.register_text_any(__MSG_TEXT_ANY)"), + content.contains("reg.register_text_any(super::__MSG_TEXT_ANY)"), "missing register_text_any call: {content}" ); assert!( diff --git a/buffa-codegen/src/tests/mod.rs b/buffa-codegen/src/tests/mod.rs index 340cb45..d4ec645 100644 --- a/buffa-codegen/src/tests/mod.rs +++ b/buffa-codegen/src/tests/mod.rs @@ -36,6 +36,14 @@ pub(super) fn make_field(name: &str, number: i32, label: Label, ty: Type) -> Fie } } +/// Concatenate every sibling file's content for search-based assertions. +/// Under PR 1 each proto produces three files (owned, view, ext); tests +/// that look for a generated item need to search across all of them +/// since the content may live in any of the three streams. +pub(super) fn all_content(files: &[crate::GeneratedFile]) -> String { + files.iter().map(|f| f.content.as_str()).collect() +} + mod comments; mod custom_attributes; mod generation; diff --git a/buffa-codegen/src/tests/naming.rs b/buffa-codegen/src/tests/naming.rs index 0724a76..66f40df 100644 --- a/buffa-codegen/src/tests/naming.rs +++ b/buffa-codegen/src/tests/naming.rs @@ -140,10 +140,13 @@ fn test_different_snake_case_names_no_conflict() { } #[test] -fn test_nested_type_oneof_coexists_with_suffix() { - // Nested message "MyField" and oneof "my_field" coexist: the oneof - // enum is always named "MyFieldOneof" under the uniform-suffix rule, - // which happens to be collision-free against the nested struct. +fn test_nested_type_and_oneof_with_same_name_coexist() { + // Under the generated-code layout rule (see DESIGN.md), nested + // messages live in the owner's sub-module (`parent::MyField`) while + // oneof enums live in the parallel `oneofs::` tree + // (`parent::oneofs::parent::MyField`). Different modules — no Rust + // name collision — so a proto with nested `MyField` + `oneof + // my_field` compiles cleanly. let msg = DescriptorProto { name: Some("Parent".to_string()), nested_type: vec![DescriptorProto { @@ -154,7 +157,6 @@ fn test_nested_type_oneof_coexists_with_suffix() { name: Some("my_field".to_string()), ..Default::default() }], - // A real field referencing the oneof so it's not synthetic. field: vec![{ let mut f = make_field("val", 1, Label::LABEL_OPTIONAL, Type::TYPE_STRING); f.oneof_index = Some(0); @@ -166,17 +168,29 @@ fn test_nested_type_oneof_coexists_with_suffix() { file.package = Some("pkg".to_string()); file.message_type = vec![msg]; - let config = CodeGenConfig::default(); - let result = generate(&[file], &["test.proto".to_string()], &config); - let files = result.expect("nested type + oneof name collision should resolve, not error"); - let content = &files[0].content; + let files = generate( + &[file], + &["test.proto".to_string()], + &CodeGenConfig::default(), + ) + .expect("nested struct + same-name oneof live in different trees"); + let oneofs = files + .iter() + .find(|f| f.kind == crate::GeneratedFileKind::Oneofs) + .expect("oneofs stream emitted"); assert!( - content.contains("MyFieldOneof"), - "oneof enum should be suffixed with Oneof: {content}" + oneofs.content.contains("pub enum MyField"), + "oneof enum MyField should land in oneofs tree: {}", + oneofs.content ); + let owned = files + .iter() + .find(|f| f.kind == crate::GeneratedFileKind::Owned) + .expect("owned stream emitted"); assert!( - content.contains("pub struct MyField"), - "nested message struct should keep its original name: {content}" + owned.content.contains("pub struct MyField"), + "nested struct MyField stays in owner sub-module: {}", + owned.content ); } @@ -211,10 +225,12 @@ fn test_nested_type_oneof_no_conflict() { } #[test] -fn test_nested_enum_oneof_coexists_with_suffix() { - // Nested enum "RegionCodes" and oneof "region_codes": the oneof enum - // is always "RegionCodesOneof" under the uniform rule; this is the - // gh#31 motivating example. +fn test_nested_enum_and_oneof_with_same_name_coexist() { + // Nested enum `RegionCodes` in `parent::RegionCodes` + oneof + // `region_codes` in `parent::oneofs::parent::RegionCodes` — two + // different modules under the generated-code layout rule. This + // used to conflict (gh#31 motivating example); the parallel-tree + // design makes it structurally impossible. let msg = DescriptorProto { name: Some("PerkRestrictions".to_string()), enum_type: vec![EnumDescriptorProto { @@ -237,32 +253,44 @@ fn test_nested_enum_oneof_coexists_with_suffix() { file.package = Some("pkg".to_string()); file.message_type = vec![msg]; - let config = CodeGenConfig::default(); - let result = generate(&[file], &["test.proto".to_string()], &config); - let files = result.expect("nested enum + oneof name collision should resolve, not error"); - let content = &files[0].content; + let files = generate( + &[file], + &["test.proto".to_string()], + &CodeGenConfig::default(), + ) + .expect("nested enum + same-name oneof live in different trees"); + let oneofs = files + .iter() + .find(|f| f.kind == crate::GeneratedFileKind::Oneofs) + .expect("oneofs stream emitted"); assert!( - content.contains("RegionCodesOneof"), - "oneof enum should be suffixed with Oneof: {content}" + oneofs.content.contains("pub enum RegionCodes"), + "oneof enum goes in oneofs tree: {}", + oneofs.content ); + let owned = files + .iter() + .find(|f| f.kind == crate::GeneratedFileKind::Owned) + .expect("owned stream emitted"); assert!( - content.contains("pub enum RegionCodes"), - "nested enum should keep its original name: {content}" + owned.content.contains("pub enum RegionCodes"), + "nested enum stays in owner sub-module (owned tree): {}", + owned.content ); } #[test] -fn test_nested_type_oneof_view_uses_suffix() { - // When view generation is on, the view enum uses the uniform - // suffixed name (MyFieldOneofView) alongside its owned counterpart. +fn test_oneof_and_oneof_view_drop_suffix_in_parallel_trees() { + // Owned oneof `Kind` lands in `pkg::oneofs::parent::Kind`; its view + // counterpart lands in `pkg::view::oneofs::parent::Kind` — same + // PascalCase ident in both trees. The `View` suffix is dropped on + // the view-of-oneof enum because the path prefix already + // disambiguates (the documented exception — suffix retention — is + // only for message view STRUCTS, not oneof view enums). let msg = DescriptorProto { name: Some("Parent".to_string()), - nested_type: vec![DescriptorProto { - name: Some("MyField".to_string()), - ..Default::default() - }], oneof_decl: vec![OneofDescriptorProto { - name: Some("my_field".to_string()), + name: Some("kind".to_string()), ..Default::default() }], field: vec![{ @@ -277,21 +305,39 @@ fn test_nested_type_oneof_view_uses_suffix() { file.message_type = vec![msg]; let config = CodeGenConfig::default(); // views enabled by default - let result = generate(&[file], &["test.proto".to_string()], &config); - let files = result.expect("view codegen should handle oneof rename"); - let content = &files[0].content; + let files = generate(&[file], &["test.proto".to_string()], &config) + .expect("owned + view oneof enums should coexist"); + let oneofs = files + .iter() + .find(|f| f.kind == crate::GeneratedFileKind::Oneofs) + .expect("oneofs stream emitted"); + assert!( + oneofs.content.contains("pub enum Kind {"), + "owned oneof enum in oneofs:: tree (no suffix): {}", + oneofs.content + ); + let view_oneofs = files + .iter() + .find(|f| f.kind == crate::GeneratedFileKind::ViewOneofs) + .expect("view_oneofs stream emitted"); assert!( - content.contains("MyFieldOneofView"), - "view enum should use suffixed name: {content}" + view_oneofs.content.contains("pub enum Kind<"), + "view-of-oneof enum in view::oneofs:: tree (no View suffix): {}", + view_oneofs.content ); } #[test] -fn test_oneof_coexists_with_nested_view_struct_name() { - // Nested message `MyFieldView` + oneof `my_field` with views enabled: - // under the uniform-suffix rule the oneof enum is `MyFieldOneof` and - // its view is `MyFieldOneofView`; neither name collides with the - // nested message's view struct (also `MyFieldView`). +fn test_oneof_view_does_not_collide_with_nested_view_struct_name() { + // Nested message `MyFieldView` alongside `oneof my_field`. Names + // produced under the generated-code layout rule: + // - nested msg `MyFieldView` → `parent::MyFieldView` + // - nested view `MyFieldViewView` → `view::parent::MyFieldViewView` + // - oneof owned `MyField` → `oneofs::parent::MyField` + // - oneof view `MyField` → `view::oneofs::parent::MyField` (no View suffix) + // + // All four paths are distinct. The parallel-tree layout makes this + // pattern trivially legal. let msg = DescriptorProto { name: Some("Parent".to_string()), nested_type: vec![DescriptorProto { @@ -313,31 +359,24 @@ fn test_oneof_coexists_with_nested_view_struct_name() { file.package = Some("pkg".to_string()); file.message_type = vec![msg]; - let config = CodeGenConfig::default(); - let files = generate(&[file], &["test.proto".to_string()], &config) - .expect("uniform suffix keeps the oneof clear of nested view name"); - let content = &files[0].content; - assert!( - content.contains("pub enum MyFieldOneof {"), - "owned oneof enum should be MyFieldOneof: {content}" - ); - assert!( - content.contains("pub enum MyFieldOneofView<"), - "view oneof enum should be MyFieldOneofView<'a>: {content}" - ); - // The nested message's own view struct remains unchanged. - assert!( - content.contains("pub struct MyFieldView"), - "nested message view struct must be preserved: {content}" - ); + generate( + &[file], + &["test.proto".to_string()], + &CodeGenConfig::default(), + ) + .expect("parallel-tree namespacing makes this pattern legal"); } #[test] -fn test_sibling_oneof_view_names_do_not_collide() { +fn test_sibling_oneof_with_view_like_name_is_legal() { // Two sibling oneofs `my_field` and `my_field_view`. Under the - // uniform-suffix rule they become `MyFieldOneof`/`MyFieldOneofView` - // and `MyFieldViewOneof`/`MyFieldViewOneofView` — never collide with - // each other or with their own view counterparts. + // parallel-tree layout: + // - `my_field` → owned `oneofs::parent::MyField`, view `view::oneofs::parent::MyField` + // - `my_field_view` → owned `oneofs::parent::MyFieldView`, view `view::oneofs::parent::MyFieldView` + // + // (Oneof view enums drop the `View` suffix — the tree prefix is + // the disambiguator.) All four names are distinct across the two + // trees, so this is legal. let msg = DescriptorProto { name: Some("Parent".to_string()), oneof_decl: vec![ @@ -368,123 +407,18 @@ fn test_sibling_oneof_view_names_do_not_collide() { file.package = Some("pkg".to_string()); file.message_type = vec![msg]; - let files = generate( + generate( &[file], &["test.proto".to_string()], &CodeGenConfig::default(), ) - .expect("sibling oneof names must resolve without collision"); - let content = &files[0].content; - assert!( - content.contains("pub enum MyFieldOneof {"), - "first oneof owned enum should be MyFieldOneof: {content}" - ); - assert!( - content.contains("pub enum MyFieldOneofView<"), - "first oneof view enum should be MyFieldOneofView<'a>: {content}" - ); - assert!( - content.contains("pub enum MyFieldViewOneof {"), - "second oneof owned enum should be MyFieldViewOneof: {content}" - ); - assert!( - content.contains("pub enum MyFieldViewOneofView<"), - "second oneof view enum should be MyFieldViewOneofView<'a>: {content}" - ); -} - -#[test] -fn test_oneof_suffix_conflict_error_includes_scope() { - // Verify the diagnostic carries the parent message's FQN so users can - // locate which message triggered the error in a large descriptor set. - let msg = DescriptorProto { - name: Some("Parent".to_string()), - nested_type: vec![ - DescriptorProto { - name: Some("MyField".to_string()), - ..Default::default() - }, - DescriptorProto { - name: Some("MyFieldOneof".to_string()), - ..Default::default() - }, - ], - oneof_decl: vec![OneofDescriptorProto { - name: Some("my_field".to_string()), - ..Default::default() - }], - field: vec![{ - let mut f = make_field("val", 1, Label::LABEL_OPTIONAL, Type::TYPE_STRING); - f.oneof_index = Some(0); - f - }], - ..Default::default() - }; - let mut file = proto3_file("test.proto"); - file.package = Some("pkg".to_string()); - file.message_type = vec![msg]; - - let err = generate( - &[file], - &["test.proto".to_string()], - &CodeGenConfig::default(), - ) - .expect_err("double collision must error"); - match err { - CodeGenError::OneofNameConflict { - scope, - oneof_name, - attempted, - } => { - assert_eq!(scope, "pkg.Parent"); - assert_eq!(oneof_name, "my_field"); - assert_eq!(attempted, "MyFieldOneof"); - } - other => panic!("expected OneofNameConflict, got {other:?}"), - } -} - -#[test] -fn test_oneof_name_conflict_errors() { - // A nested type literally named "MyFieldOneof" alongside oneof - // "my_field" leaves the uniform-suffix name with nowhere to go — - // users must rename one side in the `.proto`. - let msg = DescriptorProto { - name: Some("Parent".to_string()), - nested_type: vec![DescriptorProto { - name: Some("MyFieldOneof".to_string()), - ..Default::default() - }], - oneof_decl: vec![OneofDescriptorProto { - name: Some("my_field".to_string()), - ..Default::default() - }], - field: vec![{ - let mut f = make_field("val", 1, Label::LABEL_OPTIONAL, Type::TYPE_STRING); - f.oneof_index = Some(0); - f - }], - ..Default::default() - }; - let mut file = proto3_file("test.proto"); - file.package = Some("pkg".to_string()); - file.message_type = vec![msg]; - - let config = CodeGenConfig::default(); - let result = generate(&[file], &["test.proto".to_string()], &config); - assert!(result.is_err()); - let err = result.unwrap_err().to_string(); - assert!( - err.contains("MyFieldOneof"), - "error should mention the attempted name: {err}" - ); + .expect("sibling oneofs with View-suffixed names should be legal"); } #[test] fn test_sibling_oneofs_get_distinct_names() { - // Two oneofs `my_field` and `my_field_oneof` — both want - // `MyFieldOneof` as their Rust name. Sequential allocation must - // assign distinct names, e.g. `MyFieldOneof` and `MyFieldOneofOneof`. + // Two oneofs with distinct PascalCase names — no collision at all + // under PR 1's no-suffix scheme. let msg = DescriptorProto { name: Some("Parent".to_string()), oneof_decl: vec![ @@ -493,7 +427,7 @@ fn test_sibling_oneofs_get_distinct_names() { ..Default::default() }, OneofDescriptorProto { - name: Some("my_field_oneof".to_string()), + name: Some("other_field".to_string()), ..Default::default() }, ], @@ -521,20 +455,23 @@ fn test_sibling_oneofs_get_distinct_names() { }; let result = generate(&[file], &["test.proto".to_string()], &config); let files = result.expect("sibling oneofs should get distinct names"); - let content = &files[0].content; + let content = all_content(&files); assert!( - content.contains("MyFieldOneof"), - "first oneof should be suffixed: {content}" + content.contains("pub enum MyField"), + "first oneof should emit as MyField: {content}" ); assert!( - content.contains("MyFieldOneofOneof"), - "second oneof should be double-suffixed: {content}" + content.contains("pub enum OtherField"), + "second oneof should emit as OtherField: {content}" ); } #[test] -fn test_view_name_conflict_detected() { - // Messages "Foo" and "FooView" — Foo's view type collides with FooView struct. +fn test_top_level_view_no_longer_collides_with_named_view_message() { + // Under PR 1's `view::` namespace the top-level view for message `Foo` + // lives at `pkg::view::FooView`, so a sibling message literally named + // `FooView` at `pkg::FooView` doesn't collide with anything. Its own + // view lands at `pkg::view::FooViewView` — ugly but legal. let mut file = proto3_file("test.proto"); file.package = Some("pkg".to_string()); file.message_type = vec![ @@ -549,37 +486,35 @@ fn test_view_name_conflict_detected() { ]; let config = CodeGenConfig::default(); // views enabled by default - let result = generate(&[file], &["test.proto".to_string()], &config); - assert!(result.is_err()); - let err = result.unwrap_err().to_string(); - assert!(err.contains("Foo"), "should mention owned message: {err}"); + let files = generate(&[file], &["test.proto".to_string()], &config) + .expect("namespace split resolves the old collision"); + let content = all_content(&files); assert!( - err.contains("FooView"), - "should mention view collision: {err}" + content.contains("pub struct Foo "), + "owned Foo should still be generated: {content}" + ); + assert!( + content.contains("pub struct FooView "), + "owned FooView message should still be generated at package scope: {content}" + ); + // Under PR 1 the view tree is stitched by `generate_module_tree`, + // not wrapped per-file. Per-proto outputs are three siblings: an + // owned `.rs`, a `.__view.rs`, and a `.__ext.rs` — verify the + // view-tree sibling exists and carries the view structs. + let view_file = files + .iter() + .find(|f| f.name == "test.__view.rs") + .expect("expected __view.rs output"); + assert!( + view_file.content.contains("pub struct FooView"), + "view struct FooView<'a> should land in the view-tree file: {}", + view_file.content + ); + assert!( + view_file.content.contains("pub struct FooViewView"), + "FooView (owned) also gets a view: `FooViewView<'a>`: {}", + view_file.content ); -} - -#[test] -fn test_view_name_conflict_not_checked_when_views_disabled() { - let mut file = proto3_file("test.proto"); - file.package = Some("pkg".to_string()); - file.message_type = vec![ - DescriptorProto { - name: Some("Foo".to_string()), - ..Default::default() - }, - DescriptorProto { - name: Some("FooView".to_string()), - ..Default::default() - }, - ]; - - let config = CodeGenConfig { - generate_views: false, - ..Default::default() - }; - let result = generate(&[file], &["test.proto".to_string()], &config); - assert!(result.is_ok(), "no conflict when views are disabled"); } #[test] @@ -937,7 +872,7 @@ fn test_oneof_variant_named_self_escapes_to_self_underscore() { &CodeGenConfig::default(), ) .expect("oneof with `self` variant must compile"); - let content = &files[0].content; + let content = all_content(&files); // The reserved `Self` is suffixed to `Self_` by `make_field_ident`; // the bare `Manager` variant is unaffected. assert!( diff --git a/buffa-codegen/src/tests/view_codegen.rs b/buffa-codegen/src/tests/view_codegen.rs index d785dc1..1886e8d 100644 --- a/buffa-codegen/src/tests/view_codegen.rs +++ b/buffa-codegen/src/tests/view_codegen.rs @@ -33,7 +33,7 @@ fn test_view_explicit_presence_scalar_is_option() { &CodeGenConfig::default(), ) .expect("should generate"); - let content = &files[0].content; + let content = crate::tests::all_content(&files); // View struct field should be Option. assert!( content.contains("pub value: Option"), @@ -78,7 +78,7 @@ fn test_view_repeated_message_field() { &CodeGenConfig::default(), ) .expect("should generate"); - let content = &files[0].content; + let content = crate::tests::all_content(&files); // Both Item and Container views should be generated. assert!( content.contains("pub struct ItemView"), @@ -146,20 +146,29 @@ fn test_view_oneof_with_message_variant() { &CodeGenConfig::default(), ) .expect("should generate"); - let content = &files[0].content; - // View struct must have an optional PayloadOneofView field. + let content = crate::tests::all_content(&files); + // View struct `RequestView` (inside `view::`) has `payload` field + // pointing at its view-of-oneof enum `Payload` (no View suffix — + // the `view::oneofs::` tree prefix disambiguates). From inside + // `view::RequestView` (depth 1) the enum lives at + // `view::oneofs::request::Payload`, reached as `oneofs::request::Payload`. assert!( - content.contains("pub payload: ::core::option::Option: {content}" + content.contains("pub payload: ::core::option::Option: {content}" ); - // The oneof view enum must have both variants. + // The view-of-oneof enum (inside `view::oneofs::request`) has both + // variants. Its message-typed `Body` variant references sibling + // view `BodyView<'a>` in the sibling `view::` tree — from inside + // `view::oneofs::request` (depth 3), the path is + // `super::super::super::view::BodyView` via the standard view-path + // resolver. assert!( content.contains("Count(i32)"), - "PayloadView must have Count(i32): {content}" + "Payload enum must have Count(i32): {content}" ); assert!( - content.contains("Body(::buffa::alloc::boxed::Box`: a struct whose string/bytes fields are `&'a str`/`&'a [u8]`, //! borrowing directly from the input buffer without allocation. -//! - A `FooKindView<'a>` enum for each oneof, mirroring the owned `FooKind` -//! but with borrowed variants. +//! - A borrowed oneof enum at `view::oneofs::foo::Kind<'a>` for each oneof, +//! mirroring the owned `oneofs::foo::Kind` but with borrowed variants. //! - `impl MessageView<'a> for FooView<'a>`: provides `decode_view` (zero-copy //! decode) and `to_owned_message` (conversion to the owned type). @@ -64,8 +64,52 @@ fn bytes_to_owned( } } -/// Generate view types for a message and all non-map nested messages. -/// Returns `(top_level_items, module_items)` matching `generate_message`. +/// Generate view items for a message and (recursively) all of its nested +/// messages, as a single `TokenStream`. The stream has the shape: +/// +/// ```ignore +/// pub struct FooView<'a> { … } // struct + decode/to_owned impls +/// impl<'a> FooView<'a> { … } +/// impl<'a> MessageView<'a> for FooView<'a> { … } +/// pub mod foo { // only if Foo has nested message types +/// use super::*; +/// pub struct InnerView<'a> { … } // nested-view (recursive) +/// pub mod inner { … } +/// } +/// ``` +/// +/// Oneof view enums for `Foo`'s oneofs live in a separate stream destined +/// for `view::oneofs::foo::Kind<'a>` — see `ViewOutput::oneof_items`. +/// +/// The caller wraps the outer stream in `pub mod view { … }` at the +/// package level and includes the contents from every file in the same +/// package — so `views::foo::InnerView` ends up at `my_pkg::view::foo:: +/// InnerView<'a>`. +/// +/// Nesting: `scope.nesting` counts module hops from package root to the +/// CURRENT point in the view tree. Top-level view items live at depth 1 +/// (inside `pub mod view`). Nested views of `Foo` live at depth 2 +/// (inside `view::foo`). Owned-type references climb out via that many +/// `super::` segments. +/// View generation result for a single proto message. +/// +/// The content is split across two streams because the view `items` and +/// view-of-oneof `oneof_items` land in different trees +/// (`pkg::view::...` vs `pkg::view::oneofs::...`) — the latter is a +/// modifier of the `oneofs::` kind, stitched separately by +/// `generate_module_tree`. +pub struct ViewOutput { + /// View struct + nested sub-module (for nested-message views). + /// Destined for `pub mod view { ... }` at the package level. + pub items: TokenStream, + /// Contents of THIS message's sub-module within the view-of-oneofs + /// tree: direct view-of-oneof enums + recursive `pub mod ` + /// wrappers. Destined for `pub mod view { pub mod oneofs { ... } }` + /// at the package level, wrapped in `pub mod ` by the + /// caller (`generate_file`). + pub oneof_items: TokenStream, +} + pub fn generate_view( ctx: &CodeGenContext, msg: &DescriptorProto, @@ -73,22 +117,25 @@ pub fn generate_view( rust_name: &str, proto_fqn: &str, features: &ResolvedFeatures, -) -> Result<(TokenStream, TokenStream), CodeGenError> { +) -> Result { let scope = MessageScope { ctx, current_package, proto_fqn, features, - nesting: 0, + // One `super::` hop to escape `pub mod view { ... }`. + nesting: 1, + in_view_tree: true, }; - generate_view_with_nesting(scope, msg, rust_name) + generate_view_items(scope, msg, rust_name) } -pub(crate) fn generate_view_with_nesting( +/// Recursive helper shared by top-level and nested view generation. +fn generate_view_items( scope: MessageScope<'_>, msg: &DescriptorProto, rust_name: &str, -) -> Result<(TokenStream, TokenStream), CodeGenError> { +) -> Result { let MessageScope { ctx, proto_fqn, @@ -97,16 +144,101 @@ pub(crate) fn generate_view_with_nesting( } = scope; let proto_name = msg.name.as_deref().unwrap_or(rust_name); let mod_name_str = crate::oneof::to_snake_case(proto_name); - let mod_ident = make_field_ident(&mod_name_str); - - // Note: nested views are generated by generate_message, not here. - // This function only generates the view for the current message. - - let oneof_idents = - crate::oneof::resolve_oneof_idents(msg, proto_fqn, ctx.config.generate_views)?; - + let mod_ident_raw = make_field_ident(&mod_name_str); + + // Sibling owned sub-module path. Used by `build_to_owned_fields` to + // reach this message's OWNED oneof enum (`Kind`) when materializing + // an owned message from a view. Resolves to the FULL owned module + // path (package-relative, then prefixed with `super::`s to climb + // out of the view tree). Computed below alongside `owned_ident`. + // Build `super::super::…::` token stream for `scope.nesting` hops. + // `syn::parse_str` handles a trailing `::` fine because it parses as + // an unconstrained TokenStream, not a full path. + let supers = "super::".repeat(scope.nesting); + let supers_tokens: TokenStream = syn::parse_str(&supers).unwrap_or_default(); + + let oneof_idents = crate::oneof::resolve_oneof_idents(msg, proto_fqn)?; + + // View struct name always keeps the `View` suffix (for co-import + // ergonomics — callers commonly bind both `Foo` and `FooView` in the + // same scope). let view_ident = format_ident!("{}View", rust_name); - let owned_ident = format_ident!("{}", rust_name); + // Owned counterpart: lives in the owned tree at the mirrored + // position. We resolve the full owned path (including any nested- + // module prefix like `outer::`) via the context's type map, then + // prefix with the right number of `super::` hops for the current + // view-tree nesting. + let proto_fqn_dotted = format!(".{proto_fqn}"); + let owned_path_from_pkg = ctx + .rust_type(&proto_fqn_dotted) + .and_then(|full| { + // Strip the package prefix (e.g. `google::protobuf::outer::Middle` + // → `outer::Middle`, or `google::protobuf::Any` → `Any`) so + // we can re-prefix with `super::`s below. The package prefix + // is `current_package` translated to `::` form. + let pkg_rust = scope.current_package.replace('.', "::"); + if pkg_rust.is_empty() { + Some(full.to_string()) + } else { + full.strip_prefix(&format!("{pkg_rust}::")) + .map(|s| s.to_string()) + } + }) + .unwrap_or_else(|| rust_name.to_string()); + // Use `rust_path_to_tokens` rather than `syn::parse_str`: the latter + // chokes on keyword path segments like `google::r#type::LatLng` + // (protoc allows `type` as a package name). The helper already + // produces properly-escaped idents for each path segment. + let owned_path_tokens: TokenStream = crate::idents::rust_path_to_tokens(&owned_path_from_pkg); + let owned_ident: TokenStream = quote! { #supers_tokens #owned_path_tokens }; + + // Owned sub-module path (mirror of `owned_path_from_pkg` with the + // final Type segment replaced by its snake_case). For a top-level + // `Foo` this is `foo`; for a nested `Outer.Middle` it's + // `outer::middle`; etc. + // + // Parent prefix comes straight from `owned_path_from_pkg` by splitting + // off the last segment; we then append the current message's own + // snake-cased module name. + let owned_mod_suffix = match owned_path_from_pkg.rsplit_once("::") { + Some((parent, _leaf)) => format!("{parent}::{mod_name_str}"), + None => mod_name_str.clone(), + }; + let owned_mod_tokens: TokenStream = crate::idents::rust_path_to_tokens(&owned_mod_suffix); + + // Path prefix to this message's view-of-oneof sub-module within + // the parallel `view::oneofs::` tree, relative to the current + // view-struct emission scope. For a view struct at `view::` (depth + // 1) targeting `view::oneofs::foo::Kind`: `oneofs::foo::` (sibling + // in our `view::` module). For a nested view struct at + // `view::foo::InnerView` (depth 2) targeting + // `view::oneofs::foo::inner::Kind`: `super::oneofs::foo::inner::` + // (climb out to `view::`, then descend). + // + // Formally: (scope.nesting - 1) `super::`s + `oneofs::` + owned + // module path chain (same as owned_mod_suffix). + let view_oneofs_supers = if scope.nesting > 1 { + "super::".repeat(scope.nesting - 1) + } else { + String::new() + }; + let view_oneofs_supers_tokens: TokenStream = + syn::parse_str(&view_oneofs_supers).unwrap_or_default(); + let view_oneofs_prefix: TokenStream = quote! { + #view_oneofs_supers_tokens oneofs:: #owned_mod_tokens :: + }; + + // Path prefix to this message's OWNED oneof sub-module within the + // parallel `oneofs::` tree, reachable from the current view-struct + // scope. From view:: at depth N, climb N supers to the package, + // then descend into `oneofs::::::` — N is + // `scope.nesting`. + // + // Used by `build_to_owned_fields` to reach the OWNED `Kind` enum + // when materializing an owned message from a view. + let owned_oneofs_prefix: TokenStream = quote! { + #supers_tokens oneofs:: #owned_mod_tokens :: + }; // View struct fields (excludes real-oneof members, map fields, and // unsupported types like groups). @@ -120,24 +252,48 @@ pub(crate) fn generate_view_with_nesting( .flatten() .collect::>(); - // One `Option>` per non-synthetic oneof (module-qualified). + // One `Option>` (no `View` suffix — tree disambiguates) + // per non-synthetic oneof, path-qualified to `view::oneofs::<…>`. let oneof_struct_fields = - oneof_view_struct_fields(ctx, msg, &mod_ident, features, &oneof_idents)?; - - // Oneof view enum definitions (go inside the module). + oneof_view_struct_fields(ctx, msg, &view_oneofs_prefix, features, &oneof_idents)?; + + // Oneof view enum definitions. They live in the parallel + // `view::oneofs::::::` tree — one extra + // level deeper than the corresponding owned oneof enum. Relative + // to the view struct emission scope (`scope.nesting`), the enum + // sits two modules deeper: view::oneofs itself plus the self_mod + // wrapper. Pass `scope` + 2 so variant-type path resolution inside + // the enum produces the right number of `super::` hops for owned + // and sibling-view targets. + let enum_scope = MessageScope { + nesting: scope.nesting + 2, + ..scope + }; let oneof_view_enums = msg .oneof_decl .iter() .enumerate() - .map(|(idx, oneof)| generate_oneof_view_enum(scope, msg, idx, oneof, &oneof_idents)) + .map(|(idx, oneof)| generate_oneof_view_enum(enum_scope, msg, idx, oneof, &oneof_idents)) .collect::, _>>()?; - // decode_view match arms. + // decode_view match arms. Oneof arms assign to view-of-oneof enum + // variants living at `view::oneofs::<…>`, reached via + // `view_oneofs_prefix`. let (scalar_arms, repeated_arms, oneof_arms) = - build_decode_arms(scope, msg, &mod_ident, &oneof_idents)?; - - // to_owned_message field initialisers. - let owned_fields = build_to_owned_fields(scope, msg, &mod_ident, &oneof_idents)?; + build_decode_arms(scope, msg, &view_oneofs_prefix, &oneof_idents)?; + + // to_owned_message field initialisers. Oneof variants reference + // the view-of-oneof enum (for pattern matching) at + // `view::oneofs::::Kind` and the owned oneof enum (for + // construction) at `oneofs::::Kind`, reached via the two + // prefixes computed above. + let owned_fields = build_to_owned_fields( + scope, + msg, + &view_oneofs_prefix, + &owned_oneofs_prefix, + &oneof_idents, + )?; let unknown_fields_field = if ctx.config.preserve_unknown_fields { quote! { pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, } @@ -172,9 +328,11 @@ pub(crate) fn generate_view_with_nesting( quote! { #[doc(hidden)] pub __buffa_phantom: ::core::marker::PhantomData<&'a ()>, } }; - let mod_items = quote! { - #(#oneof_view_enums)* - }; + // Sub-module for this message's nested-view items. Oneof-view + // enums are extracted into the separate oneofs:: tree, so only + // recursive nested-view contents end up here (collected in + // `nested_view_items` below). + let mod_items = quote! {}; let view_doc = crate::comments::doc_attrs(ctx.comment(proto_fqn)); @@ -297,7 +455,78 @@ pub(crate) fn generate_view_with_nesting( } }; - Ok((top_level, mod_items)) + // Recurse into nested messages — their view items live inside the + // owner's sub-module within the view tree (`view::foo::InnerView`), + // and their oneof-view items feed into THIS message's oneof_items + // under a nested sub-module. Skip synthetic map-entry messages. + let mut nested_view_items = TokenStream::new(); + let mut nested_oneof_view_items = TokenStream::new(); + for nested in &msg.nested_type { + let is_map_entry = nested + .options + .as_option() + .and_then(|o| o.map_entry) + .unwrap_or(false); + if is_map_entry { + continue; + } + let nested_name = nested.name.as_deref().unwrap_or(""); + let nested_fqn = format!("{}.{}", proto_fqn, nested_name); + let nested_features = + crate::features::resolve_child(features, crate::features::message_features(nested)); + let nested_scope = MessageScope { + proto_fqn: &nested_fqn, + features: &nested_features, + nesting: scope.nesting + 1, + ..scope + }; + let ViewOutput { + items: nested_items, + oneof_items: nested_oneofs, + } = generate_view_items(nested_scope, nested, nested_name)?; + nested_view_items.extend(nested_items); + if !nested_oneofs.is_empty() { + let nested_mod_ident = make_field_ident(&crate::oneof::to_snake_case(nested_name)); + nested_oneof_view_items.extend(quote! { + pub mod #nested_mod_ident { + #[allow(unused_imports)] + use super::*; + #nested_oneofs + } + }); + } + } + + // Combine recursive nested-view items into a single sub-module + // (`view::foo::...`). Oneof-view enums moved to the separate + // oneof_items stream, so this sub-module holds only nested-view + // content; emit only when non-empty. + let has_sub_items = !mod_items.is_empty() || !nested_view_items.is_empty(); + let sub_module = if has_sub_items { + quote! { + pub mod #mod_ident_raw { + #[allow(unused_imports)] + use super::*; + #mod_items + #nested_view_items + } + } + } else { + TokenStream::new() + }; + + // Assemble this message's view-of-oneofs sub-module contents: + // direct oneof-view enums plus the recursive nested-message + // wrappers collected above. + let oneof_items = quote! { + #(#oneof_view_enums)* + #nested_oneof_view_items + }; + + Ok(ViewOutput { + items: quote! { #top_level #sub_module }, + oneof_items, + }) } // --------------------------------------------------------------------------- @@ -557,10 +786,11 @@ fn message_view_has_borrowing_field( false } +#[allow(clippy::too_many_arguments)] fn oneof_view_struct_fields( ctx: &CodeGenContext, msg: &DescriptorProto, - mod_ident: &proc_macro2::Ident, + view_oneofs_prefix: &TokenStream, features: &ResolvedFeatures, oneof_idents: &std::collections::HashMap, ) -> Result, CodeGenError> { @@ -583,14 +813,16 @@ fn oneof_view_struct_fields( .as_deref() .ok_or(CodeGenError::MissingField("oneof.name"))?; let field_ident = make_field_ident(oneof_name); - let view_enum = format_ident!("{}View", base_ident); + // View-of-oneof enum drops the `View` suffix — the + // `view::oneofs::` path prefix is the disambiguator. + let enum_ident = base_ident.clone(); let generics = if oneof_view_needs_lifetime(ctx, &fields, features) { quote! { <'a> } } else { quote! {} }; out.push(quote! { - pub #field_ident: ::core::option::Option<#mod_ident::#view_enum #generics>, + pub #field_ident: ::core::option::Option<#view_oneofs_prefix #enum_ident #generics>, }); } Ok(out) @@ -623,7 +855,10 @@ fn generate_oneof_view_enum( return Ok(TokenStream::new()); } - let view_enum = format_ident!("{}View", base_ident); + // View-of-oneof enum drops the `View` suffix — the + // `view::oneofs::` path prefix is the disambiguator (see + // DESIGN.md → "Generated code layout"). + let view_enum = base_ident.clone(); let variants = fields .iter() @@ -639,11 +874,15 @@ fn generate_oneof_view_enum( Type::TYPE_STRING => quote! { &'a str }, Type::TYPE_BYTES => quote! { &'a [u8] }, Type::TYPE_MESSAGE | Type::TYPE_GROUP => { - let view_ty = resolve_view_ty_tokens(scope.deeper(), f)?; + // Oneof view enum lives in the view-oneofs tree + // (`view::oneofs::foo::Kind<'a>`). The caller already + // passed a `scope.deeper()` to reflect that depth, so + // standard view-path resolution applies. + let view_ty = resolve_view_ty_tokens(scope, f)?; quote! { ::buffa::alloc::boxed::Box<#view_ty> } } Type::TYPE_ENUM => { - let et = resolve_enum_ty(scope.deeper(), f)?; + let et = resolve_enum_ty(scope, f)?; if is_closed_enum(&f_features) { quote! { #et } } else { @@ -678,7 +917,7 @@ fn generate_oneof_view_enum( fn build_decode_arms( scope: MessageScope<'_>, msg: &DescriptorProto, - mod_ident: &proc_macro2::Ident, + mod_ident: &TokenStream, oneof_idents: &std::collections::HashMap, ) -> Result<(Vec, Vec, Vec), CodeGenError> { let scalar_fields: Vec<_> = msg @@ -1101,13 +1340,16 @@ fn oneof_decode_arms( base_ident: &proc_macro2::Ident, oneof_name: &str, fields: &[&FieldDescriptorProto], - mod_ident: &proc_macro2::Ident, + mod_ident: &TokenStream, ) -> Result, CodeGenError> { let MessageScope { ctx, features, .. } = scope; let preserve_unknown_fields = ctx.config.preserve_unknown_fields; let field_ident = make_field_ident(oneof_name); - let view_enum_simple = format_ident!("{}View", base_ident); - let view_enum: TokenStream = quote! { #mod_ident::#view_enum_simple }; + // View-of-oneof enum drops the `View` suffix. `mod_ident` here is + // the `view::oneofs::::` path prefix passed down from + // `generate_view_items::view_oneofs_prefix`. + let view_enum_simple = base_ident.clone(); + let view_enum: TokenStream = quote! { #mod_ident #view_enum_simple }; fields .iter() @@ -1214,7 +1456,8 @@ fn oneof_decode_arms( fn build_to_owned_fields( scope: MessageScope<'_>, msg: &DescriptorProto, - mod_ident: &proc_macro2::Ident, + view_oneofs_prefix: &TokenStream, + owned_mod_ident: &TokenStream, oneof_idents: &std::collections::HashMap, ) -> Result, CodeGenError> { let MessageScope { ctx, features, .. } = scope; @@ -1265,9 +1508,14 @@ fn build_to_owned_fields( continue; } let field_ident = make_field_ident(oneof_name); - let view_enum_simple = format_ident!("{}View", base_ident); - let view_enum: TokenStream = quote! { #mod_ident::#view_enum_simple }; - let owned_enum: TokenStream = quote! { #mod_ident::#base_ident }; + // View-of-oneof enum: lives at `view::oneofs::::Kind`, + // reached via `view_oneofs_prefix` (no `View` suffix — the + // parallel tree disambiguates). + let view_enum: TokenStream = quote! { #view_oneofs_prefix #base_ident }; + // Owned-side oneof enum lives at `oneofs::::Kind`, + // reached via `owned_mod_ident` (which is now the oneofs-tree + // path: climb out of view::, descend into oneofs::). + let owned_enum: TokenStream = quote! { #owned_mod_ident #base_ident }; let match_arms = group .iter() @@ -1450,13 +1698,26 @@ fn resolve_enum_ty( } /// Resolve the view type tokens for a message field -/// (e.g. `"Address"` → `AddressView<'a>`). +/// Resolve the view type tokens for a message field. +/// +/// Branches on `scope.in_view_tree`: +/// - `true` (top-level view inside `pub mod view { ... }`): same-package +/// view refs are siblings; cross-package refs get a `view::` segment +/// inserted. +/// - `false` (nested view inside an owner's message sub-module, or +/// oneof-view-enum in owner's module): paths keep the legacy +/// `View` suffix in the owner's module. fn resolve_view_ty_tokens( scope: MessageScope<'_>, field: &FieldDescriptorProto, ) -> Result { let owned = resolve_owned_path(scope, field)?; - Ok(owned_to_view_ty_tokens(&owned)) + let target_same_package = proto_type_same_package(field, scope); + Ok(rewrite_to_view_path( + &owned, + target_same_package, + /* with_lifetime */ true, + )) } /// Resolve the view type tokens used for `decode_view` calls @@ -1466,7 +1727,32 @@ fn resolve_view_decode_tokens( field: &FieldDescriptorProto, ) -> Result { let owned = resolve_owned_path(scope, field)?; - Ok(owned_to_view_decode_tokens(&owned)) + let target_same_package = proto_type_same_package(field, scope); + Ok(rewrite_to_view_path( + &owned, + target_same_package, + /* with_lifetime */ false, + )) +} + +/// Determine if the target message referenced by `field.type_name` lives +/// in the SAME proto package as the scope emitting the reference. +/// +/// Cross-package refs (including extern crates) need a `view::` segment +/// injected before the final ident in the generated view path, to +/// re-enter the target's package view tree. Same-package refs resolve +/// to a mirrored position inside our own view tree and don't need the +/// extra hop. +fn proto_type_same_package(field: &FieldDescriptorProto, scope: MessageScope<'_>) -> bool { + let Some(type_name) = field.type_name.as_deref() else { + return false; + }; + match scope.ctx.package_of(type_name) { + Some(pkg) => pkg == scope.current_package, + // Extern types (not in our descriptor set) are by definition + // not in our current package. + None => false, + } } fn resolve_owned_path( @@ -1483,22 +1769,92 @@ fn resolve_owned_path( .ok_or_else(|| CodeGenError::Other(format!("message type '{type_name}' not found"))) } -/// Convert an owned type path string to view type tokens with a lifetime. +/// Rewrite an owned-type path string to the corresponding view-type path. /// -/// `"Address"` → `AddressView<'a>`, `"pkg::Foo"` → `pkg::FooView<'a>`. -fn owned_to_view_ty_tokens(owned: &str) -> TokenStream { - let (prefix_tokens, last_name) = split_path_last(owned); - let view_ident = make_field_ident(&format!("{last_name}View")); - quote! { #prefix_tokens #view_ident<'a> } -} - -/// Convert an owned type path string to view decode path tokens (no lifetime). +/// **Key insight**: the view tree mirrors the owned tree exactly. A +/// top-level `Foo` at `my_pkg::Foo` has its view at `my_pkg::view::Foo` +/// (sibling module path), and a nested `Outer.Inner` at +/// `my_pkg::outer::Inner` has its view at `my_pkg::view::outer::Inner`. +/// The spine (package path + nested-module path) is identical in both +/// trees. +/// +/// Because the spines match, `rust_type_relative` — which computes the +/// view-tree-internal path from the current scope's nesting — already +/// produces exactly the owned-side path structure we want, modulo the +/// final ident. We just append `View` to the last segment and append +/// the lifetime param. +/// +/// **Extern targets** (other crates, `::crate::…` or `crate::…`) follow +/// the same rule: the sibling crate's `view::` module mirrors its owned +/// tree, so the suffix swap suffices. Suppose buffa-types puts its WKT +/// views at `::buffa_types::google::protobuf::view::TimestampView`; +/// that path is what `resolve_owned_path` would produce for the owned +/// `Timestamp` plus the suffix swap. +/// +/// **Actually**: extern crates may not yet have moved their views into +/// a `view::` sub-module — `buffa-types` is the canonical example, and +/// its `lib.rs` now does the module stitching. Nested-target handling +/// is identical for same-crate and extern. /// -/// `"Address"` → `AddressView`, `"pkg::Foo"` → `pkg::FooView`. -fn owned_to_view_decode_tokens(owned: &str) -> TokenStream { +/// `target_is_nested` is plumbed through for future use (view-tree +/// introspection) but the current implementation doesn't branch on it. +fn rewrite_to_view_path( + owned: &str, + target_same_package: bool, + with_lifetime: bool, +) -> TokenStream { let (prefix_tokens, last_name) = split_path_last(owned); let view_ident = make_field_ident(&format!("{last_name}View")); - quote! { #prefix_tokens #view_ident } + let lifetime = if with_lifetime { + quote! { <'a> } + } else { + quote! {} + }; + + // The view tree mirrors the owned tree inside each package. Rewrite + // rules: + // + // owned `super::Foo` → `Foo<'a>` + // owned `super::super::Foo` → `super::FooView<'a>` + // owned `super::outer::Inner` → `outer::InnerView<'a>` + // owned `super::super::outer::Inner` → `super::outer::InnerView<'a>` + // owned `super::super::pkg_b::Foo` → `super::pkg_b::view::FooView<'a>` + // owned `super::super::pkg_b::outer::Inner` → `super::pkg_b::view::outer::InnerView<'a>` + // owned `::buffa_types::google::protobuf::Timestamp` + // → `::buffa_types::google::protobuf::view::TimestampView<'a>` + // + // Algorithm: + // + // SAME-PACKAGE target (our own view tree mirrors the owned tree): + // Strip one leading `super::` (the hop that escapes our view:: + // to the package root, which we don't need when the target is + // in our own view tree). Keep the rest, swap the final ident. + // owned `super::Foo` → `Foo<'a>` + // owned `super::super::Foo` → `super::FooView<'a>` + // owned `super::outer::Inner` → `outer::InnerView<'a>` + // owned `super::attribute::Peer` → `attribute::PeerView<'a>` + // + // CROSS-PACKAGE target (same crate, different proto package): + // Keep the owned prefix verbatim (the climb reaches out through + // however many packages), then inject `view::` before the final + // ident to re-enter the target package's view tree. + // owned `super::super::super::pkg_b::Foo` + // → `super::super::super::pkg_b::view::FooView<'a>` + // + // EXTERN target (`::crate::…`): + // Same rewrite as cross-package: preserve prefix, inject `view::` + // before the final ident. Matches `buffa-types`'s layout. + // owned `::buffa_types::google::protobuf::Timestamp` + // → `::buffa_types::google::protobuf::view::TimestampView<'a>` + if target_same_package { + let stripped = owned.strip_prefix("super::").unwrap_or(owned); + let (remainder_prefix, _) = split_path_last(stripped); + return quote! { #remainder_prefix #view_ident #lifetime }; + } + + // Cross-package / extern: keep the full owned prefix, inject + // `view::` before the final ident. + quote! { #prefix_tokens view:: #view_ident #lifetime } } /// Split `"a::b::Foo"` into (tokens for `a::b::`, `"Foo"`). diff --git a/buffa-codegen/tests/codegen_integration.rs b/buffa-codegen/tests/codegen_integration.rs index 33b173f..eb5a2c1 100644 --- a/buffa-codegen/tests/codegen_integration.rs +++ b/buffa-codegen/tests/codegen_integration.rs @@ -88,12 +88,14 @@ fn generate_proto(proto: &str, config: &CodeGenConfig) -> String { let files = buffa_codegen::generate(&fds.file, &["test.proto".into()], config) .unwrap_or_else(|e| panic!("codegen failed: {e}\n\nProto:\n{proto}")); - assert_eq!( - files.len(), - 1, - "generate_proto expects single-file output; use compile_protos directly for multi-file" - ); - files.into_iter().next().unwrap().content + // Each proto produces up to three sibling files (owned, view, ext). + // Inline tests concatenate all of them so string-search assertions + // find items regardless of which stream emits them. + files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n") } /// Helper: run codegen on a proto file from buffa-test/protos/. @@ -124,15 +126,20 @@ fn json_no_views() -> CodeGenConfig { fn codegen_basic() { let files = generate_for("basic.proto", &CodeGenConfig::default()); assert!(!files.is_empty()); - assert!(files[0].content.contains("@generated")); + assert!(files.iter().any(|f| f.content.contains("@generated"))); } #[test] fn codegen_basic_views_vs_no_views() { + // With views the `.__view.rs` sibling carries the view tree; without + // views it's empty. Compare the total content size across siblings. + fn total_len(files: &[GeneratedFile]) -> usize { + files.iter().map(|f| f.content.len()).sum() + } let with_views = generate_for("basic.proto", &CodeGenConfig::default()); let without_views = generate_for("basic.proto", &no_views()); assert!( - without_views[0].content.len() < with_views[0].content.len(), + total_len(&without_views) < total_len(&with_views), "disabling views should produce shorter output" ); } @@ -140,7 +147,12 @@ fn codegen_basic_views_vs_no_views() { #[test] fn codegen_keywords() { let files = generate_for("keywords.proto", &no_views()); - let content = &files[0].content; + let content = files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n"); + let content = &content; assert!(content.contains("pub mod r#type")); assert!(content.contains("self_:")); assert!(content.contains("r#type:")); @@ -149,7 +161,12 @@ fn codegen_keywords() { #[test] fn codegen_proto2_defaults() { let files = generate_for("proto2_defaults.proto", &no_views()); - let content = &files[0].content; + let content = files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n"); + let content = &content; assert!(content.contains("pub struct WithDefaults")); assert!(content.contains("pub struct Proto2Message")); assert!(content.contains("pub name:")); @@ -159,7 +176,12 @@ fn codegen_proto2_defaults() { #[test] fn codegen_json_produces_serde_derives() { let files = generate_for("json_types.proto", &json_no_views()); - let content = &files[0].content; + let content = files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n"); + let content = &content; assert!(content.contains("::serde::Serialize")); assert!(content.contains("::serde::Deserialize")); } @@ -167,13 +189,20 @@ fn codegen_json_produces_serde_derives() { #[test] fn codegen_json_disabled_has_no_serde() { let files = generate_for("json_types.proto", &no_views()); - assert!(!files[0].content.contains("::serde::Serialize")); + assert!(!files + .iter() + .any(|f| f.content.contains("::serde::Serialize"))); } #[test] fn codegen_nested_deep() { let files = generate_for("nested_deep.proto", &no_views()); - let content = &files[0].content; + let content = files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n"); + let content = &content; assert!(content.contains("pub mod outer")); assert!(content.contains("pub mod middle")); assert!(content.contains("pub struct Inner")); @@ -182,7 +211,12 @@ fn codegen_nested_deep() { #[test] fn codegen_wkt_auto_mapping() { let files = generate_for("wkt_usage.proto", &no_views()); - let content = &files[0].content; + let content = files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n"); + let content = &content; assert!(content.contains("::buffa_types::google::protobuf::Timestamp")); assert!(content.contains("::buffa_types::google::protobuf::Duration")); } @@ -194,7 +228,12 @@ fn codegen_wkt_explicit_extern_overrides_auto() { .extern_paths .push((".google.protobuf".into(), "::my_custom_wkts".into())); let files = generate_for("wkt_usage.proto", &config); - let content = &files[0].content; + let content = files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n"); + let content = &content; assert!(content.contains("::my_custom_wkts::Timestamp")); assert!(!content.contains("::buffa_types::")); } @@ -202,7 +241,12 @@ fn codegen_wkt_explicit_extern_overrides_auto() { #[test] fn codegen_name_collisions() { let files = generate_for("name_collisions.proto", &no_views()); - let content = &files[0].content; + let content = files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n"); + let content = &content; assert!(content.contains("pub struct Vec")); assert!(content.contains("pub struct String")); assert!(content.contains("pub struct Option")); @@ -224,17 +268,30 @@ fn codegen_cross_package_uses_super() { ); let files = buffa_codegen::generate(&fds.file, &["cross_package.proto".into()], &no_views()) .expect("codegen failed"); - assert!(files[0].content.contains("super::")); + assert!(files.iter().any(|f| f.content.contains("super::"))); } // ── Module tree generation ────────────────────────────────────────────── #[test] fn module_tree_basic() { + use buffa_codegen::{GeneratedFileKind, ModuleTreeEntry}; let entries = vec![ - ("foo.rs", "my.pkg"), - ("bar.rs", "my.pkg"), - ("baz.rs", "other"), + ModuleTreeEntry { + file_name: "foo.rs", + package: "my.pkg", + kind: GeneratedFileKind::Owned, + }, + ModuleTreeEntry { + file_name: "bar.rs", + package: "my.pkg", + kind: GeneratedFileKind::Owned, + }, + ModuleTreeEntry { + file_name: "baz.rs", + package: "other", + kind: GeneratedFileKind::Owned, + }, ]; let tree = buffa_codegen::generate_module_tree(&entries, "", false); assert!(tree.contains("pub mod my")); @@ -245,7 +302,12 @@ fn module_tree_basic() { #[test] fn module_tree_inner_allow() { - let entries = vec![("f.rs", "pkg")]; + use buffa_codegen::{GeneratedFileKind, ModuleTreeEntry}; + let entries = vec![ModuleTreeEntry { + file_name: "f.rs", + package: "pkg", + kind: GeneratedFileKind::Owned, + }]; let with = buffa_codegen::generate_module_tree(&entries, "", true); let without = buffa_codegen::generate_module_tree(&entries, "", false); assert!(with.contains("#![allow(")); @@ -254,11 +316,95 @@ fn module_tree_inner_allow() { #[test] fn module_tree_keyword_escaping() { - let entries = vec![("t.rs", "google.type")]; + use buffa_codegen::{GeneratedFileKind, ModuleTreeEntry}; + let entries = vec![ModuleTreeEntry { + file_name: "t.rs", + package: "google.type", + kind: GeneratedFileKind::Owned, + }]; let tree = buffa_codegen::generate_module_tree(&entries, "", false); assert!(tree.contains("pub mod r#type")); } +#[test] +fn module_tree_coalesces_view_and_ext_per_package() { + // Two files in the same proto package — `my.pkg` — each contributing + // all five sibling kinds. The module tree must wrap them in a single + // `pub mod view { … pub mod oneofs { … } }`, a single `pub mod ext`, + // and a single outer `pub mod oneofs` per package, not one per source + // file. + use buffa_codegen::{GeneratedFileKind, ModuleTreeEntry}; + let entries = vec![ + ModuleTreeEntry { + file_name: "foo.rs", + package: "my.pkg", + kind: GeneratedFileKind::Owned, + }, + ModuleTreeEntry { + file_name: "foo.__view.rs", + package: "my.pkg", + kind: GeneratedFileKind::View, + }, + ModuleTreeEntry { + file_name: "foo.__ext.rs", + package: "my.pkg", + kind: GeneratedFileKind::Ext, + }, + ModuleTreeEntry { + file_name: "foo.__oneofs.rs", + package: "my.pkg", + kind: GeneratedFileKind::Oneofs, + }, + ModuleTreeEntry { + file_name: "foo.__view_oneofs.rs", + package: "my.pkg", + kind: GeneratedFileKind::ViewOneofs, + }, + ModuleTreeEntry { + file_name: "bar.rs", + package: "my.pkg", + kind: GeneratedFileKind::Owned, + }, + ModuleTreeEntry { + file_name: "bar.__view.rs", + package: "my.pkg", + kind: GeneratedFileKind::View, + }, + ModuleTreeEntry { + file_name: "bar.__ext.rs", + package: "my.pkg", + kind: GeneratedFileKind::Ext, + }, + ModuleTreeEntry { + file_name: "bar.__oneofs.rs", + package: "my.pkg", + kind: GeneratedFileKind::Oneofs, + }, + ModuleTreeEntry { + file_name: "bar.__view_oneofs.rs", + package: "my.pkg", + kind: GeneratedFileKind::ViewOneofs, + }, + ]; + let tree = buffa_codegen::generate_module_tree(&entries, "", false); + // One `pub mod view` per package, containing one inner `pub mod oneofs` + // for the view-oneof kind, plus one `pub mod ext` and one outer + // `pub mod oneofs` for the owned-oneof kind. + assert_eq!(tree.matches("pub mod view {").count(), 1); + assert_eq!(tree.matches("pub mod ext {").count(), 1); + // Two `pub mod oneofs {` occurrences: one nested inside `view::` for + // view-of-oneofs, and one at the package root for owned oneofs. + assert_eq!(tree.matches("pub mod oneofs {").count(), 2); + assert!(tree.contains(r#"include!("foo.__view.rs")"#)); + assert!(tree.contains(r#"include!("bar.__view.rs")"#)); + assert!(tree.contains(r#"include!("foo.__ext.rs")"#)); + assert!(tree.contains(r#"include!("bar.__ext.rs")"#)); + assert!(tree.contains(r#"include!("foo.__oneofs.rs")"#)); + assert!(tree.contains(r#"include!("bar.__oneofs.rs")"#)); + assert!(tree.contains(r#"include!("foo.__view_oneofs.rs")"#)); + assert!(tree.contains(r#"include!("bar.__view_oneofs.rs")"#)); +} + // ── Inline proto tests ────────────────────────────────────────────────── // These replace the hand-constructed DescriptorProto tests in lib.rs, // using inline proto definitions for clarity. @@ -363,8 +509,9 @@ fn inline_oneof() { "#, &no_views(), ); - assert!(content.contains("pub info: Option")); - assert!(content.contains("pub enum InfoOneof")); + // Oneof enum now lives in the parallel `oneofs::` tree per package. + assert!(content.contains("pub info: Option")); + assert!(content.contains("pub enum Info")); assert!(content.contains("Email(")); assert!(content.contains("Phone(")); } @@ -512,10 +659,12 @@ fn inline_oneof_duplicate_message_type_no_from_collision() { "#, &no_views(), ); - // Box on both message variants. + // Box on both message variants. The oneof enum now lives at + // `oneofs::t::Kind`, so references to the sibling-at-package-root + // `Placeholder` and `T` go through `super::super::`. assert_eq!( content - .matches("::buffa::alloc::boxed::Box") + .matches("::buffa::alloc::boxed::Box") .count(), 2, "both Placeholder variants should be boxed: {content}" @@ -523,13 +672,15 @@ fn inline_oneof_duplicate_message_type_no_from_collision() { // Only ONE From impl (for T, which appears once), not two for Placeholder. assert_eq!( content - .matches("impl From for Kind") + .matches("impl From for Kind") .count(), 0, "duplicate-type From impls must be skipped: {content}" ); assert_eq!( - content.matches("impl From for Kind").count(), + content + .matches("impl From for Kind") + .count(), 1, "unique-type From impl must still be generated: {content}" ); @@ -664,7 +815,12 @@ fn inline_cross_package_super() { ); let files = buffa_codegen::generate(&fds.file, &["main.proto".into()], &no_views()) .expect("codegen failed"); - let content = &files[0].content; + let content = files + .iter() + .map(|f| f.content.as_str()) + .collect::>() + .join("\n"); + let content = &content; // Cross-package ref should use super:: to reach sibling package. assert!( content.contains("super::dep::Shared"), diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__ext.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__ext.rs new file mode 100644 index 0000000..e14b820 --- /dev/null +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/compiler/plugin.proto + diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__oneofs.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__oneofs.rs new file mode 100644 index 0000000..5d7007d --- /dev/null +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: google/protobuf/compiler/plugin.proto + diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs new file mode 100644 index 0000000..a1617d9 --- /dev/null +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents, empty) +// source: google/protobuf/compiler/plugin.proto + diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view_oneofs.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view_oneofs.rs new file mode 100644 index 0000000..8a3fca4 --- /dev/null +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: google/protobuf/compiler/plugin.proto + diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs index e5f13f5..ef49764 100644 --- a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/compiler/plugin.proto /// The version number of protocol compiler. diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.__ext.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.__ext.rs new file mode 100644 index 0000000..fd1584c --- /dev/null +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/descriptor.proto + diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.__oneofs.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.__oneofs.rs new file mode 100644 index 0000000..8da3351 --- /dev/null +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: google/protobuf/descriptor.proto + diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs new file mode 100644 index 0000000..328aa7d --- /dev/null +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents, empty) +// source: google/protobuf/descriptor.proto + diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.__view_oneofs.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.__view_oneofs.rs new file mode 100644 index 0000000..485ecdf --- /dev/null +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: google/protobuf/descriptor.proto + diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.rs index 6932363..f11c65a 100644 --- a/buffa-descriptor/src/generated/google.protobuf.descriptor.rs +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/descriptor.proto /// The full set of known editions. diff --git a/buffa-descriptor/src/generated/mod.rs b/buffa-descriptor/src/generated/mod.rs index bab2dfe..572abb6 100644 --- a/buffa-descriptor/src/generated/mod.rs +++ b/buffa-descriptor/src/generated/mod.rs @@ -8,6 +8,15 @@ //! //! To regenerate, run `task gen-bootstrap-types` from the repo root. +// Each `.proto` file contributes five sibling outputs to its package +// module (see `buffa-codegen::GeneratedFileKind` — owned, view, ext, +// oneofs, view_oneofs). Descriptor codegen runs with +// `generate_views=false` and has no file-level extensions / oneofs, so +// the ancillary `.__view*.rs` / `.__ext.rs` / `.__oneofs.rs` / +// `.__view_oneofs.rs` siblings are empty-bodied — we still `include!` +// them so the sibling-file invariant is visible in the source tree and +// so fresh clones don't surprise the reader. + #[allow( clippy::all, dead_code, diff --git a/buffa-test/src/lib.rs b/buffa-test/src/lib.rs index aa2a4d9..7abd77e 100644 --- a/buffa-test/src/lib.rs +++ b/buffa-test/src/lib.rs @@ -1,12 +1,74 @@ // Wrap generated code in the package module so intra-file type references // (e.g. `basic::Status`, `basic::Address`) resolve correctly. // +// Each `.proto` file produces five sibling `.rs` outputs: +// - `.rs` — owned items (structs, enums, nested modules) +// - `.__view.rs` — view-tree contents (included inside `view::`) +// - `.__ext.rs` — extension-tree contents (included inside `ext::`) +// - `.__oneofs.rs` — owned oneof enums (inside `oneofs::`) +// - `.__view_oneofs.rs` — view oneof enums (inside `view::oneofs::`) +// +// Each pub mod below mirrors that layout: `include!` the owned file at +// the package root, and stitch the ancillary siblings inside +// `pub mod view { … pub mod oneofs { … } }`, `pub mod ext { … }`, and +// `pub mod oneofs { … }` per package. +// // The clippy allows suppress lints that fire on generated code patterns: // - derivable_impls: generated enum Default impls are explicit rather than derived // - match_single_binding: empty messages generate a single-arm wildcard merge match + +macro_rules! include_generated { + ($stem:literal) => { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".rs")); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + clippy::wildcard_in_or_patterns, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod view { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__view.rs")); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + clippy::wildcard_in_or_patterns, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__view_oneofs.rs")); + } + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod ext { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__ext.rs")); + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + clippy::wildcard_in_or_patterns, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!(env!("OUT_DIR"), "/", $stem, ".__oneofs.rs")); + } + }; +} + #[allow(clippy::derivable_impls, clippy::match_single_binding)] pub mod basic { - include!(concat!(env!("OUT_DIR"), "/basic.rs")); + include_generated!("basic"); } #[allow( @@ -15,7 +77,7 @@ pub mod basic { non_camel_case_types )] pub mod proto3sem { - include!(concat!(env!("OUT_DIR"), "/proto3_semantics.rs")); + include_generated!("proto3_semantics"); } #[allow( @@ -25,37 +87,37 @@ pub mod proto3sem { dead_code )] pub mod keywords { - include!(concat!(env!("OUT_DIR"), "/keywords.rs")); + include_generated!("keywords"); } #[allow(clippy::derivable_impls, clippy::match_single_binding)] pub mod nested { - include!(concat!(env!("OUT_DIR"), "/nested_deep.rs")); + include_generated!("nested_deep"); } #[allow(clippy::derivable_impls, clippy::match_single_binding)] pub mod wkt { - include!(concat!(env!("OUT_DIR"), "/wkt_usage.rs")); + include_generated!("wkt_usage"); } #[allow(clippy::derivable_impls, clippy::match_single_binding)] pub mod cross { - include!(concat!(env!("OUT_DIR"), "/cross_package.rs")); + include_generated!("cross_package"); } #[allow(clippy::derivable_impls, clippy::match_single_binding)] pub mod cross_syntax { - include!(concat!(env!("OUT_DIR"), "/cross_syntax.rs")); + include_generated!("cross_syntax"); } #[allow(clippy::derivable_impls, clippy::match_single_binding)] pub mod collisions { - include!(concat!(env!("OUT_DIR"), "/name_collisions.rs")); + include_generated!("name_collisions"); } #[allow(clippy::derivable_impls, clippy::match_single_binding, dead_code)] pub mod prelude_shadow { - include!(concat!(env!("OUT_DIR"), "/prelude_shadow.rs")); + include_generated!("prelude_shadow"); } #[allow( @@ -64,7 +126,7 @@ pub mod prelude_shadow { non_camel_case_types )] pub mod proto2 { - include!(concat!(env!("OUT_DIR"), "/proto2_defaults.rs")); + include_generated!("proto2_defaults"); } #[allow( @@ -74,7 +136,7 @@ pub mod proto2 { dead_code )] pub mod json_types { - include!(concat!(env!("OUT_DIR"), "/json_types.rs")); + include_generated!("json_types"); } #[allow( @@ -83,7 +145,7 @@ pub mod json_types { non_camel_case_types )] pub mod p2json { - include!(concat!(env!("OUT_DIR"), "/proto2_json.rs")); + include_generated!("proto2_json"); } #[allow( @@ -92,7 +154,7 @@ pub mod p2json { non_camel_case_types )] pub mod utf8test { - include!(concat!(env!("OUT_DIR"), "/utf8_validation.rs")); + include_generated!("utf8_validation"); } #[allow( @@ -103,7 +165,7 @@ pub mod utf8test { dead_code )] pub mod edenumjson { - include!(concat!(env!("OUT_DIR"), "/editions_enum_json.rs")); + include_generated!("editions_enum_json"); } #[allow( @@ -113,7 +175,7 @@ pub mod edenumjson { dead_code )] pub mod edge { - include!(concat!(env!("OUT_DIR"), "/edge_cases.rs")); + include_generated!("edge_cases"); } #[allow( @@ -123,7 +185,7 @@ pub mod edge { dead_code )] pub mod custopts { - include!(concat!(env!("OUT_DIR"), "/custom_options.rs")); + include_generated!("custom_options"); } #[allow( @@ -133,7 +195,7 @@ pub mod custopts { dead_code )] pub mod extjson { - include!(concat!(env!("OUT_DIR"), "/ext_json.rs")); + include_generated!("ext_json"); } #[allow( @@ -143,7 +205,7 @@ pub mod extjson { dead_code )] pub mod groupext { - include!(concat!(env!("OUT_DIR"), "/group_ext.rs")); + include_generated!("group_ext"); } #[allow( @@ -153,7 +215,7 @@ pub mod groupext { dead_code )] pub mod msgset { - include!(concat!(env!("OUT_DIR"), "/messageset.rs")); + include_generated!("messageset"); } #[cfg(has_edition_2024)] @@ -164,7 +226,7 @@ pub mod msgset { dead_code )] pub mod ed2024 { - include!(concat!(env!("OUT_DIR"), "/editions_2024.rs")); + include_generated!("editions_2024"); } // Regression: use_bytes_type() previously produced uncompilable decode code. @@ -178,6 +240,49 @@ pub mod ed2024 { )] pub mod basic_bytes { include!(concat!(env!("OUT_DIR"), "/bytes_variant/basic.rs")); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod view { + include!(concat!(env!("OUT_DIR"), "/bytes_variant/basic.__view.rs")); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/bytes_variant/basic.__view_oneofs.rs" + )); + } + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod ext { + include!(concat!(env!("OUT_DIR"), "/bytes_variant/basic.__ext.rs")); + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!(env!("OUT_DIR"), "/bytes_variant/basic.__oneofs.rs")); + } } // Views + preserve_unknown_fields=false: covers the else-branches in view @@ -190,6 +295,55 @@ pub mod basic_bytes { )] pub mod basic_no_uf { include!(concat!(env!("OUT_DIR"), "/no_unknown_views/basic.rs")); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod view { + include!(concat!( + env!("OUT_DIR"), + "/no_unknown_views/basic.__view.rs" + )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/no_unknown_views/basic.__view_oneofs.rs" + )); + } + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod ext { + include!(concat!(env!("OUT_DIR"), "/no_unknown_views/basic.__ext.rs")); + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/no_unknown_views/basic.__oneofs.rs" + )); + } } // These tests intentionally use the field-assignment style diff --git a/buffa-test/src/tests/basic.rs b/buffa-test/src/tests/basic.rs index 35b155d..2acfb77 100644 --- a/buffa-test/src/tests/basic.rs +++ b/buffa-test/src/tests/basic.rs @@ -191,19 +191,27 @@ fn test_map_empty_round_trip() { fn test_oneof_round_trip() { // Set the email variant. let mut msg = Person::default(); - msg.contact = Some(person::ContactOneof::Email("alice@example.com".into())); + msg.contact = Some(crate::basic::oneofs::person::Contact::Email( + "alice@example.com".into(), + )); let decoded = round_trip(&msg); assert_eq!( decoded.contact, - Some(person::ContactOneof::Email("alice@example.com".into())) + Some(crate::basic::oneofs::person::Contact::Email( + "alice@example.com".into() + )) ); // Overwrite with phone variant — last write wins. - msg.contact = Some(person::ContactOneof::Phone("+1-555-1234".into())); + msg.contact = Some(crate::basic::oneofs::person::Contact::Phone( + "+1-555-1234".into(), + )); let decoded = round_trip(&msg); assert_eq!( decoded.contact, - Some(person::ContactOneof::Phone("+1-555-1234".into())) + Some(crate::basic::oneofs::person::Contact::Phone( + "+1-555-1234".into() + )) ); // Unset. diff --git a/buffa-test/src/tests/bytes_type.rs b/buffa-test/src/tests/bytes_type.rs index 44cddd4..26958c3 100644 --- a/buffa-test/src/tests/bytes_type.rs +++ b/buffa-test/src/tests/bytes_type.rs @@ -49,7 +49,7 @@ fn test_bytes_type_view_to_owned() { // Views borrow &[u8]; to_owned_message must produce Bytes (not Vec) // when use_bytes_type() is active. Previously this emitted .to_vec() // unconditionally, failing to compile. - use crate::basic_bytes::PersonView; + use crate::basic_bytes::view::PersonView; use buffa::MessageView; let msg = Person { id: 7, @@ -82,8 +82,9 @@ fn test_bytes_type_view_to_owned() { // The bytes_variant build block compiles BytesContexts with use_bytes_type() // + generate_views=true; compilation alone is the primary assertion. -use crate::basic_bytes::bytes_contexts::ChoiceOneof; -use crate::basic_bytes::{BytesContexts, BytesContextsView}; +use crate::basic_bytes::oneofs::bytes_contexts::Choice; +use crate::basic_bytes::view::BytesContextsView; +use crate::basic_bytes::BytesContexts; #[test] fn test_bytes_type_repeated_view_to_owned() { @@ -116,9 +117,7 @@ fn test_bytes_type_repeated_view_to_owned() { fn test_bytes_type_oneof_view_to_owned() { use buffa::MessageView; let msg = BytesContexts { - choice: Some(ChoiceOneof::Raw(bytes::Bytes::from_static(&[ - 0x00, 0xFF, 0x7F, - ]))), + choice: Some(Choice::Raw(bytes::Bytes::from_static(&[0x00, 0xFF, 0x7F]))), ..Default::default() }; let wire = msg.encode_to_vec(); @@ -130,7 +129,7 @@ fn test_bytes_type_oneof_view_to_owned() { // Match ergonomics: v in the arm is &&[u8], *v is &[u8]. let owned: BytesContexts = view.to_owned_message(); match &owned.choice { - Some(ChoiceOneof::Raw(b)) => assert_eq!(&b[..], &[0x00, 0xFF, 0x7F]), + Some(Choice::Raw(b)) => assert_eq!(&b[..], &[0x00, 0xFF, 0x7F]), other => panic!("expected Choice::Raw, got {other:?}"), } assert_eq!(owned.encode_to_vec(), wire); @@ -180,7 +179,7 @@ fn test_bytes_type_json_all_contexts_roundtrip() { bytes::Bytes::from_static(&[0x04]), bytes::Bytes::from_static(b""), ], - choice: Some(ChoiceOneof::Raw(bytes::Bytes::from_static(&[0xDE, 0xAD]))), + choice: Some(Choice::Raw(bytes::Bytes::from_static(&[0xDE, 0xAD]))), by_key: [("k".to_string(), vec![0x05])].into_iter().collect(), ..Default::default() }; @@ -198,7 +197,7 @@ fn test_bytes_type_json_all_contexts_roundtrip() { assert_eq!(back.maybe.as_deref(), Some(&[0x02, 0x03][..])); assert_eq!(back.many, msg.many); match &back.choice { - Some(ChoiceOneof::Raw(b)) => assert_eq!(&b[..], &[0xDE, 0xAD]), + Some(Choice::Raw(b)) => assert_eq!(&b[..], &[0xDE, 0xAD]), other => panic!("expected Choice::Raw, got {other:?}"), } assert_eq!(back.by_key, msg.by_key); @@ -243,7 +242,7 @@ fn test_bytes_type_json_cross_decodes_external_json() { assert!(back.many[0].is_empty()); assert_eq!(&back.many[1][..], b"A"); match &back.choice { - Some(ChoiceOneof::Raw(b)) => assert_eq!(&b[..], &[0x00, 0xFF, 0x7F]), + Some(Choice::Raw(b)) => assert_eq!(&b[..], &[0x00, 0xFF, 0x7F]), other => panic!("expected Choice::Raw, got {other:?}"), } } diff --git a/buffa-test/src/tests/closed_enum.rs b/buffa-test/src/tests/closed_enum.rs index 1e00287..aa96bd7 100644 --- a/buffa-test/src/tests/closed_enum.rs +++ b/buffa-test/src/tests/closed_enum.rs @@ -182,7 +182,7 @@ fn test_closed_enum_negative_unknown_value_sign_extension() { #[test] fn test_view_closed_enum_optional_unknown_to_unknown_fields() { - use crate::proto2::ClosedEnumContextsView; + use crate::proto2::view::ClosedEnumContextsView; use buffa::MessageView; let wire = varint_field(1, 99); let view = ClosedEnumContextsView::decode_view(&wire).unwrap(); @@ -195,7 +195,8 @@ fn test_view_closed_enum_optional_unknown_to_unknown_fields() { #[test] fn test_view_closed_enum_repeated_unpacked_unknown_preserved() { - use crate::proto2::{ClosedEnumContextsView, Priority}; + use crate::proto2::view::ClosedEnumContextsView; + use crate::proto2::Priority; use buffa::MessageView; // Field 2 (unpacked): [LOW=0, 99, HIGH=2] let mut wire = Vec::new(); @@ -219,7 +220,7 @@ fn test_view_closed_enum_repeated_unpacked_unknown_preserved() { #[test] fn test_view_closed_enum_oneof_unknown_to_unknown_fields() { - use crate::proto2::ClosedEnumContextsView; + use crate::proto2::view::ClosedEnumContextsView; use buffa::MessageView; let wire = varint_field(4, 99); let view = ClosedEnumContextsView::decode_view(&wire).unwrap(); @@ -231,7 +232,8 @@ fn test_view_closed_enum_oneof_unknown_to_unknown_fields() { #[test] fn test_view_closed_enum_known_not_routed() { - use crate::proto2::{ClosedEnumContextsView, Priority}; + use crate::proto2::view::ClosedEnumContextsView; + use crate::proto2::Priority; use buffa::MessageView; let wire = varint_field(1, 2); // HIGH = 2 let view = ClosedEnumContextsView::decode_view(&wire).unwrap(); @@ -243,7 +245,8 @@ fn test_view_closed_enum_known_not_routed() { fn test_view_owned_parity_for_closed_enum_unknowns() { // Whatever the owned decoder produces, the view path must produce // byte-identical output after to_owned_message().encode_to_vec(). - use crate::proto2::{ClosedEnumContexts, ClosedEnumContextsView}; + use crate::proto2::view::ClosedEnumContextsView; + use crate::proto2::ClosedEnumContexts; use buffa::{Message, MessageView}; let mut wire = Vec::new(); wire.extend(varint_field(1, 99)); // optional unknown diff --git a/buffa-test/src/tests/collision.rs b/buffa-test/src/tests/collision.rs index c757fca..fe4d226 100644 --- a/buffa-test/src/tests/collision.rs +++ b/buffa-test/src/tests/collision.rs @@ -81,23 +81,23 @@ fn test_oneof_name_matching_parent_message() { use crate::collisions; let msg = collisions::Status { - status: Some(collisions::status::StatusOneof::Code(42)), + status: Some(collisions::oneofs::status::Status::Code(42)), ..core::default::Default::default() }; let decoded = round_trip(&msg); assert_eq!( decoded.status, - Some(collisions::status::StatusOneof::Code(42)) + Some(collisions::oneofs::status::Status::Code(42)) ); let msg2 = collisions::Status { - status: Some(collisions::status::StatusOneof::Message("error".into())), + status: Some(collisions::oneofs::status::Status::Message("error".into())), ..core::default::Default::default() }; let decoded = round_trip(&msg2); assert_eq!( decoded.status, - Some(collisions::status::StatusOneof::Message("error".into())) + Some(collisions::oneofs::status::Status::Message("error".into())) ); } @@ -115,7 +115,7 @@ fn test_container_references_collision_types() { ..core::default::Default::default() }), status: buffa::MessageField::some(collisions::Status { - status: Some(collisions::status::StatusOneof::Code(1)), + status: Some(collisions::oneofs::status::Status::Code(1)), ..core::default::Default::default() }), ..core::default::Default::default() @@ -130,12 +130,12 @@ fn test_nested_option_message_round_trip() { // gh#36: nested `message Option` shadows core::option::Option in the // message's `pub mod { use super::*; }` scope. The proto is built with // views + JSON enabled so all Option<...> emission paths compile. - use crate::prelude_shadow::{self, picker}; + use crate::prelude_shadow::{self, oneofs, picker}; let msg = prelude_shadow::Picker { options: vec![picker::Option { title: Some("a".into()), - value: Some(picker::option::ValueOneof::IntValue(7)), + value: Some(oneofs::picker::option::Value::IntValue(7)), ..core::default::Default::default() }], label: Some("L".into()), diff --git a/buffa-test/src/tests/extensions.rs b/buffa-test/src/tests/extensions.rs index 6525389..e029c13 100644 --- a/buffa-test/src/tests/extensions.rs +++ b/buffa-test/src/tests/extensions.rs @@ -1,9 +1,10 @@ //! Integration tests for `extend` codegen and the `ExtensionSet` runtime API. -use crate::custopts::{ - carrier, Annotation, Carrier, ACTIVE, ANN, C_ACTIVE, C_ANN, C_FLAG, C_LABEL, C_MARKER, - C_PRIORITY, C_TAGS, C_WEIGHT, IS_INTERNAL, LABEL, PRIORITY, TAGS, WEIGHT, +use crate::custopts::ext::{ + ACTIVE, ANN, C_ACTIVE, C_ANN, C_FLAG, C_LABEL, C_MARKER, C_PRIORITY, C_TAGS, C_WEIGHT, + IS_INTERNAL, LABEL, PRIORITY, TAGS, WEIGHT, }; +use crate::custopts::{carrier, Annotation, Carrier}; use buffa::{Extension, ExtensionSet, Message}; /// Sanity: field numbers, extendees, and codec types match the proto source. @@ -272,7 +273,8 @@ fn proto2_default_after_roundtrip() { // Group-encoded extensions (editions DELIMITED) // ──────────────────────────────────────────────────────────────────────────── -use crate::groupext::{Carrier as GroupCarrier, Inner, DELIM_INNER, DELIM_REPEATED}; +use crate::groupext::ext::{DELIM_INNER, DELIM_REPEATED}; +use crate::groupext::{Carrier as GroupCarrier, Inner}; use buffa::extension::codecs::{GroupCodec, Repeated}; /// Codec type check: DELIMITED extension gets `GroupCodec`, not `MessageCodec`. diff --git a/buffa-test/src/tests/extensions_json.rs b/buffa-test/src/tests/extensions_json.rs index 542d8fc..e6127a9 100644 --- a/buffa-test/src/tests/extensions_json.rs +++ b/buffa-test/src/tests/extensions_json.rs @@ -2,9 +2,8 @@ //! round-trip through `serde_json` via the generated `register_types` + //! the runtime's `#[serde(flatten)]` wrapper. -use crate::extjson::{ - register_types, Ann, Carrier, Color, ANN, ANNS, BIGS, COLOR, COLORS, NUMS, WEIGHT, -}; +use crate::extjson::ext::{register_types, ANN, ANNS, BIGS, COLOR, COLORS, NUMS, WEIGHT}; +use crate::extjson::{Ann, Carrier, Color}; use buffa::type_registry::{set_type_registry, TypeRegistry}; use buffa::{Enumeration, ExtensionSet}; diff --git a/buffa-test/src/tests/json.rs b/buffa-test/src/tests/json.rs index b842d6a..844a048 100644 --- a/buffa-test/src/tests/json.rs +++ b/buffa-test/src/tests/json.rs @@ -51,7 +51,7 @@ fn test_json_oneof_round_trip() { use crate::json_types::WithOneof; let msg = WithOneof { - value: Some(crate::json_types::with_oneof::ValueOneof::Text( + value: Some(crate::json_types::oneofs::with_oneof::Value::Text( "hello".into(), )), ..Default::default() @@ -66,24 +66,24 @@ fn test_json_oneof_all_scalar_types_round_trip() { // Exercises serde_helper_path dispatch for all proto3-JSON-special // scalar types in oneof position, and the corresponding runtime // json_helpers::{int64, uint32, uint64, float, double, bytes} paths. - use crate::json_types::with_oneof_types::KindOneof; + use crate::json_types::oneofs::with_oneof_types::Kind; use crate::json_types::WithOneofTypes; #[rustfmt::skip] - let cases: &[(KindOneof, &str)] = &[ + let cases: &[(Kind, &str)] = &[ // int64 → quoted decimal string. - (KindOneof::I64(i64::MAX), r#"{"i64":"9223372036854775807"}"#), - (KindOneof::I64(-1), r#"{"i64":"-1"}"#), + (Kind::I64(i64::MAX), r#"{"i64":"9223372036854775807"}"#), + (Kind::I64(-1), r#"{"i64":"-1"}"#), // uint32 → unquoted integer. - (KindOneof::U32(u32::MAX), r#"{"u32":4294967295}"#), + (Kind::U32(u32::MAX), r#"{"u32":4294967295}"#), // uint64 → quoted decimal string. - (KindOneof::U64(u64::MAX), r#"{"u64":"18446744073709551615"}"#), + (Kind::U64(u64::MAX), r#"{"u64":"18446744073709551615"}"#), // float → JSON number. - (KindOneof::F32(1.5), r#"{"f32":1.5}"#), + (Kind::F32(1.5), r#"{"f32":1.5}"#), // double → JSON number. - (KindOneof::F64(3.25), r#"{"f64":3.25}"#), + (Kind::F64(3.25), r#"{"f64":3.25}"#), // bytes → base64-encoded string. - (KindOneof::B(vec![0xDE, 0xAD]), r#"{"b":"3q0="}"#), + (Kind::B(vec![0xDE, 0xAD]), r#"{"b":"3q0="}"#), ]; for (kind, expected_json) in cases { @@ -96,8 +96,8 @@ fn test_json_oneof_all_scalar_types_round_trip() { let decoded: WithOneofTypes = serde_json::from_str(&json).expect("deserialize"); match (&decoded.kind, kind) { - (Some(KindOneof::F32(a)), KindOneof::F32(b)) => assert_eq!(a, b), - (Some(KindOneof::F64(a)), KindOneof::F64(b)) => assert_eq!(a, b), + (Some(Kind::F32(a)), Kind::F32(b)) => assert_eq!(a, b), + (Some(Kind::F64(a)), Kind::F64(b)) => assert_eq!(a, b), (a, b) => assert_eq!(a, &Some(b.clone()), "deserialize mismatch"), } } @@ -106,17 +106,17 @@ fn test_json_oneof_all_scalar_types_round_trip() { #[test] fn test_json_oneof_float_special_values() { // NaN/Infinity/-Infinity serialize as string tokens per proto3-JSON spec. - use crate::json_types::with_oneof_types::KindOneof; + use crate::json_types::oneofs::with_oneof_types::Kind; use crate::json_types::WithOneofTypes; #[rustfmt::skip] - let cases: &[(KindOneof, &str)] = &[ - (KindOneof::F32(f32::NAN), r#"{"f32":"NaN"}"#), - (KindOneof::F32(f32::INFINITY), r#"{"f32":"Infinity"}"#), - (KindOneof::F32(f32::NEG_INFINITY), r#"{"f32":"-Infinity"}"#), - (KindOneof::F64(f64::NAN), r#"{"f64":"NaN"}"#), - (KindOneof::F64(f64::INFINITY), r#"{"f64":"Infinity"}"#), - (KindOneof::F64(f64::NEG_INFINITY), r#"{"f64":"-Infinity"}"#), + let cases: &[(Kind, &str)] = &[ + (Kind::F32(f32::NAN), r#"{"f32":"NaN"}"#), + (Kind::F32(f32::INFINITY), r#"{"f32":"Infinity"}"#), + (Kind::F32(f32::NEG_INFINITY), r#"{"f32":"-Infinity"}"#), + (Kind::F64(f64::NAN), r#"{"f64":"NaN"}"#), + (Kind::F64(f64::INFINITY), r#"{"f64":"Infinity"}"#), + (Kind::F64(f64::NEG_INFINITY), r#"{"f64":"-Infinity"}"#), ]; for (kind, expected_json) in cases { @@ -130,13 +130,13 @@ fn test_json_oneof_float_special_values() { let decoded: WithOneofTypes = serde_json::from_str(&json).expect("deserialize"); // Check class equality for NaN (NaN != NaN). match (decoded.kind.unwrap(), kind) { - (KindOneof::F32(a), KindOneof::F32(b)) => { + (Kind::F32(a), Kind::F32(b)) => { assert_eq!(a.is_nan(), b.is_nan(), "{kind:?}"); if !b.is_nan() { assert_eq!(a, *b, "{kind:?}"); } } - (KindOneof::F64(a), KindOneof::F64(b)) => { + (Kind::F64(a), Kind::F64(b)) => { assert_eq!(a.is_nan(), b.is_nan(), "{kind:?}"); if !b.is_nan() { assert_eq!(a, *b, "{kind:?}"); @@ -151,12 +151,12 @@ fn test_json_oneof_float_special_values() { fn test_json_oneof_null_value() { // google.protobuf.NullValue in a oneof serializes as JSON null. // On deserialize, JSON null populates the NullValue variant (not unset). - use crate::json_types::with_oneof_types::KindOneof; + use crate::json_types::oneofs::with_oneof_types::Kind; use crate::json_types::WithOneofTypes; use buffa_types::google::protobuf::NullValue; let msg = WithOneofTypes { - kind: Some(KindOneof::Nv(NullValue::NULL_VALUE.into())), + kind: Some(Kind::Nv(NullValue::NULL_VALUE.into())), ..Default::default() }; let json = serde_json::to_string(&msg).expect("serialize"); @@ -164,7 +164,7 @@ fn test_json_oneof_null_value() { let decoded: WithOneofTypes = serde_json::from_str(&json).expect("deserialize"); assert!( - matches!(decoded.kind, Some(KindOneof::Nv(_))), + matches!(decoded.kind, Some(Kind::Nv(_))), "expected Nv variant, got {:?}", decoded.kind ); @@ -174,14 +174,14 @@ fn test_json_oneof_null_value() { fn test_json_oneof_float_deserialize_from_integer() { // proto3-JSON: float/double fields accept integer JSON values. // Exercises json_helpers::float::visit_i64/visit_u64. - use crate::json_types::with_oneof_types::KindOneof; + use crate::json_types::oneofs::with_oneof_types::Kind; use crate::json_types::WithOneofTypes; let decoded: WithOneofTypes = serde_json::from_str(r#"{"f32": 42}"#).unwrap(); - assert_eq!(decoded.kind, Some(KindOneof::F32(42.0))); + assert_eq!(decoded.kind, Some(Kind::F32(42.0))); let decoded: WithOneofTypes = serde_json::from_str(r#"{"f64": -7}"#).unwrap(); - assert_eq!(decoded.kind, Some(KindOneof::F64(-7.0))); + assert_eq!(decoded.kind, Some(Kind::F64(-7.0))); } #[test] @@ -437,7 +437,7 @@ fn test_json_optional_open_enum_integer_deserialize() { #[test] fn test_json_mixed_oneof_and_fields_round_trip() { - use crate::json_types::mixed_oneof_and_fields::ChoiceOneof; + use crate::json_types::oneofs::mixed_oneof_and_fields::Choice; use crate::json_types::{MixedOneofAndFields, Scalar}; let msg = MixedOneofAndFields { @@ -450,7 +450,7 @@ fn test_json_mixed_oneof_and_fields_round_trip() { }), dynamic: buffa::MessageField::some(buffa_types::google::protobuf::Value::from(3.14)), snake_case_field: 7, - choice: Some(ChoiceOneof::Text("hello".into())), + choice: Some(Choice::Text("hello".into())), ..Default::default() }; @@ -469,7 +469,7 @@ fn test_json_mixed_oneof_and_fields_round_trip() { assert_eq!(decoded.scalar.int32_val, 42); assert_eq!(decoded.dynamic.as_number(), Some(3.14)); assert_eq!(decoded.snake_case_field, 7); - assert_eq!(decoded.choice, Some(ChoiceOneof::Text("hello".into()))); + assert_eq!(decoded.choice, Some(Choice::Text("hello".into()))); } #[test] @@ -490,12 +490,13 @@ fn test_json_mixed_value_field_null_forwarding() { // not "field absent". The custom Deserialize must forward null to // Value's own Deserialize rather than skipping the field. use crate::json_types::MixedOneofAndFields; - use buffa_types::google::protobuf::{value::KindOneof, NullValue}; + use buffa_types::google::protobuf::oneofs::value::Kind; + use buffa_types::google::protobuf::NullValue; let decoded: MixedOneofAndFields = serde_json::from_str(r#"{"dynamic": null}"#).unwrap(); assert!(decoded.dynamic.is_set(), "null should set the Value field"); assert!( - matches!(decoded.dynamic.kind, Some(KindOneof::NullValue(_))), + matches!(decoded.dynamic.kind, Some(Kind::NullValue(_))), "expected NullValue, got {:?}", decoded.dynamic.kind ); diff --git a/buffa-test/src/tests/keyword.rs b/buffa-test/src/tests/keyword.rs index 2e6440a..c75e86c 100644 --- a/buffa-test/src/tests/keyword.rs +++ b/buffa-test/src/tests/keyword.rs @@ -46,13 +46,13 @@ fn test_keyword_expression_with_oneof() { ..Default::default() }), match_mode: buffa::EnumValue::Known(keywords::Match::MATCH_EXACT), - value: Some(keywords::expression::ValueOneof::Literal("42".into())), + value: Some(keywords::oneofs::expression::Value::Literal("42".into())), ..Default::default() }; let decoded = round_trip(&msg); assert_eq!(decoded.result_type.name, "bool"); assert_eq!( decoded.value, - Some(keywords::expression::ValueOneof::Literal("42".into())) + Some(keywords::oneofs::expression::Value::Literal("42".into())) ); } diff --git a/buffa-test/src/tests/message_set.rs b/buffa-test/src/tests/message_set.rs index 495181a..37c5864 100644 --- a/buffa-test/src/tests/message_set.rs +++ b/buffa-test/src/tests/message_set.rs @@ -8,7 +8,8 @@ //! `{number: type_id, data: LengthDelimited(payload)}`. Decode unwraps the //! group; encode rewraps it. -use crate::msgset::{Container, Marker, Payload, MARKER_EXT, PAYLOAD_EXT}; +use crate::msgset::ext::{MARKER_EXT, PAYLOAD_EXT}; +use crate::msgset::{Container, Marker, Payload}; use buffa::{ExtensionSet, Message}; #[test] diff --git a/buffa-test/src/tests/nesting.rs b/buffa-test/src/tests/nesting.rs index b8efe57..abe0e37 100644 --- a/buffa-test/src/tests/nesting.rs +++ b/buffa-test/src/tests/nesting.rs @@ -19,7 +19,7 @@ fn test_deep_nesting_round_trip() { }), ..Default::default() }), - content: Some(nested::outer::ContentOneof::Text("hello".into())), + content: Some(nested::oneofs::outer::Content::Text("hello".into())), ..Default::default() }; let decoded = round_trip(&msg); @@ -64,37 +64,41 @@ fn test_multi_oneof_variants() { // Test each variant type round-trips correctly. let cases: Vec = vec![ MultiOneof { - value: Some(nested::multi_oneof::ValueOneof::IntVal(42)), + value: Some(nested::oneofs::multi_oneof::Value::IntVal(42)), ..Default::default() }, MultiOneof { - value: Some(nested::multi_oneof::ValueOneof::LongVal(i64::MAX)), + value: Some(nested::oneofs::multi_oneof::Value::LongVal(i64::MAX)), ..Default::default() }, MultiOneof { - value: Some(nested::multi_oneof::ValueOneof::FloatVal(1.5)), + value: Some(nested::oneofs::multi_oneof::Value::FloatVal(1.5)), ..Default::default() }, MultiOneof { - value: Some(nested::multi_oneof::ValueOneof::DoubleVal( + value: Some(nested::oneofs::multi_oneof::Value::DoubleVal( std::f64::consts::PI, )), ..Default::default() }, MultiOneof { - value: Some(nested::multi_oneof::ValueOneof::BoolVal(true)), + value: Some(nested::oneofs::multi_oneof::Value::BoolVal(true)), ..Default::default() }, MultiOneof { - value: Some(nested::multi_oneof::ValueOneof::StringVal("hello".into())), + value: Some(nested::oneofs::multi_oneof::Value::StringVal( + "hello".into(), + )), ..Default::default() }, MultiOneof { - value: Some(nested::multi_oneof::ValueOneof::BytesVal(vec![0xFF, 0x00])), + value: Some(nested::oneofs::multi_oneof::Value::BytesVal(vec![ + 0xFF, 0x00, + ])), ..Default::default() }, MultiOneof { - value: Some(nested::multi_oneof::ValueOneof::MessageVal(Box::new( + value: Some(nested::oneofs::multi_oneof::Value::MessageVal(Box::new( nested::outer::middle::Inner { data: vec![1, 2, 3], active: true, @@ -116,21 +120,22 @@ fn test_recursive_oneof_direct() { // Expr { kind { Expr negated = 3; } } is directly self-recursive // through the oneof. Message/group variants are always boxed to // break the infinite-size cycle. - use crate::nested::{expr, Expr}; + use crate::nested::oneofs::expr; + use crate::nested::Expr; let inner = Expr { - kind: Some(expr::KindOneof::IntLiteral(42)), + kind: Some(expr::Kind::IntLiteral(42)), ..Default::default() }; let outer = Expr { - kind: Some(expr::KindOneof::Negated(Box::new(inner))), + kind: Some(expr::Kind::Negated(Box::new(inner))), ..Default::default() }; let decoded = round_trip(&outer); assert_eq!(decoded, outer); // Verify deref through Box works transparently in pattern matching. match &decoded.kind { - Some(expr::KindOneof::Negated(e)) => match &e.kind { - Some(expr::KindOneof::IntLiteral(n)) => assert_eq!(*n, 42), + Some(expr::Kind::Negated(e)) => match &e.kind { + Some(expr::Kind::IntLiteral(n)) => assert_eq!(*n, 42), other => panic!("expected IntLiteral, got {other:?}"), }, other => panic!("expected Negated, got {other:?}"), @@ -142,13 +147,14 @@ fn test_recursive_oneof_mutual() { // Expr -> BinaryOp -> Expr mutual recursion. BinaryOp fields use // MessageField (already boxed); the Expr.kind.binary variant is // the boxed side of the cycle. - use crate::nested::{expr, BinaryOp, Expr}; + use crate::nested::oneofs::expr; + use crate::nested::{BinaryOp, Expr}; let lhs = Expr { - kind: Some(expr::KindOneof::IntLiteral(1)), + kind: Some(expr::Kind::IntLiteral(1)), ..Default::default() }; let rhs = Expr { - kind: Some(expr::KindOneof::IntLiteral(2)), + kind: Some(expr::Kind::IntLiteral(2)), ..Default::default() }; let op = BinaryOp { @@ -159,7 +165,7 @@ fn test_recursive_oneof_mutual() { }; // Use the generated From impl instead of manual Box::new. let expr = Expr { - kind: Some(expr::KindOneof::from(op)), + kind: Some(expr::Kind::from(op)), ..Default::default() }; let decoded = round_trip(&expr); @@ -170,7 +176,8 @@ fn test_recursive_oneof_mutual() { fn test_from_msg_for_option_oneof() { // `From for Option` lets struct-literal construction skip both // the explicit `Some(...)` and `Box::new(...)` for message-typed variants. - use crate::nested::{expr, BinaryOp, Expr}; + use crate::nested::oneofs::expr; + use crate::nested::{BinaryOp, Expr}; let op = BinaryOp { op: "*".into(), ..Default::default() @@ -182,7 +189,7 @@ fn test_from_msg_for_option_oneof() { ..Default::default() }; let explicit = Expr { - kind: Some(expr::KindOneof::Binary(Box::new(op))), + kind: Some(expr::Kind::Binary(Box::new(op))), ..Default::default() }; assert_eq!(terse, explicit); @@ -190,7 +197,7 @@ fn test_from_msg_for_option_oneof() { // Round-trip sanity. let decoded = round_trip(&terse); match decoded.kind { - Some(expr::KindOneof::Binary(b)) => assert_eq!(b.op, "*"), + Some(expr::Kind::Binary(b)) => assert_eq!(b.op, "*"), other => panic!("expected Binary, got {other:?}"), } @@ -206,12 +213,13 @@ fn test_from_msg_for_option_oneof() { fn test_recursive_oneof_merge_semantics() { // When the same message-typed oneof variant appears twice on the // wire, the second occurrence merges into the first (proto3 spec). - use crate::nested::{expr, BinaryOp, Expr}; + use crate::nested::oneofs::expr; + use crate::nested::{BinaryOp, Expr}; let first = Expr { - kind: Some(expr::KindOneof::from(BinaryOp { + kind: Some(expr::Kind::from(BinaryOp { op: "+".into(), lhs: buffa::MessageField::some(Expr { - kind: Some(expr::KindOneof::IntLiteral(1)), + kind: Some(expr::Kind::IntLiteral(1)), ..Default::default() }), ..Default::default() @@ -219,9 +227,9 @@ fn test_recursive_oneof_merge_semantics() { ..Default::default() }; let second = Expr { - kind: Some(expr::KindOneof::from(BinaryOp { + kind: Some(expr::Kind::from(BinaryOp { rhs: buffa::MessageField::some(Expr { - kind: Some(expr::KindOneof::IntLiteral(2)), + kind: Some(expr::Kind::IntLiteral(2)), ..Default::default() }), ..Default::default() @@ -233,7 +241,7 @@ fn test_recursive_oneof_merge_semantics() { let merged = Expr::decode(&mut bytes.as_slice()).expect("decode"); // Both lhs (from first) and rhs (from second) should be present. match &merged.kind { - Some(expr::KindOneof::Binary(b)) => { + Some(expr::Kind::Binary(b)) => { assert_eq!(b.op, "+"); assert!(b.lhs.is_set(), "lhs from first merge lost"); assert!(b.rhs.is_set(), "rhs from second merge lost"); @@ -246,22 +254,24 @@ fn test_recursive_oneof_merge_semantics() { fn test_view_oneof_boxed_message_variant() { // View oneof enums box message/group variants for the same reason // as owned enums. The Box holds a lifetime-bound view struct. - use crate::nested::{expr, Expr, ExprView}; + use crate::nested::oneofs::expr; + use crate::nested::view::ExprView; + use crate::nested::Expr; use buffa::MessageView; let inner = Expr { - kind: Some(expr::KindOneof::IntLiteral(42)), + kind: Some(expr::Kind::IntLiteral(42)), ..Default::default() }; let outer = Expr { - kind: Some(expr::KindOneof::Negated(Box::new(inner))), + kind: Some(expr::Kind::Negated(Box::new(inner))), ..Default::default() }; let bytes = outer.encode_to_vec(); let view = ExprView::decode_view(&bytes).expect("decode_view"); // Pattern-matched binding auto-derefs through Box>. match &view.kind { - Some(expr::KindOneofView::Negated(v)) => match &v.kind { - Some(expr::KindOneofView::IntLiteral(n)) => assert_eq!(*n, 42), + Some(crate::nested::view::oneofs::expr::Kind::Negated(v)) => match &v.kind { + Some(crate::nested::view::oneofs::expr::Kind::IntLiteral(n)) => assert_eq!(*n, 42), other => panic!("expected IntLiteral, got {other:?}"), }, other => panic!("expected Negated, got {other:?}"), @@ -274,10 +284,11 @@ fn test_view_oneof_boxed_message_variant() { #[test] fn test_view_oneof_message_variant_to_owned() { // Non-recursive message variant: Middle in Outer.content.structured. - use crate::nested::{self, Outer, OuterView}; + use crate::nested::view::OuterView; + use crate::nested::{self, Outer}; use buffa::MessageView; let msg = Outer { - content: Some(nested::outer::ContentOneof::Structured(Box::new( + content: Some(nested::oneofs::outer::Content::Structured(Box::new( nested::outer::Middle { value: 7, ..Default::default() @@ -288,7 +299,7 @@ fn test_view_oneof_message_variant_to_owned() { let bytes = msg.encode_to_vec(); let view = OuterView::decode_view(&bytes).expect("decode_view"); match &view.content { - Some(nested::outer::ContentOneofView::Structured(m)) => assert_eq!(m.value, 7), + Some(crate::nested::view::oneofs::outer::Content::Structured(m)) => assert_eq!(m.value, 7), other => panic!("expected Structured, got {other:?}"), } assert_eq!(view.to_owned_message(), msg); @@ -323,7 +334,8 @@ fn test_recursive_singular_message_field() { fn test_view_recursive_singular_message_field() { // View path through the same cycle. MessageFieldView boxes internally, // Deref returns &V transparently. - use crate::nested::{corecursive, Corecursive, CorecursiveView}; + use crate::nested::view::CorecursiveView; + use crate::nested::{corecursive, Corecursive}; use buffa::MessageView; let msg = Corecursive { name: "root".into(), @@ -352,7 +364,8 @@ fn test_view_message_field_merge_semantics() { // When a singular message field appears twice on the wire, the // second occurrence merges into the first field-by-field (proto // spec). The view decoder must do this, not replace. - use crate::nested::{corecursive, Corecursive, CorecursiveView}; + use crate::nested::view::CorecursiveView; + use crate::nested::{corecursive, Corecursive}; use buffa::MessageView; // First: nested.value = 1, nested.back.name = "from_first" let first = Corecursive { @@ -394,14 +407,16 @@ fn test_view_message_field_merge_semantics() { #[test] fn test_view_oneof_message_variant_merge_semantics() { // Same merge semantics for message-typed oneof variants. - use crate::nested::{expr, BinaryOp, Expr, ExprView}; + use crate::nested::oneofs::expr; + use crate::nested::view::ExprView; + use crate::nested::{BinaryOp, Expr}; use buffa::MessageView; // First: binary with lhs set let first = Expr { - kind: Some(expr::KindOneof::from(BinaryOp { + kind: Some(expr::Kind::from(BinaryOp { op: "+".into(), lhs: buffa::MessageField::some(Expr { - kind: Some(expr::KindOneof::IntLiteral(1)), + kind: Some(expr::Kind::IntLiteral(1)), ..Default::default() }), ..Default::default() @@ -410,9 +425,9 @@ fn test_view_oneof_message_variant_merge_semantics() { }; // Second: binary with rhs set (no op, no lhs) let second = Expr { - kind: Some(expr::KindOneof::from(BinaryOp { + kind: Some(expr::Kind::from(BinaryOp { rhs: buffa::MessageField::some(Expr { - kind: Some(expr::KindOneof::IntLiteral(2)), + kind: Some(expr::Kind::IntLiteral(2)), ..Default::default() }), ..Default::default() @@ -424,7 +439,7 @@ fn test_view_oneof_message_variant_merge_semantics() { let view = ExprView::decode_view(&wire).unwrap(); match &view.kind { - Some(expr::KindOneofView::Binary(b)) => { + Some(crate::nested::view::oneofs::expr::Kind::Binary(b)) => { assert_eq!(b.op, "+", "op from first (second didn't set it)"); assert!(b.lhs.is_set(), "lhs from first must survive merge"); assert!(b.rhs.is_set(), "rhs from second"); diff --git a/buffa-test/src/tests/proto2.rs b/buffa-test/src/tests/proto2.rs index c5dc937..76d94dd 100644 --- a/buffa-test/src/tests/proto2.rs +++ b/buffa-test/src/tests/proto2.rs @@ -261,7 +261,8 @@ fn test_proto2_group_wire_format() { #[test] fn test_view_coverage_owned_round_trip() { - use crate::proto2::view_coverage::{ChoiceOneof, Payload}; + use crate::proto2::oneofs::view_coverage::Choice; + use crate::proto2::view_coverage::Payload; use crate::proto2::{Priority, ViewCoverage}; let mut by_id = std::collections::HashMap::new(); @@ -276,7 +277,7 @@ fn test_view_coverage_owned_round_trip() { level: Priority::CRITICAL, by_id, priorities, - choice: Some(ChoiceOneof::Payload(Box::new(Payload { + choice: Some(Choice::Payload(Box::new(Payload { x: Some(42), y: Some("hello".into()), ..Default::default() @@ -291,7 +292,7 @@ fn test_view_coverage_owned_round_trip() { assert_eq!(decoded.priorities.get("low"), Some(&Priority::LOW)); assert_eq!(decoded.priorities.get("high"), Some(&Priority::HIGH)); match decoded.choice { - Some(ChoiceOneof::Payload(p)) => { + Some(Choice::Payload(p)) => { assert_eq!(p.x, Some(42)); assert_eq!(p.y.as_deref(), Some("hello")); } @@ -304,8 +305,10 @@ fn test_view_coverage_via_view() { // View-decode → to_owned_message → encode round-trip. // Exercises: singular closed-enum view type, MapView, // MapView<&str, ClosedEnum>, group-in-oneof view decode + merge. - use crate::proto2::view_coverage::{ChoiceOneof, Payload}; - use crate::proto2::{Priority, ViewCoverage, ViewCoverageView}; + use crate::proto2::oneofs::view_coverage::Choice; + use crate::proto2::view::ViewCoverageView; + use crate::proto2::view_coverage::Payload; + use crate::proto2::{Priority, ViewCoverage}; use buffa::MessageView; let mut by_id = std::collections::HashMap::new(); @@ -318,7 +321,7 @@ fn test_view_coverage_via_view() { level: Priority::HIGH, by_id, priorities, - choice: Some(ChoiceOneof::Payload(Box::new(Payload { + choice: Some(Choice::Payload(Box::new(Payload { x: Some(99), y: Some("world".into()), ..Default::default() @@ -344,7 +347,7 @@ fn test_view_coverage_via_view() { assert_eq!(owned.by_id.get(&7).map(String::as_str), Some("seven")); assert_eq!(owned.priorities.get("med"), Some(&Priority::MEDIUM)); match &owned.choice { - Some(ChoiceOneof::Payload(p)) => { + Some(Choice::Payload(p)) => { assert_eq!(p.x, Some(99)); assert_eq!(p.y.as_deref(), Some("world")); } @@ -359,7 +362,8 @@ fn test_view_coverage_via_view() { fn test_view_coverage_required_enum_default() { // Required closed-enum field defaults to the first enum value (LOW=0). // View decode of empty buffer should also produce the default. - use crate::proto2::{Priority, ViewCoverage, ViewCoverageView}; + use crate::proto2::view::ViewCoverageView; + use crate::proto2::{Priority, ViewCoverage}; use buffa::MessageView; let d = ViewCoverage::default(); @@ -373,14 +377,16 @@ fn test_view_coverage_required_enum_default() { fn test_view_coverage_group_in_oneof_merge() { // Proto spec: same oneof field on the wire twice → merge (for messages/ // groups). Exercises the `_merge_into_view` branch for group-in-oneof. - use crate::proto2::view_coverage::{ChoiceOneof, Payload}; - use crate::proto2::{Priority, ViewCoverage, ViewCoverageView}; + use crate::proto2::oneofs::view_coverage::Choice; + use crate::proto2::view::ViewCoverageView; + use crate::proto2::view_coverage::Payload; + use crate::proto2::{Priority, ViewCoverage}; use buffa::MessageView; // First occurrence: only x set. let first = ViewCoverage { level: Priority::LOW, - choice: Some(ChoiceOneof::Payload(Box::new(Payload { + choice: Some(Choice::Payload(Box::new(Payload { x: Some(1), ..Default::default() }))), @@ -389,7 +395,7 @@ fn test_view_coverage_group_in_oneof_merge() { // Second occurrence: only y set. let second = ViewCoverage { level: Priority::LOW, - choice: Some(ChoiceOneof::Payload(Box::new(Payload { + choice: Some(Choice::Payload(Box::new(Payload { y: Some("merged".into()), ..Default::default() }))), @@ -403,7 +409,7 @@ fn test_view_coverage_group_in_oneof_merge() { let view = ViewCoverageView::decode_view(&wire).unwrap(); let owned = view.to_owned_message(); match owned.choice { - Some(ChoiceOneof::Payload(p)) => { + Some(Choice::Payload(p)) => { // Both x (from first) and y (from second) should be present. assert_eq!(p.x, Some(1), "x should survive merge"); assert_eq!(p.y.as_deref(), Some("merged"), "y should be added by merge"); diff --git a/buffa-test/src/tests/proto3_semantics.rs b/buffa-test/src/tests/proto3_semantics.rs index 13d5b0b..ee80ccf 100644 --- a/buffa-test/src/tests/proto3_semantics.rs +++ b/buffa-test/src/tests/proto3_semantics.rs @@ -9,6 +9,7 @@ //! 4. Packed repeated scalars by default. //! 5. Synthetic oneofs for `optional` fields filtered from user-visible API. +use crate::proto3sem::view::{EnumContextsView, ImplicitScalarsView, OptionalAllTypesView}; use crate::proto3sem::*; use buffa::{EnumValue, Message, MessageView}; @@ -365,13 +366,17 @@ fn enum_unknown_value_preserved_map() { #[test] fn enum_unknown_value_preserved_oneof() { let msg = EnumContexts { - choice: Some(enum_contexts::ChoiceOneof::Picked(EnumValue::Unknown(77))), + choice: Some(crate::proto3sem::oneofs::enum_contexts::Choice::Picked( + EnumValue::Unknown(77), + )), ..Default::default() }; let decoded = round_trip(&msg); assert_eq!( decoded.choice, - Some(enum_contexts::ChoiceOneof::Picked(EnumValue::Unknown(77))) + Some(crate::proto3sem::oneofs::enum_contexts::Choice::Picked( + EnumValue::Unknown(77) + )) ); } diff --git a/buffa-test/src/tests/textproto.rs b/buffa-test/src/tests/textproto.rs index 9970528..b431b22 100644 --- a/buffa-test/src/tests/textproto.rs +++ b/buffa-test/src/tests/textproto.rs @@ -96,7 +96,9 @@ fn person_roundtrip() { ..Default::default() }]; p.maybe_age = Some(30); - p.contact = Some(person::ContactOneof::Email("alice@example.com".into())); + p.contact = Some(crate::basic::oneofs::person::Contact::Email( + "alice@example.com".into(), + )); let text = encode_to_string(&p); let back: Person = decode_from_str(&text).unwrap(); @@ -155,18 +157,25 @@ fn closed_enum_encode_decode() { #[test] fn oneof_variants() { let mut p = Person::default(); - p.contact = Some(person::ContactOneof::Phone("555-1234".into())); + p.contact = Some(crate::basic::oneofs::person::Contact::Phone( + "555-1234".into(), + )); assert_eq!(encode_to_string(&p), r#"phone: "555-1234""#); let p: Person = decode_from_str(r#"email: "x@y.com""#).unwrap(); assert_eq!( p.contact, - Some(person::ContactOneof::Email("x@y.com".into())) + Some(crate::basic::oneofs::person::Contact::Email( + "x@y.com".into() + )) ); // Last-wins when both variants appear (textproto merge semantics). let p: Person = decode_from_str(r#"email: "a" phone: "b""#).unwrap(); - assert_eq!(p.contact, Some(person::ContactOneof::Phone("b".into()))); + assert_eq!( + p.contact, + Some(crate::basic::oneofs::person::Contact::Phone("b".into())) + ); } // ── repeated ──────────────────────────────────────────────────────────────── @@ -339,11 +348,12 @@ fn group_decode_accepts_both_names() { #[test] fn group_in_oneof_uses_type_name() { - use crate::proto2::view_coverage::{ChoiceOneof, Payload}; + use crate::proto2::oneofs::view_coverage::Choice; + use crate::proto2::view_coverage::Payload; use crate::proto2::ViewCoverage; let mut v = ViewCoverage::default(); - v.choice = Some(ChoiceOneof::Payload(Box::new(Payload { + v.choice = Some(Choice::Payload(Box::new(Payload { x: Some(5), ..Default::default() }))); @@ -354,9 +364,9 @@ fn group_in_oneof_uses_type_name() { // Decode accepts both forms. let mut v = ViewCoverage::default(); buffa::text::merge_from_str(&mut v, "Payload { x: 10 }").unwrap(); - assert!(matches!(v.choice, Some(ChoiceOneof::Payload(ref p)) if p.x == Some(10))); + assert!(matches!(v.choice, Some(Choice::Payload(ref p)) if p.x == Some(10))); let mut v = ViewCoverage::default(); buffa::text::merge_from_str(&mut v, "payload { x: 11 }").unwrap(); - assert!(matches!(v.choice, Some(ChoiceOneof::Payload(ref p)) if p.x == Some(11))); + assert!(matches!(v.choice, Some(Choice::Payload(ref p)) if p.x == Some(11))); } diff --git a/buffa-test/src/tests/utf8_validation.rs b/buffa-test/src/tests/utf8_validation.rs index 1243a4e..a1ab6fb 100644 --- a/buffa-test/src/tests/utf8_validation.rs +++ b/buffa-test/src/tests/utf8_validation.rs @@ -1,3 +1,4 @@ +use crate::utf8test::view::StringNoValidationView; use crate::utf8test::*; use buffa::{Message, MessageView}; @@ -63,16 +64,13 @@ fn view_none_accepts_invalid_utf8() { #[test] fn oneof_none_variant_is_vec_u8() { - use oneof_no_validation::ContentOneof; + use crate::utf8test::oneofs::oneof_no_validation::Content; let msg = OneofNoValidation { - content: Some(ContentOneof::RawText(b"bytes".to_vec())), + content: Some(Content::RawText(b"bytes".to_vec())), ..Default::default() }; let decoded = OneofNoValidation::decode_from_slice(&msg.encode_to_vec()).unwrap(); - assert_eq!( - decoded.content, - Some(ContentOneof::RawText(b"bytes".to_vec())) - ); + assert_eq!(decoded.content, Some(Content::RawText(b"bytes".to_vec()))); } #[test] diff --git a/buffa-test/src/tests/view.rs b/buffa-test/src/tests/view.rs index 06fd2ed..094afd7 100644 --- a/buffa-test/src/tests/view.rs +++ b/buffa-test/src/tests/view.rs @@ -1,6 +1,7 @@ //! View type tests: decode_view, MessageFieldView Deref, to_owned_message, //! MapView iteration, oneof views, unknown-field preservation, recursion limit. +use crate::basic::view::{AddressView, EmptyView, InventoryView, PersonView}; use crate::basic::*; use buffa::{Message, MessageView}; @@ -96,11 +97,15 @@ fn test_view_proto3_optional_unset_is_none() { #[test] fn test_view_decodes_oneof() { let mut msg = Person::default(); - msg.contact = Some(person::ContactOneof::Email("bob@example.com".into())); + msg.contact = Some(crate::basic::oneofs::person::Contact::Email( + "bob@example.com".into(), + )); let bytes = msg.encode_to_vec(); let view = PersonView::decode_view(&bytes).expect("decode_view"); match view.contact { - Some(person::ContactOneofView::Email(s)) => assert_eq!(s, "bob@example.com"), + Some(crate::basic::view::oneofs::person::Contact::Email(s)) => { + assert_eq!(s, "bob@example.com") + } other => panic!("expected Email, got {other:?}"), } } @@ -112,7 +117,9 @@ fn test_view_to_owned_roundtrip() { msg.name = "Carol".into(); msg.tags = vec!["x".into(), "y".into()]; msg.maybe_age = Some(30); - msg.contact = Some(person::ContactOneof::Phone("+1-555-0000".into())); + msg.contact = Some(crate::basic::oneofs::person::Contact::Phone( + "+1-555-0000".into(), + )); let bytes = msg.encode_to_vec(); let view = PersonView::decode_view(&bytes).expect("decode_view"); let owned = view.to_owned_message(); @@ -122,7 +129,9 @@ fn test_view_to_owned_roundtrip() { assert_eq!(owned.maybe_age, Some(30)); assert_eq!( owned.contact, - Some(person::ContactOneof::Phone("+1-555-0000".into())) + Some(crate::basic::oneofs::person::Contact::Phone( + "+1-555-0000".into() + )) ); } @@ -316,7 +325,8 @@ fn test_view_map_with_open_enum_value() { #[test] fn test_view_no_unknown_fields_all_scalar_compiles() { - use crate::basic_no_uf::{AllScalars, AllScalarsView, Empty, EmptyView}; + use crate::basic_no_uf::view::{AllScalarsView, EmptyView}; + use crate::basic_no_uf::{AllScalars, Empty}; // EmptyView<'a> has NO fields; AllScalarsView<'a> has only scalars. // Both now carry a PhantomData marker. let e = Empty::default(); diff --git a/buffa-test/src/tests/wkt.rs b/buffa-test/src/tests/wkt.rs index ad8b609..c8b20ad 100644 --- a/buffa-test/src/tests/wkt.rs +++ b/buffa-test/src/tests/wkt.rs @@ -13,7 +13,8 @@ fn test_wkt_in_oneof_from_impls() { // // The fact that this test compiles IS the test: wkt_usage.proto has // Envelope.content with Any/Timestamp (extern) + Event (local) variants. - use crate::wkt::{envelope, Envelope, Event}; + use crate::wkt::oneofs::envelope; + use crate::wkt::{Envelope, Event}; use buffa_types::google::protobuf::Any; // Local variant: both From impls exist. @@ -23,27 +24,27 @@ fn test_wkt_in_oneof_from_impls() { }; assert!(matches!( env.content, - Some(envelope::ContentOneof::EventContent(_)) + Some(envelope::Content::EventContent(_)) )); // Extern variant: only From for Content exists — Some() is explicit. let env = Envelope { - content: Some(envelope::ContentOneof::from(Any::default())), + content: Some(envelope::Content::from(Any::default())), ..Default::default() }; assert!(matches!( env.content, - Some(envelope::ContentOneof::AnyContent(_)) + Some(envelope::Content::AnyContent(_)) )); // The From for Option impl for extern T does NOT exist. // The following would not compile (uncomment to verify): - // let _: Option = Any::default().into(); + // let _: Option = Any::default().into(); let decoded = round_trip(&env); assert!(matches!( decoded.content, - Some(envelope::ContentOneof::AnyContent(_)) + Some(envelope::Content::AnyContent(_)) )); } @@ -85,7 +86,8 @@ fn test_wkt_view_with_extern_path() { // appends "View" to the last path segment: crate::wkt::Timestamp // → crate::wkt::TimestampView (which is re-exported from buffa-types' // generated code). - use crate::wkt::{Event, EventView}; + use crate::wkt::view::EventView; + use crate::wkt::Event; use buffa::MessageView; let msg = Event { created_at: buffa::MessageField::some(buffa_types::google::protobuf::Timestamp { diff --git a/buffa-types/src/generated/google.protobuf.any.__ext.rs b/buffa-types/src/generated/google.protobuf.any.__ext.rs new file mode 100644 index 0000000..8c07180 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.any.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/any.proto + diff --git a/buffa-types/src/generated/google.protobuf.any.__oneofs.rs b/buffa-types/src/generated/google.protobuf.any.__oneofs.rs new file mode 100644 index 0000000..2206a14 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.any.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: google/protobuf/any.proto + diff --git a/buffa-types/src/generated/google.protobuf.any.__view.rs b/buffa-types/src/generated/google.protobuf.any.__view.rs new file mode 100644 index 0000000..3258270 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.any.__view.rs @@ -0,0 +1,244 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents) +// source: google/protobuf/any.proto + +/// `Any` contains an arbitrary serialized protocol buffer message along with a +/// URL that describes the type of the serialized message. +/// +/// Protobuf library provides support to pack/unpack Any values in the form +/// of utility functions or additional generated methods of the Any type. +/// +/// Example 1: Pack and unpack a message in C++. +/// +/// ```text +/// Foo foo = ...; +/// Any any; +/// any.PackFrom(foo); +/// ... +/// if (any.UnpackTo(&foo)) { +/// ... +/// } +/// ``` +/// +/// Example 2: Pack and unpack a message in Java. +/// +/// ```text +/// Foo foo = ...; +/// Any any = Any.pack(foo); +/// ... +/// if (any.is(Foo.class)) { +/// foo = any.unpack(Foo.class); +/// } +/// // or ... +/// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +/// foo = any.unpack(Foo.getDefaultInstance()); +/// } +/// ``` +/// +/// Example 3: Pack and unpack a message in Python. +/// +/// ```text +/// foo = Foo(...) +/// any = Any() +/// any.Pack(foo) +/// ... +/// if any.Is(Foo.DESCRIPTOR): +/// any.Unpack(foo) +/// ... +/// ``` +/// +/// Example 4: Pack and unpack a message in Go +/// +/// ```text +/// foo := &pb.Foo{...} +/// any, err := anypb.New(foo) +/// if err != nil { +/// ... +/// } +/// ... +/// foo := &pb.Foo{} +/// if err := any.UnmarshalTo(foo); err != nil { +/// ... +/// } +/// ``` +/// +/// The pack methods provided by protobuf library will by default use +/// 'type.googleapis.com/full.type.name' as the type URL and the unpack +/// methods only use the fully qualified type name after the last '/' +/// in the type URL, for example "foo.bar.com/x/y.z" will yield type +/// name "y.z". +/// +/// JSON +/// ==== +/// The JSON representation of an `Any` value uses the regular +/// representation of the deserialized, embedded message, with an +/// additional field `@type` which contains the type URL. Example: +/// +/// ```text +/// package google.profile; +/// message Person { +/// string first_name = 1; +/// string last_name = 2; +/// } +/// +/// { +/// "@type": "type.googleapis.com/google.profile.Person", +/// "firstName": , +/// "lastName": +/// } +/// ``` +/// +/// If the embedded message type is well-known and has a custom JSON +/// representation, that representation will be embedded adding a field +/// `value` which holds the custom JSON in addition to the `@type` +/// field. Example (for message \[google.protobuf.Duration\]\[\]): +/// +/// ```text +/// { +/// "@type": "type.googleapis.com/google.protobuf.Duration", +/// "value": "1.212s" +/// } +/// ``` +#[derive(Clone, Debug, Default)] +pub struct AnyView<'a> { + /// A URL/resource name that uniquely identifies the type of the serialized + /// protocol buffer message. This string must contain at least + /// one "/" character. The last segment of the URL's path must represent + /// the fully qualified name of the type (as in + /// `path/google.protobuf.Duration`). The name should be in a canonical form + /// (e.g., leading "." is not accepted). + /// + /// In practice, teams usually precompile into the binary all types that they + /// expect it to use in the context of Any. However, for URLs which use the + /// scheme `http`, `https`, or no scheme, one can optionally set up a type + /// server that maps type URLs to message definitions as follows: + /// + /// * If no scheme is provided, `https` is assumed. + /// * An HTTP GET on the URL must yield a \[google.protobuf.Type\]\[\] + /// value in binary format, or produce an error. + /// * Applications are allowed to cache lookup results based on the + /// URL, or have them precompiled into a binary to avoid any + /// lookup. Therefore, binary compatibility needs to be preserved + /// on changes to types. (Use versioned type names to manage + /// breaking changes.) + /// + /// Note: this functionality is not currently available in the official + /// protobuf release, and it is not used for type URLs beginning with + /// type.googleapis.com. As of May 2023, there are no widely used type server + /// implementations and no plans to implement one. + /// + /// Schemes other than `http`, `https` (or the empty scheme) might be + /// used with implementation specific semantics. + /// + /// Field 1: `type_url` + pub type_url: &'a str, + /// Must be a valid serialized protocol buffer of the above specified type. + /// + /// Field 2: `value` + pub value: &'a [u8], + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> AnyView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + view.type_url = ::buffa::types::borrow_str(&mut cur)?; + } + 2u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 2u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::borrow_bytes(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for AnyView<'a> { + type Owned = super::Any; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::Any { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::Any { + type_url: self.type_url.to_string(), + value: ::bytes::Bytes::copy_from_slice(self.value), + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for AnyView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for AnyView<'a> { + type Static = AnyView<'static>; +} diff --git a/buffa-types/src/generated/google.protobuf.any.__view_oneofs.rs b/buffa-types/src/generated/google.protobuf.any.__view_oneofs.rs new file mode 100644 index 0000000..1bd6c78 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.any.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: google/protobuf/any.proto + diff --git a/buffa-types/src/generated/google.protobuf.any.rs b/buffa-types/src/generated/google.protobuf.any.rs index 0c0d995..7bdcdf3 100644 --- a/buffa-types/src/generated/google.protobuf.any.rs +++ b/buffa-types/src/generated/google.protobuf.any.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/any.proto /// `Any` contains an arbitrary serialized protocol buffer message along with a @@ -266,244 +266,3 @@ pub const __ANY_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa::type_r text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// `Any` contains an arbitrary serialized protocol buffer message along with a -/// URL that describes the type of the serialized message. -/// -/// Protobuf library provides support to pack/unpack Any values in the form -/// of utility functions or additional generated methods of the Any type. -/// -/// Example 1: Pack and unpack a message in C++. -/// -/// ```text -/// Foo foo = ...; -/// Any any; -/// any.PackFrom(foo); -/// ... -/// if (any.UnpackTo(&foo)) { -/// ... -/// } -/// ``` -/// -/// Example 2: Pack and unpack a message in Java. -/// -/// ```text -/// Foo foo = ...; -/// Any any = Any.pack(foo); -/// ... -/// if (any.is(Foo.class)) { -/// foo = any.unpack(Foo.class); -/// } -/// // or ... -/// if (any.isSameTypeAs(Foo.getDefaultInstance())) { -/// foo = any.unpack(Foo.getDefaultInstance()); -/// } -/// ``` -/// -/// Example 3: Pack and unpack a message in Python. -/// -/// ```text -/// foo = Foo(...) -/// any = Any() -/// any.Pack(foo) -/// ... -/// if any.Is(Foo.DESCRIPTOR): -/// any.Unpack(foo) -/// ... -/// ``` -/// -/// Example 4: Pack and unpack a message in Go -/// -/// ```text -/// foo := &pb.Foo{...} -/// any, err := anypb.New(foo) -/// if err != nil { -/// ... -/// } -/// ... -/// foo := &pb.Foo{} -/// if err := any.UnmarshalTo(foo); err != nil { -/// ... -/// } -/// ``` -/// -/// The pack methods provided by protobuf library will by default use -/// 'type.googleapis.com/full.type.name' as the type URL and the unpack -/// methods only use the fully qualified type name after the last '/' -/// in the type URL, for example "foo.bar.com/x/y.z" will yield type -/// name "y.z". -/// -/// JSON -/// ==== -/// The JSON representation of an `Any` value uses the regular -/// representation of the deserialized, embedded message, with an -/// additional field `@type` which contains the type URL. Example: -/// -/// ```text -/// package google.profile; -/// message Person { -/// string first_name = 1; -/// string last_name = 2; -/// } -/// -/// { -/// "@type": "type.googleapis.com/google.profile.Person", -/// "firstName": , -/// "lastName": -/// } -/// ``` -/// -/// If the embedded message type is well-known and has a custom JSON -/// representation, that representation will be embedded adding a field -/// `value` which holds the custom JSON in addition to the `@type` -/// field. Example (for message \[google.protobuf.Duration\]\[\]): -/// -/// ```text -/// { -/// "@type": "type.googleapis.com/google.protobuf.Duration", -/// "value": "1.212s" -/// } -/// ``` -#[derive(Clone, Debug, Default)] -pub struct AnyView<'a> { - /// A URL/resource name that uniquely identifies the type of the serialized - /// protocol buffer message. This string must contain at least - /// one "/" character. The last segment of the URL's path must represent - /// the fully qualified name of the type (as in - /// `path/google.protobuf.Duration`). The name should be in a canonical form - /// (e.g., leading "." is not accepted). - /// - /// In practice, teams usually precompile into the binary all types that they - /// expect it to use in the context of Any. However, for URLs which use the - /// scheme `http`, `https`, or no scheme, one can optionally set up a type - /// server that maps type URLs to message definitions as follows: - /// - /// * If no scheme is provided, `https` is assumed. - /// * An HTTP GET on the URL must yield a \[google.protobuf.Type\]\[\] - /// value in binary format, or produce an error. - /// * Applications are allowed to cache lookup results based on the - /// URL, or have them precompiled into a binary to avoid any - /// lookup. Therefore, binary compatibility needs to be preserved - /// on changes to types. (Use versioned type names to manage - /// breaking changes.) - /// - /// Note: this functionality is not currently available in the official - /// protobuf release, and it is not used for type URLs beginning with - /// type.googleapis.com. As of May 2023, there are no widely used type server - /// implementations and no plans to implement one. - /// - /// Schemes other than `http`, `https` (or the empty scheme) might be - /// used with implementation specific semantics. - /// - /// Field 1: `type_url` - pub type_url: &'a str, - /// Must be a valid serialized protocol buffer of the above specified type. - /// - /// Field 2: `value` - pub value: &'a [u8], - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> AnyView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - view.type_url = ::buffa::types::borrow_str(&mut cur)?; - } - 2u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 2u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::borrow_bytes(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for AnyView<'a> { - type Owned = Any; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> Any { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - Any { - type_url: self.type_url.to_string(), - value: ::bytes::Bytes::copy_from_slice(self.value), - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for AnyView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for AnyView<'a> { - type Static = AnyView<'static>; -} diff --git a/buffa-types/src/generated/google.protobuf.duration.__ext.rs b/buffa-types/src/generated/google.protobuf.duration.__ext.rs new file mode 100644 index 0000000..c49d702 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.duration.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/duration.proto + diff --git a/buffa-types/src/generated/google.protobuf.duration.__oneofs.rs b/buffa-types/src/generated/google.protobuf.duration.__oneofs.rs new file mode 100644 index 0000000..d39b46b --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.duration.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: google/protobuf/duration.proto + diff --git a/buffa-types/src/generated/google.protobuf.duration.__view.rs b/buffa-types/src/generated/google.protobuf.duration.__view.rs new file mode 100644 index 0000000..27eb40e --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.duration.__view.rs @@ -0,0 +1,191 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents) +// source: google/protobuf/duration.proto + +/// A Duration represents a signed, fixed-length span of time represented +/// as a count of seconds and fractions of seconds at nanosecond +/// resolution. It is independent of any calendar and concepts like "day" +/// or "month". It is related to Timestamp in that the difference between +/// two Timestamp values is a Duration and it can be added or subtracted +/// from a Timestamp. Range is approximately +-10,000 years. +/// +/// # Examples +/// +/// Example 1: Compute Duration from two Timestamps in pseudo code. +/// +/// ```text +/// Timestamp start = ...; +/// Timestamp end = ...; +/// Duration duration = ...; +/// +/// duration.seconds = end.seconds - start.seconds; +/// duration.nanos = end.nanos - start.nanos; +/// +/// if (duration.seconds < 0 && duration.nanos > 0) { +/// duration.seconds += 1; +/// duration.nanos -= 1000000000; +/// } else if (duration.seconds > 0 && duration.nanos < 0) { +/// duration.seconds -= 1; +/// duration.nanos += 1000000000; +/// } +/// ``` +/// +/// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. +/// +/// ```text +/// Timestamp start = ...; +/// Duration duration = ...; +/// Timestamp end = ...; +/// +/// end.seconds = start.seconds + duration.seconds; +/// end.nanos = start.nanos + duration.nanos; +/// +/// if (end.nanos < 0) { +/// end.seconds -= 1; +/// end.nanos += 1000000000; +/// } else if (end.nanos >= 1000000000) { +/// end.seconds += 1; +/// end.nanos -= 1000000000; +/// } +/// ``` +/// +/// Example 3: Compute Duration from datetime.timedelta in Python. +/// +/// ```text +/// td = datetime.timedelta(days=3, minutes=10) +/// duration = Duration() +/// duration.FromTimedelta(td) +/// ``` +/// +/// # JSON Mapping +/// +/// In JSON format, the Duration type is encoded as a string rather than an +/// object, where the string ends in the suffix "s" (indicating seconds) and +/// is preceded by the number of seconds, with nanoseconds expressed as +/// fractional seconds. For example, 3 seconds with 0 nanoseconds should be +/// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should +/// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 +/// microsecond should be expressed in JSON format as "3.000001s". +#[derive(Clone, Debug, Default)] +pub struct DurationView<'a> { + /// Signed seconds of the span of time. Must be from -315,576,000,000 + /// to +315,576,000,000 inclusive. Note: these bounds are computed from: + /// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years + /// + /// Field 1: `seconds` + pub seconds: i64, + /// Signed fractions of a second at nanosecond resolution of the span + /// of time. Durations less than one second are represented with a 0 + /// `seconds` field and a positive or negative `nanos` field. For durations + /// of one second or more, a non-zero value for the `nanos` field must be + /// of the same sign as the `seconds` field. Must be from -999,999,999 + /// to +999,999,999 inclusive. + /// + /// Field 2: `nanos` + pub nanos: i32, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> DurationView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.seconds = ::buffa::types::decode_int64(&mut cur)?; + } + 2u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 2u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.nanos = ::buffa::types::decode_int32(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for DurationView<'a> { + type Owned = super::Duration; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::Duration { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::Duration { + seconds: self.seconds, + nanos: self.nanos, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for DurationView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for DurationView<'a> { + type Static = DurationView<'static>; +} diff --git a/buffa-types/src/generated/google.protobuf.duration.__view_oneofs.rs b/buffa-types/src/generated/google.protobuf.duration.__view_oneofs.rs new file mode 100644 index 0000000..9ee4870 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.duration.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: google/protobuf/duration.proto + diff --git a/buffa-types/src/generated/google.protobuf.duration.rs b/buffa-types/src/generated/google.protobuf.duration.rs index fb6cae2..78db9a3 100644 --- a/buffa-types/src/generated/google.protobuf.duration.rs +++ b/buffa-types/src/generated/google.protobuf.duration.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/duration.proto /// A Duration represents a signed, fixed-length span of time represented @@ -241,191 +241,3 @@ pub const __DURATION_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa::t text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// A Duration represents a signed, fixed-length span of time represented -/// as a count of seconds and fractions of seconds at nanosecond -/// resolution. It is independent of any calendar and concepts like "day" -/// or "month". It is related to Timestamp in that the difference between -/// two Timestamp values is a Duration and it can be added or subtracted -/// from a Timestamp. Range is approximately +-10,000 years. -/// -/// # Examples -/// -/// Example 1: Compute Duration from two Timestamps in pseudo code. -/// -/// ```text -/// Timestamp start = ...; -/// Timestamp end = ...; -/// Duration duration = ...; -/// -/// duration.seconds = end.seconds - start.seconds; -/// duration.nanos = end.nanos - start.nanos; -/// -/// if (duration.seconds < 0 && duration.nanos > 0) { -/// duration.seconds += 1; -/// duration.nanos -= 1000000000; -/// } else if (duration.seconds > 0 && duration.nanos < 0) { -/// duration.seconds -= 1; -/// duration.nanos += 1000000000; -/// } -/// ``` -/// -/// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. -/// -/// ```text -/// Timestamp start = ...; -/// Duration duration = ...; -/// Timestamp end = ...; -/// -/// end.seconds = start.seconds + duration.seconds; -/// end.nanos = start.nanos + duration.nanos; -/// -/// if (end.nanos < 0) { -/// end.seconds -= 1; -/// end.nanos += 1000000000; -/// } else if (end.nanos >= 1000000000) { -/// end.seconds += 1; -/// end.nanos -= 1000000000; -/// } -/// ``` -/// -/// Example 3: Compute Duration from datetime.timedelta in Python. -/// -/// ```text -/// td = datetime.timedelta(days=3, minutes=10) -/// duration = Duration() -/// duration.FromTimedelta(td) -/// ``` -/// -/// # JSON Mapping -/// -/// In JSON format, the Duration type is encoded as a string rather than an -/// object, where the string ends in the suffix "s" (indicating seconds) and -/// is preceded by the number of seconds, with nanoseconds expressed as -/// fractional seconds. For example, 3 seconds with 0 nanoseconds should be -/// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should -/// be expressed in JSON format as "3.000000001s", and 3 seconds and 1 -/// microsecond should be expressed in JSON format as "3.000001s". -#[derive(Clone, Debug, Default)] -pub struct DurationView<'a> { - /// Signed seconds of the span of time. Must be from -315,576,000,000 - /// to +315,576,000,000 inclusive. Note: these bounds are computed from: - /// 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years - /// - /// Field 1: `seconds` - pub seconds: i64, - /// Signed fractions of a second at nanosecond resolution of the span - /// of time. Durations less than one second are represented with a 0 - /// `seconds` field and a positive or negative `nanos` field. For durations - /// of one second or more, a non-zero value for the `nanos` field must be - /// of the same sign as the `seconds` field. Must be from -999,999,999 - /// to +999,999,999 inclusive. - /// - /// Field 2: `nanos` - pub nanos: i32, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> DurationView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.seconds = ::buffa::types::decode_int64(&mut cur)?; - } - 2u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 2u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.nanos = ::buffa::types::decode_int32(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for DurationView<'a> { - type Owned = Duration; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> Duration { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - Duration { - seconds: self.seconds, - nanos: self.nanos, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for DurationView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for DurationView<'a> { - type Static = DurationView<'static>; -} diff --git a/buffa-types/src/generated/google.protobuf.empty.__ext.rs b/buffa-types/src/generated/google.protobuf.empty.__ext.rs new file mode 100644 index 0000000..da4c352 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.empty.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/empty.proto + diff --git a/buffa-types/src/generated/google.protobuf.empty.__oneofs.rs b/buffa-types/src/generated/google.protobuf.empty.__oneofs.rs new file mode 100644 index 0000000..4945ad4 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.empty.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: google/protobuf/empty.proto + diff --git a/buffa-types/src/generated/google.protobuf.empty.__view.rs b/buffa-types/src/generated/google.protobuf.empty.__view.rs new file mode 100644 index 0000000..15b810c --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.empty.__view.rs @@ -0,0 +1,99 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents) +// source: google/protobuf/empty.proto + +/// A generic empty message that you can re-use to avoid defining duplicated +/// empty messages in your APIs. +/// +/// Example usage: gRPC uses google.protobuf.Empty as the input and output +/// type for RPCs defined as returning or accepting "nothing": +/// +/// ```text +/// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +/// ``` +#[derive(Clone, Debug, Default)] +pub struct EmptyView<'a> { + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> EmptyView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for EmptyView<'a> { + type Owned = super::Empty; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::Empty { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::Empty { + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for EmptyView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for EmptyView<'a> { + type Static = EmptyView<'static>; +} diff --git a/buffa-types/src/generated/google.protobuf.empty.__view_oneofs.rs b/buffa-types/src/generated/google.protobuf.empty.__view_oneofs.rs new file mode 100644 index 0000000..994e7e2 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.empty.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: google/protobuf/empty.proto + diff --git a/buffa-types/src/generated/google.protobuf.empty.rs b/buffa-types/src/generated/google.protobuf.empty.rs index 2c6b5ce..3dc0e34 100644 --- a/buffa-types/src/generated/google.protobuf.empty.rs +++ b/buffa-types/src/generated/google.protobuf.empty.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/empty.proto /// A generic empty message that you can re-use to avoid defining duplicated @@ -120,99 +120,3 @@ pub const __EMPTY_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa::type text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// A generic empty message that you can re-use to avoid defining duplicated -/// empty messages in your APIs. -/// -/// Example usage: gRPC uses google.protobuf.Empty as the input and output -/// type for RPCs defined as returning or accepting "nothing": -/// -/// ```text -/// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); -/// ``` -#[derive(Clone, Debug, Default)] -pub struct EmptyView<'a> { - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> EmptyView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for EmptyView<'a> { - type Owned = Empty; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> Empty { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - Empty { - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for EmptyView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for EmptyView<'a> { - type Static = EmptyView<'static>; -} diff --git a/buffa-types/src/generated/google.protobuf.field_mask.__ext.rs b/buffa-types/src/generated/google.protobuf.field_mask.__ext.rs new file mode 100644 index 0000000..95417d0 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.field_mask.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/field_mask.proto + diff --git a/buffa-types/src/generated/google.protobuf.field_mask.__oneofs.rs b/buffa-types/src/generated/google.protobuf.field_mask.__oneofs.rs new file mode 100644 index 0000000..745afad --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.field_mask.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: google/protobuf/field_mask.proto + diff --git a/buffa-types/src/generated/google.protobuf.field_mask.__view.rs b/buffa-types/src/generated/google.protobuf.field_mask.__view.rs new file mode 100644 index 0000000..71fa953 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.field_mask.__view.rs @@ -0,0 +1,328 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents) +// source: google/protobuf/field_mask.proto + +/// `FieldMask` represents a set of symbolic field paths, for example: +/// +/// ```text +/// paths: "f.a" +/// paths: "f.b.d" +/// ``` +/// +/// Here `f` represents a field in some root message, `a` and `b` +/// fields in the message found in `f`, and `d` a field found in the +/// message in `f.b`. +/// +/// Field masks are used to specify a subset of fields that should be +/// returned by a get operation or modified by an update operation. +/// Field masks also have a custom JSON encoding (see below). +/// +/// # Field Masks in Projections +/// +/// When used in the context of a projection, a response message or +/// sub-message is filtered by the API to only contain those fields as +/// specified in the mask. For example, if the mask in the previous +/// example is applied to a response message as follows: +/// +/// ```text +/// f { +/// a : 22 +/// b { +/// d : 1 +/// x : 2 +/// } +/// y : 13 +/// } +/// z: 8 +/// ``` +/// +/// The result will not contain specific values for fields x,y and z +/// (their value will be set to the default, and omitted in proto text +/// output): +/// +/// +/// ```text +/// f { +/// a : 22 +/// b { +/// d : 1 +/// } +/// } +/// ``` +/// +/// A repeated field is not allowed except at the last position of a +/// paths string. +/// +/// If a FieldMask object is not present in a get operation, the +/// operation applies to all fields (as if a FieldMask of all fields +/// had been specified). +/// +/// Note that a field mask does not necessarily apply to the +/// top-level response message. In case of a REST get operation, the +/// field mask applies directly to the response, but in case of a REST +/// list operation, the mask instead applies to each individual message +/// in the returned resource list. In case of a REST custom method, +/// other definitions may be used. Where the mask applies will be +/// clearly documented together with its declaration in the API. In +/// any case, the effect on the returned resource/resources is required +/// behavior for APIs. +/// +/// # Field Masks in Update Operations +/// +/// A field mask in update operations specifies which fields of the +/// targeted resource are going to be updated. The API is required +/// to only change the values of the fields as specified in the mask +/// and leave the others untouched. If a resource is passed in to +/// describe the updated values, the API ignores the values of all +/// fields not covered by the mask. +/// +/// If a repeated field is specified for an update operation, new values will +/// be appended to the existing repeated field in the target resource. Note that +/// a repeated field is only allowed in the last position of a `paths` string. +/// +/// If a sub-message is specified in the last position of the field mask for an +/// update operation, then new value will be merged into the existing sub-message +/// in the target resource. +/// +/// For example, given the target message: +/// +/// ```text +/// f { +/// b { +/// d: 1 +/// x: 2 +/// } +/// c: [1] +/// } +/// ``` +/// +/// And an update message: +/// +/// ```text +/// f { +/// b { +/// d: 10 +/// } +/// c: [2] +/// } +/// ``` +/// +/// then if the field mask is: +/// +/// paths: \["f.b", "f.c"\] +/// +/// then the result will be: +/// +/// ```text +/// f { +/// b { +/// d: 10 +/// x: 2 +/// } +/// c: [1, 2] +/// } +/// ``` +/// +/// An implementation may provide options to override this default behavior for +/// repeated and message fields. +/// +/// In order to reset a field's value to the default, the field must +/// be in the mask and set to the default value in the provided resource. +/// Hence, in order to reset all fields of a resource, provide a default +/// instance of the resource and set all fields in the mask, or do +/// not provide a mask as described below. +/// +/// If a field mask is not present on update, the operation applies to +/// all fields (as if a field mask of all fields has been specified). +/// Note that in the presence of schema evolution, this may mean that +/// fields the client does not know and has therefore not filled into +/// the request will be reset to their default. If this is unwanted +/// behavior, a specific service may require a client to always specify +/// a field mask, producing an error if not. +/// +/// As with get operations, the location of the resource which +/// describes the updated values in the request message depends on the +/// operation kind. In any case, the effect of the field mask is +/// required to be honored by the API. +/// +/// ## Considerations for HTTP REST +/// +/// The HTTP kind of an update operation which uses a field mask must +/// be set to PATCH instead of PUT in order to satisfy HTTP semantics +/// (PUT must only be used for full updates). +/// +/// # JSON Encoding of Field Masks +/// +/// In JSON, a field mask is encoded as a single string where paths are +/// separated by a comma. Fields name in each path are converted +/// to/from lower-camel naming conventions. +/// +/// As an example, consider the following message declarations: +/// +/// ```text +/// message Profile { +/// User user = 1; +/// Photo photo = 2; +/// } +/// message User { +/// string display_name = 1; +/// string address = 2; +/// } +/// ``` +/// +/// In proto a field mask for `Profile` may look as such: +/// +/// ```text +/// mask { +/// paths: "user.display_name" +/// paths: "photo" +/// } +/// ``` +/// +/// In JSON, the same mask is represented as below: +/// +/// ```text +/// { +/// mask: "user.displayName,photo" +/// } +/// ``` +/// +/// # Field Masks and Oneof Fields +/// +/// Field masks treat fields in oneofs just as regular fields. Consider the +/// following message: +/// +/// ```text +/// message SampleMessage { +/// oneof test_oneof { +/// string name = 4; +/// SubMessage sub_message = 9; +/// } +/// } +/// ``` +/// +/// The field mask can be: +/// +/// ```text +/// mask { +/// paths: "name" +/// } +/// ``` +/// +/// Or: +/// +/// ```text +/// mask { +/// paths: "sub_message" +/// } +/// ``` +/// +/// Note that oneof type names ("test_oneof" in this case) cannot be used in +/// paths. +/// +/// ## Field Mask Verification +/// +/// The implementation of any API method which has a FieldMask type field in the +/// request should verify the included field paths, and return an +/// `INVALID_ARGUMENT` error if any path is unmappable. +#[derive(Clone, Debug, Default)] +pub struct FieldMaskView<'a> { + /// The set of field mask paths. + /// + /// Field 1: `paths` + pub paths: ::buffa::RepeatedView<'a, &'a str>, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> FieldMaskView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + view.paths.push(::buffa::types::borrow_str(&mut cur)?); + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for FieldMaskView<'a> { + type Owned = super::FieldMask; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::FieldMask { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::FieldMask { + paths: self.paths.iter().map(|s| s.to_string()).collect(), + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for FieldMaskView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for FieldMaskView<'a> { + type Static = FieldMaskView<'static>; +} diff --git a/buffa-types/src/generated/google.protobuf.field_mask.__view_oneofs.rs b/buffa-types/src/generated/google.protobuf.field_mask.__view_oneofs.rs new file mode 100644 index 0000000..713ef11 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.field_mask.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: google/protobuf/field_mask.proto + diff --git a/buffa-types/src/generated/google.protobuf.field_mask.rs b/buffa-types/src/generated/google.protobuf.field_mask.rs index 2d9ef78..05ca092 100644 --- a/buffa-types/src/generated/google.protobuf.field_mask.rs +++ b/buffa-types/src/generated/google.protobuf.field_mask.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/field_mask.proto /// `FieldMask` represents a set of symbolic field paths, for example: @@ -370,328 +370,3 @@ pub const __FIELD_MASK_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa: text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// `FieldMask` represents a set of symbolic field paths, for example: -/// -/// ```text -/// paths: "f.a" -/// paths: "f.b.d" -/// ``` -/// -/// Here `f` represents a field in some root message, `a` and `b` -/// fields in the message found in `f`, and `d` a field found in the -/// message in `f.b`. -/// -/// Field masks are used to specify a subset of fields that should be -/// returned by a get operation or modified by an update operation. -/// Field masks also have a custom JSON encoding (see below). -/// -/// # Field Masks in Projections -/// -/// When used in the context of a projection, a response message or -/// sub-message is filtered by the API to only contain those fields as -/// specified in the mask. For example, if the mask in the previous -/// example is applied to a response message as follows: -/// -/// ```text -/// f { -/// a : 22 -/// b { -/// d : 1 -/// x : 2 -/// } -/// y : 13 -/// } -/// z: 8 -/// ``` -/// -/// The result will not contain specific values for fields x,y and z -/// (their value will be set to the default, and omitted in proto text -/// output): -/// -/// -/// ```text -/// f { -/// a : 22 -/// b { -/// d : 1 -/// } -/// } -/// ``` -/// -/// A repeated field is not allowed except at the last position of a -/// paths string. -/// -/// If a FieldMask object is not present in a get operation, the -/// operation applies to all fields (as if a FieldMask of all fields -/// had been specified). -/// -/// Note that a field mask does not necessarily apply to the -/// top-level response message. In case of a REST get operation, the -/// field mask applies directly to the response, but in case of a REST -/// list operation, the mask instead applies to each individual message -/// in the returned resource list. In case of a REST custom method, -/// other definitions may be used. Where the mask applies will be -/// clearly documented together with its declaration in the API. In -/// any case, the effect on the returned resource/resources is required -/// behavior for APIs. -/// -/// # Field Masks in Update Operations -/// -/// A field mask in update operations specifies which fields of the -/// targeted resource are going to be updated. The API is required -/// to only change the values of the fields as specified in the mask -/// and leave the others untouched. If a resource is passed in to -/// describe the updated values, the API ignores the values of all -/// fields not covered by the mask. -/// -/// If a repeated field is specified for an update operation, new values will -/// be appended to the existing repeated field in the target resource. Note that -/// a repeated field is only allowed in the last position of a `paths` string. -/// -/// If a sub-message is specified in the last position of the field mask for an -/// update operation, then new value will be merged into the existing sub-message -/// in the target resource. -/// -/// For example, given the target message: -/// -/// ```text -/// f { -/// b { -/// d: 1 -/// x: 2 -/// } -/// c: [1] -/// } -/// ``` -/// -/// And an update message: -/// -/// ```text -/// f { -/// b { -/// d: 10 -/// } -/// c: [2] -/// } -/// ``` -/// -/// then if the field mask is: -/// -/// paths: \["f.b", "f.c"\] -/// -/// then the result will be: -/// -/// ```text -/// f { -/// b { -/// d: 10 -/// x: 2 -/// } -/// c: [1, 2] -/// } -/// ``` -/// -/// An implementation may provide options to override this default behavior for -/// repeated and message fields. -/// -/// In order to reset a field's value to the default, the field must -/// be in the mask and set to the default value in the provided resource. -/// Hence, in order to reset all fields of a resource, provide a default -/// instance of the resource and set all fields in the mask, or do -/// not provide a mask as described below. -/// -/// If a field mask is not present on update, the operation applies to -/// all fields (as if a field mask of all fields has been specified). -/// Note that in the presence of schema evolution, this may mean that -/// fields the client does not know and has therefore not filled into -/// the request will be reset to their default. If this is unwanted -/// behavior, a specific service may require a client to always specify -/// a field mask, producing an error if not. -/// -/// As with get operations, the location of the resource which -/// describes the updated values in the request message depends on the -/// operation kind. In any case, the effect of the field mask is -/// required to be honored by the API. -/// -/// ## Considerations for HTTP REST -/// -/// The HTTP kind of an update operation which uses a field mask must -/// be set to PATCH instead of PUT in order to satisfy HTTP semantics -/// (PUT must only be used for full updates). -/// -/// # JSON Encoding of Field Masks -/// -/// In JSON, a field mask is encoded as a single string where paths are -/// separated by a comma. Fields name in each path are converted -/// to/from lower-camel naming conventions. -/// -/// As an example, consider the following message declarations: -/// -/// ```text -/// message Profile { -/// User user = 1; -/// Photo photo = 2; -/// } -/// message User { -/// string display_name = 1; -/// string address = 2; -/// } -/// ``` -/// -/// In proto a field mask for `Profile` may look as such: -/// -/// ```text -/// mask { -/// paths: "user.display_name" -/// paths: "photo" -/// } -/// ``` -/// -/// In JSON, the same mask is represented as below: -/// -/// ```text -/// { -/// mask: "user.displayName,photo" -/// } -/// ``` -/// -/// # Field Masks and Oneof Fields -/// -/// Field masks treat fields in oneofs just as regular fields. Consider the -/// following message: -/// -/// ```text -/// message SampleMessage { -/// oneof test_oneof { -/// string name = 4; -/// SubMessage sub_message = 9; -/// } -/// } -/// ``` -/// -/// The field mask can be: -/// -/// ```text -/// mask { -/// paths: "name" -/// } -/// ``` -/// -/// Or: -/// -/// ```text -/// mask { -/// paths: "sub_message" -/// } -/// ``` -/// -/// Note that oneof type names ("test_oneof" in this case) cannot be used in -/// paths. -/// -/// ## Field Mask Verification -/// -/// The implementation of any API method which has a FieldMask type field in the -/// request should verify the included field paths, and return an -/// `INVALID_ARGUMENT` error if any path is unmappable. -#[derive(Clone, Debug, Default)] -pub struct FieldMaskView<'a> { - /// The set of field mask paths. - /// - /// Field 1: `paths` - pub paths: ::buffa::RepeatedView<'a, &'a str>, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> FieldMaskView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - view.paths.push(::buffa::types::borrow_str(&mut cur)?); - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for FieldMaskView<'a> { - type Owned = FieldMask; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> FieldMask { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - FieldMask { - paths: self.paths.iter().map(|s| s.to_string()).collect(), - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for FieldMaskView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for FieldMaskView<'a> { - type Static = FieldMaskView<'static>; -} diff --git a/buffa-types/src/generated/google.protobuf.struct.__ext.rs b/buffa-types/src/generated/google.protobuf.struct.__ext.rs new file mode 100644 index 0000000..de93394 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.struct.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/struct.proto + diff --git a/buffa-types/src/generated/google.protobuf.struct.__oneofs.rs b/buffa-types/src/generated/google.protobuf.struct.__oneofs.rs new file mode 100644 index 0000000..0ecec33 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.struct.__oneofs.rs @@ -0,0 +1,39 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents) +// source: google/protobuf/struct.proto + +pub mod value { + #[allow(unused_imports)] + use super::*; + /// The kind of value. + #[derive(Clone, PartialEq, Debug)] + #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] + pub enum Kind { + NullValue(::buffa::EnumValue), + NumberValue(f64), + StringValue(::buffa::alloc::string::String), + BoolValue(bool), + StructValue(::buffa::alloc::boxed::Box), + ListValue(::buffa::alloc::boxed::Box), + } + impl ::buffa::Oneof for Kind {} + impl From for Kind { + fn from(v: super::super::Struct) -> Self { + Self::StructValue(::buffa::alloc::boxed::Box::new(v)) + } + } + impl From for ::core::option::Option { + fn from(v: super::super::Struct) -> Self { + Self::Some(Kind::from(v)) + } + } + impl From for Kind { + fn from(v: super::super::ListValue) -> Self { + Self::ListValue(::buffa::alloc::boxed::Box::new(v)) + } + } + impl From for ::core::option::Option { + fn from(v: super::super::ListValue) -> Self { + Self::Some(Kind::from(v)) + } + } +} diff --git a/buffa-types/src/generated/google.protobuf.struct.__view.rs b/buffa-types/src/generated/google.protobuf.struct.__view.rs new file mode 100644 index 0000000..5da5673 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.struct.__view.rs @@ -0,0 +1,501 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents) +// source: google/protobuf/struct.proto + +/// `Struct` represents a structured data value, consisting of fields +/// which map to dynamically typed values. In some languages, `Struct` +/// might be supported by a native representation. For example, in +/// scripting languages like JS a struct is represented as an +/// object. The details of that representation are described together +/// with the proto support for the language. +/// +/// The JSON representation for `Struct` is JSON object. +#[derive(Clone, Debug, Default)] +pub struct StructView<'a> { + /// Unordered map of dynamically typed values. + /// + /// Field 1: `fields` (map) + pub fields: ::buffa::MapView<'a, &'a str, ValueView<'a>>, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> StructView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + let entry_bytes = ::buffa::types::borrow_bytes(&mut cur)?; + let mut entry_cur: &'a [u8] = entry_bytes; + let mut key = ""; + let mut val = ::core::default::Default::default(); + while !entry_cur.is_empty() { + let entry_tag = ::buffa::encoding::Tag::decode(&mut entry_cur)?; + match entry_tag.field_number() { + 1 => { + if entry_tag.wire_type() + != ::buffa::encoding::WireType::LengthDelimited + { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: entry_tag.field_number(), + expected: 2u8, + actual: entry_tag.wire_type() as u8, + }); + } + key = ::buffa::types::borrow_str(&mut entry_cur)?; + } + 2 => { + if entry_tag.wire_type() + != ::buffa::encoding::WireType::LengthDelimited + { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: entry_tag.field_number(), + expected: 2u8, + actual: entry_tag.wire_type() as u8, + }); + } + if depth == 0 { + return Err(::buffa::DecodeError::RecursionLimitExceeded); + } + let sub = ::buffa::types::borrow_bytes(&mut entry_cur)?; + val = ValueView::_decode_depth(sub, depth - 1)?; + } + _ => { + ::buffa::encoding::skip_field_depth( + entry_tag, + &mut entry_cur, + depth, + )?; + } + } + } + view.fields.push(key, val); + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for StructView<'a> { + type Owned = super::Struct; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::Struct { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::Struct { + fields: self + .fields + .iter() + .map(|(k, v)| (k.to_string(), v.to_owned_message())) + .collect(), + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for StructView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for StructView<'a> { + type Static = StructView<'static>; +} +/// `Value` represents a dynamically typed value which can be either +/// null, a number, a string, a boolean, a recursive struct value, or a +/// list of values. A producer of value is expected to set one of these +/// variants. Absence of any variant indicates an error. +/// +/// The JSON representation for `Value` is JSON value. +#[derive(Clone, Debug, Default)] +pub struct ValueView<'a> { + pub kind: ::core::option::Option>, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> ValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.kind = Some( + oneofs::value::Kind::NullValue( + ::buffa::EnumValue::from( + ::buffa::types::decode_int32(&mut cur)?, + ), + ), + ); + } + 2u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Fixed64 { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 2u32, + expected: 1u8, + actual: tag.wire_type() as u8, + }); + } + view.kind = Some( + oneofs::value::Kind::NumberValue( + ::buffa::types::decode_double(&mut cur)?, + ), + ); + } + 3u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 3u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + view.kind = Some( + oneofs::value::Kind::StringValue( + ::buffa::types::borrow_str(&mut cur)?, + ), + ); + } + 4u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 4u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.kind = Some( + oneofs::value::Kind::BoolValue( + ::buffa::types::decode_bool(&mut cur)?, + ), + ); + } + 5u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 5u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + if depth == 0 { + return Err(::buffa::DecodeError::RecursionLimitExceeded); + } + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + if let Some(oneofs::value::Kind::StructValue(ref mut existing)) = view + .kind + { + existing._merge_into_view(sub, depth - 1)?; + } else { + view.kind = Some( + oneofs::value::Kind::StructValue( + ::buffa::alloc::boxed::Box::new( + StructView::_decode_depth(sub, depth - 1)?, + ), + ), + ); + } + } + 6u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 6u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + if depth == 0 { + return Err(::buffa::DecodeError::RecursionLimitExceeded); + } + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + if let Some(oneofs::value::Kind::ListValue(ref mut existing)) = view + .kind + { + existing._merge_into_view(sub, depth - 1)?; + } else { + view.kind = Some( + oneofs::value::Kind::ListValue( + ::buffa::alloc::boxed::Box::new( + ListValueView::_decode_depth(sub, depth - 1)?, + ), + ), + ); + } + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for ValueView<'a> { + type Owned = super::Value; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::Value { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::Value { + kind: self + .kind + .as_ref() + .map(|v| match v { + oneofs::value::Kind::NullValue(v) => { + super::oneofs::value::Kind::NullValue(*v) + } + oneofs::value::Kind::NumberValue(v) => { + super::oneofs::value::Kind::NumberValue(*v) + } + oneofs::value::Kind::StringValue(v) => { + super::oneofs::value::Kind::StringValue(v.to_string()) + } + oneofs::value::Kind::BoolValue(v) => { + super::oneofs::value::Kind::BoolValue(*v) + } + oneofs::value::Kind::StructValue(v) => { + super::oneofs::value::Kind::StructValue( + ::buffa::alloc::boxed::Box::new(v.to_owned_message()), + ) + } + oneofs::value::Kind::ListValue(v) => { + super::oneofs::value::Kind::ListValue( + ::buffa::alloc::boxed::Box::new(v.to_owned_message()), + ) + } + }), + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for ValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for ValueView<'a> { + type Static = ValueView<'static>; +} +/// `ListValue` is a wrapper around a repeated field of values. +/// +/// The JSON representation for `ListValue` is JSON array. +#[derive(Clone, Debug, Default)] +pub struct ListValueView<'a> { + /// Repeated field of dynamically typed values. + /// + /// Field 1: `values` + pub values: ::buffa::RepeatedView<'a, ValueView<'a>>, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> ListValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + if depth == 0 { + return Err(::buffa::DecodeError::RecursionLimitExceeded); + } + let sub = ::buffa::types::borrow_bytes(&mut cur)?; + view.values.push(ValueView::_decode_depth(sub, depth - 1)?); + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for ListValueView<'a> { + type Owned = super::ListValue; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::ListValue { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::ListValue { + values: self.values.iter().map(|v| v.to_owned_message()).collect(), + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for ListValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for ListValueView<'a> { + type Static = ListValueView<'static>; +} diff --git a/buffa-types/src/generated/google.protobuf.struct.__view_oneofs.rs b/buffa-types/src/generated/google.protobuf.struct.__view_oneofs.rs new file mode 100644 index 0000000..df6e60a --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.struct.__view_oneofs.rs @@ -0,0 +1,16 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents) +// source: google/protobuf/struct.proto + +pub mod value { + #[allow(unused_imports)] + use super::*; + #[derive(Clone, Debug)] + pub enum Kind<'a> { + NullValue(::buffa::EnumValue), + NumberValue(f64), + StringValue(&'a str), + BoolValue(bool), + StructValue(::buffa::alloc::boxed::Box>), + ListValue(::buffa::alloc::boxed::Box>), + } +} diff --git a/buffa-types/src/generated/google.protobuf.struct.rs b/buffa-types/src/generated/google.protobuf.struct.rs index 23ac12d..1f00758 100644 --- a/buffa-types/src/generated/google.protobuf.struct.rs +++ b/buffa-types/src/generated/google.protobuf.struct.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/struct.proto /// `NullValue` is a singleton enumeration to represent the null value for the @@ -319,164 +319,6 @@ pub const __STRUCT_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa::typ text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// `Struct` represents a structured data value, consisting of fields -/// which map to dynamically typed values. In some languages, `Struct` -/// might be supported by a native representation. For example, in -/// scripting languages like JS a struct is represented as an -/// object. The details of that representation are described together -/// with the proto support for the language. -/// -/// The JSON representation for `Struct` is JSON object. -#[derive(Clone, Debug, Default)] -pub struct StructView<'a> { - /// Unordered map of dynamically typed values. - /// - /// Field 1: `fields` (map) - pub fields: ::buffa::MapView<'a, &'a str, ValueView<'a>>, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> StructView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - let entry_bytes = ::buffa::types::borrow_bytes(&mut cur)?; - let mut entry_cur: &'a [u8] = entry_bytes; - let mut key = ""; - let mut val = ::core::default::Default::default(); - while !entry_cur.is_empty() { - let entry_tag = ::buffa::encoding::Tag::decode(&mut entry_cur)?; - match entry_tag.field_number() { - 1 => { - if entry_tag.wire_type() - != ::buffa::encoding::WireType::LengthDelimited - { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: entry_tag.field_number(), - expected: 2u8, - actual: entry_tag.wire_type() as u8, - }); - } - key = ::buffa::types::borrow_str(&mut entry_cur)?; - } - 2 => { - if entry_tag.wire_type() - != ::buffa::encoding::WireType::LengthDelimited - { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: entry_tag.field_number(), - expected: 2u8, - actual: entry_tag.wire_type() as u8, - }); - } - if depth == 0 { - return Err(::buffa::DecodeError::RecursionLimitExceeded); - } - let sub = ::buffa::types::borrow_bytes(&mut entry_cur)?; - val = ValueView::_decode_depth(sub, depth - 1)?; - } - _ => { - ::buffa::encoding::skip_field_depth( - entry_tag, - &mut entry_cur, - depth, - )?; - } - } - } - view.fields.push(key, val); - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for StructView<'a> { - type Owned = Struct; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> Struct { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - Struct { - fields: self - .fields - .iter() - .map(|(k, v)| (k.to_string(), v.to_owned_message())) - .collect(), - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for StructView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for StructView<'a> { - type Static = StructView<'static>; -} /// `Value` represents a dynamically typed value which can be either /// null, a number, a string, a boolean, a recursive struct value, or a /// list of values. A producer of value is expected to set one of these @@ -486,7 +328,7 @@ unsafe impl<'a> ::buffa::HasDefaultViewInstance for StructView<'a> { #[derive(Clone, PartialEq, Default)] #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] pub struct Value { - pub kind: Option, + pub kind: Option, #[doc(hidden)] pub __buffa_unknown_fields: ::buffa::UnknownFields, #[doc(hidden)] @@ -522,25 +364,25 @@ impl ::buffa::Message for Value { let mut size = 0u32; if let ::core::option::Option::Some(ref v) = self.kind { match v { - value::KindOneof::NullValue(x) => { + oneofs::value::Kind::NullValue(x) => { size += 1u32 + ::buffa::types::int32_encoded_len(x.to_i32()) as u32; } - value::KindOneof::NumberValue(_x) => { + oneofs::value::Kind::NumberValue(_x) => { size += 1u32 + ::buffa::types::FIXED64_ENCODED_LEN as u32; } - value::KindOneof::StringValue(x) => { + oneofs::value::Kind::StringValue(x) => { size += 1u32 + ::buffa::types::string_encoded_len(x) as u32; } - value::KindOneof::BoolValue(_x) => { + oneofs::value::Kind::BoolValue(_x) => { size += 1u32 + ::buffa::types::BOOL_ENCODED_LEN as u32; } - value::KindOneof::StructValue(x) => { + oneofs::value::Kind::StructValue(x) => { let inner = x.compute_size(); size += 1u32 + ::buffa::encoding::varint_len(inner as u64) as u32 + inner; } - value::KindOneof::ListValue(x) => { + oneofs::value::Kind::ListValue(x) => { let inner = x.compute_size(); size += 1u32 + ::buffa::encoding::varint_len(inner as u64) as u32 @@ -557,7 +399,7 @@ impl ::buffa::Message for Value { use ::buffa::Enumeration as _; if let ::core::option::Option::Some(ref v) = self.kind { match v { - value::KindOneof::NullValue(x) => { + oneofs::value::Kind::NullValue(x) => { ::buffa::encoding::Tag::new( 1u32, ::buffa::encoding::WireType::Varint, @@ -565,7 +407,7 @@ impl ::buffa::Message for Value { .encode(buf); ::buffa::types::encode_int32(x.to_i32(), buf); } - value::KindOneof::NumberValue(x) => { + oneofs::value::Kind::NumberValue(x) => { ::buffa::encoding::Tag::new( 2u32, ::buffa::encoding::WireType::Fixed64, @@ -573,7 +415,7 @@ impl ::buffa::Message for Value { .encode(buf); ::buffa::types::encode_double(*x, buf); } - value::KindOneof::StringValue(x) => { + oneofs::value::Kind::StringValue(x) => { ::buffa::encoding::Tag::new( 3u32, ::buffa::encoding::WireType::LengthDelimited, @@ -581,7 +423,7 @@ impl ::buffa::Message for Value { .encode(buf); ::buffa::types::encode_string(x, buf); } - value::KindOneof::BoolValue(x) => { + oneofs::value::Kind::BoolValue(x) => { ::buffa::encoding::Tag::new( 4u32, ::buffa::encoding::WireType::Varint, @@ -589,7 +431,7 @@ impl ::buffa::Message for Value { .encode(buf); ::buffa::types::encode_bool(*x, buf); } - value::KindOneof::StructValue(x) => { + oneofs::value::Kind::StructValue(x) => { ::buffa::encoding::Tag::new( 5u32, ::buffa::encoding::WireType::LengthDelimited, @@ -598,7 +440,7 @@ impl ::buffa::Message for Value { ::buffa::encoding::encode_varint(x.cached_size() as u64, buf); x.write_to(buf); } - value::KindOneof::ListValue(x) => { + oneofs::value::Kind::ListValue(x) => { ::buffa::encoding::Tag::new( 6u32, ::buffa::encoding::WireType::LengthDelimited, @@ -631,7 +473,7 @@ impl ::buffa::Message for Value { }); } self.kind = ::core::option::Option::Some( - value::KindOneof::NullValue( + oneofs::value::Kind::NullValue( ::buffa::EnumValue::from(::buffa::types::decode_int32(buf)?), ), ); @@ -645,7 +487,7 @@ impl ::buffa::Message for Value { }); } self.kind = ::core::option::Option::Some( - value::KindOneof::NumberValue(::buffa::types::decode_double(buf)?), + oneofs::value::Kind::NumberValue(::buffa::types::decode_double(buf)?), ); } 3u32 => { @@ -657,7 +499,7 @@ impl ::buffa::Message for Value { }); } self.kind = ::core::option::Option::Some( - value::KindOneof::StringValue(::buffa::types::decode_string(buf)?), + oneofs::value::Kind::StringValue(::buffa::types::decode_string(buf)?), ); } 4u32 => { @@ -669,7 +511,7 @@ impl ::buffa::Message for Value { }); } self.kind = ::core::option::Option::Some( - value::KindOneof::BoolValue(::buffa::types::decode_bool(buf)?), + oneofs::value::Kind::BoolValue(::buffa::types::decode_bool(buf)?), ); } 5u32 => { @@ -681,7 +523,7 @@ impl ::buffa::Message for Value { }); } if let ::core::option::Option::Some( - value::KindOneof::StructValue(ref mut existing), + oneofs::value::Kind::StructValue(ref mut existing), ) = self.kind { ::buffa::Message::merge_length_delimited( @@ -693,7 +535,7 @@ impl ::buffa::Message for Value { let mut val = ::core::default::Default::default(); ::buffa::Message::merge_length_delimited(&mut val, buf, depth)?; self.kind = ::core::option::Option::Some( - value::KindOneof::StructValue( + oneofs::value::Kind::StructValue( ::buffa::alloc::boxed::Box::new(val), ), ); @@ -708,7 +550,7 @@ impl ::buffa::Message for Value { }); } if let ::core::option::Option::Some( - value::KindOneof::ListValue(ref mut existing), + oneofs::value::Kind::ListValue(ref mut existing), ) = self.kind { ::buffa::Message::merge_length_delimited( @@ -720,7 +562,9 @@ impl ::buffa::Message for Value { let mut val = ::core::default::Default::default(); ::buffa::Message::merge_length_delimited(&mut val, buf, depth)?; self.kind = ::core::option::Option::Some( - value::KindOneof::ListValue(::buffa::alloc::boxed::Box::new(val)), + oneofs::value::Kind::ListValue( + ::buffa::alloc::boxed::Box::new(val), + ), ); } } @@ -758,7 +602,7 @@ impl ::buffa::text::TextFormat for Value { use ::buffa::Enumeration as _; if let ::core::option::Option::Some(ref __v) = self.kind { match __v { - value::KindOneof::NullValue(__v) => { + oneofs::value::Kind::NullValue(__v) => { enc.write_field_name("null_value")?; match __v { ::buffa::EnumValue::Known(__e) => { @@ -767,23 +611,23 @@ impl ::buffa::text::TextFormat for Value { ::buffa::EnumValue::Unknown(__n) => enc.write_enum_number(*__n)?, } } - value::KindOneof::NumberValue(__v) => { + oneofs::value::Kind::NumberValue(__v) => { enc.write_field_name("number_value")?; enc.write_f64(*__v)?; } - value::KindOneof::StringValue(__v) => { + oneofs::value::Kind::StringValue(__v) => { enc.write_field_name("string_value")?; enc.write_string(__v)?; } - value::KindOneof::BoolValue(__v) => { + oneofs::value::Kind::BoolValue(__v) => { enc.write_field_name("bool_value")?; enc.write_bool(*__v)?; } - value::KindOneof::StructValue(__v) => { + oneofs::value::Kind::StructValue(__v) => { enc.write_field_name("struct_value")?; enc.write_message(&**__v)?; } - value::KindOneof::ListValue(__v) => { + oneofs::value::Kind::ListValue(__v) => { enc.write_field_name("list_value")?; enc.write_message(&**__v)?; } @@ -802,7 +646,7 @@ impl ::buffa::text::TextFormat for Value { match __name { "null_value" => { self.kind = ::core::option::Option::Some( - value::KindOneof::NullValue( + oneofs::value::Kind::NullValue( dec .read_enum_by_name::() .map(::buffa::EnumValue::from)?, @@ -811,22 +655,22 @@ impl ::buffa::text::TextFormat for Value { } "number_value" => { self.kind = ::core::option::Option::Some( - value::KindOneof::NumberValue(dec.read_f64()?), + oneofs::value::Kind::NumberValue(dec.read_f64()?), ); } "string_value" => { self.kind = ::core::option::Option::Some( - value::KindOneof::StringValue(dec.read_string()?.into_owned()), + oneofs::value::Kind::StringValue(dec.read_string()?.into_owned()), ); } "bool_value" => { self.kind = ::core::option::Option::Some( - value::KindOneof::BoolValue(dec.read_bool()?), + oneofs::value::Kind::BoolValue(dec.read_bool()?), ); } "struct_value" => { if let ::core::option::Option::Some( - value::KindOneof::StructValue(ref mut __existing), + oneofs::value::Kind::StructValue(ref mut __existing), ) = self.kind { dec.merge_message(&mut **__existing)?; @@ -834,7 +678,7 @@ impl ::buffa::text::TextFormat for Value { let mut __m = ::core::default::Default::default(); dec.merge_message(&mut __m)?; self.kind = ::core::option::Option::Some( - value::KindOneof::StructValue( + oneofs::value::Kind::StructValue( ::buffa::alloc::boxed::Box::new(__m), ), ); @@ -842,7 +686,7 @@ impl ::buffa::text::TextFormat for Value { } "list_value" => { if let ::core::option::Option::Some( - value::KindOneof::ListValue(ref mut __existing), + oneofs::value::Kind::ListValue(ref mut __existing), ) = self.kind { dec.merge_message(&mut **__existing)?; @@ -850,7 +694,7 @@ impl ::buffa::text::TextFormat for Value { let mut __m = ::core::default::Default::default(); dec.merge_message(&mut __m)?; self.kind = ::core::option::Option::Some( - value::KindOneof::ListValue( + oneofs::value::Kind::ListValue( ::buffa::alloc::boxed::Box::new(__m), ), ); @@ -868,278 +712,6 @@ pub const __VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa::type text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// `Value` represents a dynamically typed value which can be either -/// null, a number, a string, a boolean, a recursive struct value, or a -/// list of values. A producer of value is expected to set one of these -/// variants. Absence of any variant indicates an error. -/// -/// The JSON representation for `Value` is JSON value. -#[derive(Clone, Debug, Default)] -pub struct ValueView<'a> { - pub kind: ::core::option::Option>, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> ValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.kind = Some( - value::KindOneofView::NullValue( - ::buffa::EnumValue::from( - ::buffa::types::decode_int32(&mut cur)?, - ), - ), - ); - } - 2u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Fixed64 { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 2u32, - expected: 1u8, - actual: tag.wire_type() as u8, - }); - } - view.kind = Some( - value::KindOneofView::NumberValue( - ::buffa::types::decode_double(&mut cur)?, - ), - ); - } - 3u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 3u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - view.kind = Some( - value::KindOneofView::StringValue( - ::buffa::types::borrow_str(&mut cur)?, - ), - ); - } - 4u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 4u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.kind = Some( - value::KindOneofView::BoolValue( - ::buffa::types::decode_bool(&mut cur)?, - ), - ); - } - 5u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 5u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - if depth == 0 { - return Err(::buffa::DecodeError::RecursionLimitExceeded); - } - let sub = ::buffa::types::borrow_bytes(&mut cur)?; - if let Some(value::KindOneofView::StructValue(ref mut existing)) = view - .kind - { - existing._merge_into_view(sub, depth - 1)?; - } else { - view.kind = Some( - value::KindOneofView::StructValue( - ::buffa::alloc::boxed::Box::new( - StructView::_decode_depth(sub, depth - 1)?, - ), - ), - ); - } - } - 6u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 6u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - if depth == 0 { - return Err(::buffa::DecodeError::RecursionLimitExceeded); - } - let sub = ::buffa::types::borrow_bytes(&mut cur)?; - if let Some(value::KindOneofView::ListValue(ref mut existing)) = view - .kind - { - existing._merge_into_view(sub, depth - 1)?; - } else { - view.kind = Some( - value::KindOneofView::ListValue( - ::buffa::alloc::boxed::Box::new( - ListValueView::_decode_depth(sub, depth - 1)?, - ), - ), - ); - } - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for ValueView<'a> { - type Owned = Value; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> Value { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - Value { - kind: self - .kind - .as_ref() - .map(|v| match v { - value::KindOneofView::NullValue(v) => value::KindOneof::NullValue(*v), - value::KindOneofView::NumberValue(v) => { - value::KindOneof::NumberValue(*v) - } - value::KindOneofView::StringValue(v) => { - value::KindOneof::StringValue(v.to_string()) - } - value::KindOneofView::BoolValue(v) => value::KindOneof::BoolValue(*v), - value::KindOneofView::StructValue(v) => { - value::KindOneof::StructValue( - ::buffa::alloc::boxed::Box::new(v.to_owned_message()), - ) - } - value::KindOneofView::ListValue(v) => { - value::KindOneof::ListValue( - ::buffa::alloc::boxed::Box::new(v.to_owned_message()), - ) - } - }), - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for ValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for ValueView<'a> { - type Static = ValueView<'static>; -} -pub mod value { - #[allow(unused_imports)] - use super::*; - /// The kind of value. - #[derive(Clone, PartialEq, Debug)] - #[cfg_attr(feature = "arbitrary", derive(::arbitrary::Arbitrary))] - pub enum KindOneof { - NullValue(::buffa::EnumValue), - NumberValue(f64), - StringValue(::buffa::alloc::string::String), - BoolValue(bool), - StructValue(::buffa::alloc::boxed::Box), - ListValue(::buffa::alloc::boxed::Box), - } - impl ::buffa::Oneof for KindOneof {} - impl From for KindOneof { - fn from(v: super::Struct) -> Self { - Self::StructValue(::buffa::alloc::boxed::Box::new(v)) - } - } - impl From for ::core::option::Option { - fn from(v: super::Struct) -> Self { - Self::Some(KindOneof::from(v)) - } - } - impl From for KindOneof { - fn from(v: super::ListValue) -> Self { - Self::ListValue(::buffa::alloc::boxed::Box::new(v)) - } - } - impl From for ::core::option::Option { - fn from(v: super::ListValue) -> Self { - Self::Some(KindOneof::from(v)) - } - } - #[derive(Clone, Debug)] - pub enum KindOneofView<'a> { - NullValue(::buffa::EnumValue), - NumberValue(f64), - StringValue(&'a str), - BoolValue(bool), - StructValue(::buffa::alloc::boxed::Box>), - ListValue(::buffa::alloc::boxed::Box>), - } -} /// `ListValue` is a wrapper around a repeated field of values. /// /// The JSON representation for `ListValue` is JSON array. @@ -1299,112 +871,3 @@ pub const __LIST_VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa: text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// `ListValue` is a wrapper around a repeated field of values. -/// -/// The JSON representation for `ListValue` is JSON array. -#[derive(Clone, Debug, Default)] -pub struct ListValueView<'a> { - /// Repeated field of dynamically typed values. - /// - /// Field 1: `values` - pub values: ::buffa::RepeatedView<'a, ValueView<'a>>, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> ListValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - if depth == 0 { - return Err(::buffa::DecodeError::RecursionLimitExceeded); - } - let sub = ::buffa::types::borrow_bytes(&mut cur)?; - view.values.push(ValueView::_decode_depth(sub, depth - 1)?); - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for ListValueView<'a> { - type Owned = ListValue; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> ListValue { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - ListValue { - values: self.values.iter().map(|v| v.to_owned_message()).collect(), - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for ListValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for ListValueView<'a> { - type Static = ListValueView<'static>; -} diff --git a/buffa-types/src/generated/google.protobuf.timestamp.__ext.rs b/buffa-types/src/generated/google.protobuf.timestamp.__ext.rs new file mode 100644 index 0000000..f330cee --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.timestamp.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/timestamp.proto + diff --git a/buffa-types/src/generated/google.protobuf.timestamp.__oneofs.rs b/buffa-types/src/generated/google.protobuf.timestamp.__oneofs.rs new file mode 100644 index 0000000..ccdf235 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.timestamp.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: google/protobuf/timestamp.proto + diff --git a/buffa-types/src/generated/google.protobuf.timestamp.__view.rs b/buffa-types/src/generated/google.protobuf.timestamp.__view.rs new file mode 100644 index 0000000..27ddd5a --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.timestamp.__view.rs @@ -0,0 +1,226 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents) +// source: google/protobuf/timestamp.proto + +/// A Timestamp represents a point in time independent of any time zone or local +/// calendar, encoded as a count of seconds and fractions of seconds at +/// nanosecond resolution. The count is relative to an epoch at UTC midnight on +/// January 1, 1970, in the proleptic Gregorian calendar which extends the +/// Gregorian calendar backwards to year one. +/// +/// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +/// second table is needed for interpretation, using a \[24-hour linear +/// smear\](). +/// +/// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +/// restricting to that range, we ensure that we can convert to and from \[RFC +/// 3339\]() date strings. +/// +/// # Examples +/// +/// Example 1: Compute Timestamp from POSIX `time()`. +/// +/// ```text +/// Timestamp timestamp; +/// timestamp.set_seconds(time(NULL)); +/// timestamp.set_nanos(0); +/// ``` +/// +/// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +/// +/// ```text +/// struct timeval tv; +/// gettimeofday(&tv, NULL); +/// +/// Timestamp timestamp; +/// timestamp.set_seconds(tv.tv_sec); +/// timestamp.set_nanos(tv.tv_usec * 1000); +/// ``` +/// +/// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +/// +/// ```text +/// FILETIME ft; +/// GetSystemTimeAsFileTime(&ft); +/// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +/// +/// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +/// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +/// Timestamp timestamp; +/// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +/// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +/// ``` +/// +/// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +/// +/// ```text +/// long millis = System.currentTimeMillis(); +/// +/// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +/// .setNanos((int) ((millis % 1000) * 1000000)).build(); +/// ``` +/// +/// Example 5: Compute Timestamp from Java `Instant.now()`. +/// +/// ```text +/// Instant now = Instant.now(); +/// +/// Timestamp timestamp = +/// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) +/// .setNanos(now.getNano()).build(); +/// ``` +/// +/// Example 6: Compute Timestamp from current time in Python. +/// +/// ```text +/// timestamp = Timestamp() +/// timestamp.GetCurrentTime() +/// ``` +/// +/// # JSON Mapping +/// +/// In JSON format, the Timestamp type is encoded as a string in the +/// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +/// format is "{year}-{month}-{day}T{hour}:{min}:{sec}\[.{frac_sec}\]Z" +/// where {year} is always expressed using four digits while {month}, {day}, +/// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +/// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +/// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +/// is required. A proto3 JSON serializer should always use UTC (as indicated by +/// "Z") when printing the Timestamp type and a proto3 JSON parser should be +/// able to accept both UTC and other timezones (as indicated by an offset). +/// +/// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +/// 01:30 UTC on January 15, 2017. +/// +/// In JavaScript, one can convert a Date object to this format using the +/// standard +/// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +/// method. In Python, a standard `datetime.datetime` object can be converted +/// to this format using +/// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +/// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +/// the Joda Time's \[`ISODateTimeFormat.dateTime()`\]( +/// ) +/// ) to obtain a formatter capable of generating timestamps in this format. +#[derive(Clone, Debug, Default)] +pub struct TimestampView<'a> { + /// Represents seconds of UTC time since Unix epoch + /// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + /// 9999-12-31T23:59:59Z inclusive. + /// + /// Field 1: `seconds` + pub seconds: i64, + /// Non-negative fractions of a second at nanosecond resolution. Negative + /// second values with fractions must still have non-negative nanos values + /// that count forward in time. Must be from 0 to 999,999,999 + /// inclusive. + /// + /// Field 2: `nanos` + pub nanos: i32, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> TimestampView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.seconds = ::buffa::types::decode_int64(&mut cur)?; + } + 2u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 2u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.nanos = ::buffa::types::decode_int32(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for TimestampView<'a> { + type Owned = super::Timestamp; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::Timestamp { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::Timestamp { + seconds: self.seconds, + nanos: self.nanos, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for TimestampView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for TimestampView<'a> { + type Static = TimestampView<'static>; +} diff --git a/buffa-types/src/generated/google.protobuf.timestamp.__view_oneofs.rs b/buffa-types/src/generated/google.protobuf.timestamp.__view_oneofs.rs new file mode 100644 index 0000000..0ce14cc --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.timestamp.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: google/protobuf/timestamp.proto + diff --git a/buffa-types/src/generated/google.protobuf.timestamp.rs b/buffa-types/src/generated/google.protobuf.timestamp.rs index 34cf43c..9677710 100644 --- a/buffa-types/src/generated/google.protobuf.timestamp.rs +++ b/buffa-types/src/generated/google.protobuf.timestamp.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/timestamp.proto /// A Timestamp represents a point in time independent of any time zone or local @@ -276,226 +276,3 @@ pub const __TIMESTAMP_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa:: text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// A Timestamp represents a point in time independent of any time zone or local -/// calendar, encoded as a count of seconds and fractions of seconds at -/// nanosecond resolution. The count is relative to an epoch at UTC midnight on -/// January 1, 1970, in the proleptic Gregorian calendar which extends the -/// Gregorian calendar backwards to year one. -/// -/// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap -/// second table is needed for interpretation, using a \[24-hour linear -/// smear\](). -/// -/// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By -/// restricting to that range, we ensure that we can convert to and from \[RFC -/// 3339\]() date strings. -/// -/// # Examples -/// -/// Example 1: Compute Timestamp from POSIX `time()`. -/// -/// ```text -/// Timestamp timestamp; -/// timestamp.set_seconds(time(NULL)); -/// timestamp.set_nanos(0); -/// ``` -/// -/// Example 2: Compute Timestamp from POSIX `gettimeofday()`. -/// -/// ```text -/// struct timeval tv; -/// gettimeofday(&tv, NULL); -/// -/// Timestamp timestamp; -/// timestamp.set_seconds(tv.tv_sec); -/// timestamp.set_nanos(tv.tv_usec * 1000); -/// ``` -/// -/// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. -/// -/// ```text -/// FILETIME ft; -/// GetSystemTimeAsFileTime(&ft); -/// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; -/// -/// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z -/// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. -/// Timestamp timestamp; -/// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); -/// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); -/// ``` -/// -/// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. -/// -/// ```text -/// long millis = System.currentTimeMillis(); -/// -/// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) -/// .setNanos((int) ((millis % 1000) * 1000000)).build(); -/// ``` -/// -/// Example 5: Compute Timestamp from Java `Instant.now()`. -/// -/// ```text -/// Instant now = Instant.now(); -/// -/// Timestamp timestamp = -/// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) -/// .setNanos(now.getNano()).build(); -/// ``` -/// -/// Example 6: Compute Timestamp from current time in Python. -/// -/// ```text -/// timestamp = Timestamp() -/// timestamp.GetCurrentTime() -/// ``` -/// -/// # JSON Mapping -/// -/// In JSON format, the Timestamp type is encoded as a string in the -/// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the -/// format is "{year}-{month}-{day}T{hour}:{min}:{sec}\[.{frac_sec}\]Z" -/// where {year} is always expressed using four digits while {month}, {day}, -/// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional -/// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), -/// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -/// is required. A proto3 JSON serializer should always use UTC (as indicated by -/// "Z") when printing the Timestamp type and a proto3 JSON parser should be -/// able to accept both UTC and other timezones (as indicated by an offset). -/// -/// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past -/// 01:30 UTC on January 15, 2017. -/// -/// In JavaScript, one can convert a Date object to this format using the -/// standard -/// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) -/// method. In Python, a standard `datetime.datetime` object can be converted -/// to this format using -/// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with -/// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use -/// the Joda Time's \[`ISODateTimeFormat.dateTime()`\]( -/// ) -/// ) to obtain a formatter capable of generating timestamps in this format. -#[derive(Clone, Debug, Default)] -pub struct TimestampView<'a> { - /// Represents seconds of UTC time since Unix epoch - /// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - /// 9999-12-31T23:59:59Z inclusive. - /// - /// Field 1: `seconds` - pub seconds: i64, - /// Non-negative fractions of a second at nanosecond resolution. Negative - /// second values with fractions must still have non-negative nanos values - /// that count forward in time. Must be from 0 to 999,999,999 - /// inclusive. - /// - /// Field 2: `nanos` - pub nanos: i32, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> TimestampView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.seconds = ::buffa::types::decode_int64(&mut cur)?; - } - 2u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 2u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.nanos = ::buffa::types::decode_int32(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for TimestampView<'a> { - type Owned = Timestamp; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> Timestamp { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - Timestamp { - seconds: self.seconds, - nanos: self.nanos, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for TimestampView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for TimestampView<'a> { - type Static = TimestampView<'static>; -} diff --git a/buffa-types/src/generated/google.protobuf.wrappers.__ext.rs b/buffa-types/src/generated/google.protobuf.wrappers.__ext.rs new file mode 100644 index 0000000..a5fd251 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.wrappers.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: google/protobuf/wrappers.proto + diff --git a/buffa-types/src/generated/google.protobuf.wrappers.__oneofs.rs b/buffa-types/src/generated/google.protobuf.wrappers.__oneofs.rs new file mode 100644 index 0000000..b491afc --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.wrappers.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: google/protobuf/wrappers.proto + diff --git a/buffa-types/src/generated/google.protobuf.wrappers.__view.rs b/buffa-types/src/generated/google.protobuf.wrappers.__view.rs new file mode 100644 index 0000000..9010b58 --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.wrappers.__view.rs @@ -0,0 +1,948 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents) +// source: google/protobuf/wrappers.proto + +/// Wrapper message for `double`. +/// +/// The JSON representation for `DoubleValue` is JSON number. +#[derive(Clone, Debug, Default)] +pub struct DoubleValueView<'a> { + /// The double value. + /// + /// Field 1: `value` + pub value: f64, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> DoubleValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Fixed64 { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 1u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::decode_double(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for DoubleValueView<'a> { + type Owned = super::DoubleValue; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::DoubleValue { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::DoubleValue { + value: self.value, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for DoubleValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for DoubleValueView<'a> { + type Static = DoubleValueView<'static>; +} +/// Wrapper message for `float`. +/// +/// The JSON representation for `FloatValue` is JSON number. +#[derive(Clone, Debug, Default)] +pub struct FloatValueView<'a> { + /// The float value. + /// + /// Field 1: `value` + pub value: f32, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> FloatValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Fixed32 { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 5u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::decode_float(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for FloatValueView<'a> { + type Owned = super::FloatValue; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::FloatValue { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::FloatValue { + value: self.value, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for FloatValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for FloatValueView<'a> { + type Static = FloatValueView<'static>; +} +/// Wrapper message for `int64`. +/// +/// The JSON representation for `Int64Value` is JSON string. +#[derive(Clone, Debug, Default)] +pub struct Int64ValueView<'a> { + /// The int64 value. + /// + /// Field 1: `value` + pub value: i64, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> Int64ValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::decode_int64(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for Int64ValueView<'a> { + type Owned = super::Int64Value; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::Int64Value { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::Int64Value { + value: self.value, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for Int64ValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for Int64ValueView<'a> { + type Static = Int64ValueView<'static>; +} +/// Wrapper message for `uint64`. +/// +/// The JSON representation for `UInt64Value` is JSON string. +#[derive(Clone, Debug, Default)] +pub struct UInt64ValueView<'a> { + /// The uint64 value. + /// + /// Field 1: `value` + pub value: u64, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> UInt64ValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::decode_uint64(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for UInt64ValueView<'a> { + type Owned = super::UInt64Value; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::UInt64Value { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::UInt64Value { + value: self.value, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for UInt64ValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for UInt64ValueView<'a> { + type Static = UInt64ValueView<'static>; +} +/// Wrapper message for `int32`. +/// +/// The JSON representation for `Int32Value` is JSON number. +#[derive(Clone, Debug, Default)] +pub struct Int32ValueView<'a> { + /// The int32 value. + /// + /// Field 1: `value` + pub value: i32, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> Int32ValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::decode_int32(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for Int32ValueView<'a> { + type Owned = super::Int32Value; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::Int32Value { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::Int32Value { + value: self.value, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for Int32ValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for Int32ValueView<'a> { + type Static = Int32ValueView<'static>; +} +/// Wrapper message for `uint32`. +/// +/// The JSON representation for `UInt32Value` is JSON number. +#[derive(Clone, Debug, Default)] +pub struct UInt32ValueView<'a> { + /// The uint32 value. + /// + /// Field 1: `value` + pub value: u32, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> UInt32ValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::decode_uint32(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for UInt32ValueView<'a> { + type Owned = super::UInt32Value; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::UInt32Value { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::UInt32Value { + value: self.value, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for UInt32ValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for UInt32ValueView<'a> { + type Static = UInt32ValueView<'static>; +} +/// Wrapper message for `bool`. +/// +/// The JSON representation for `BoolValue` is JSON `true` and `false`. +#[derive(Clone, Debug, Default)] +pub struct BoolValueView<'a> { + /// The bool value. + /// + /// Field 1: `value` + pub value: bool, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> BoolValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::Varint { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 0u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::decode_bool(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for BoolValueView<'a> { + type Owned = super::BoolValue; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::BoolValue { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::BoolValue { + value: self.value, + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for BoolValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for BoolValueView<'a> { + type Static = BoolValueView<'static>; +} +/// Wrapper message for `string`. +/// +/// The JSON representation for `StringValue` is JSON string. +#[derive(Clone, Debug, Default)] +pub struct StringValueView<'a> { + /// The string value. + /// + /// Field 1: `value` + pub value: &'a str, + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> StringValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::borrow_str(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for StringValueView<'a> { + type Owned = super::StringValue; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::StringValue { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::StringValue { + value: self.value.to_string(), + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for StringValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for StringValueView<'a> { + type Static = StringValueView<'static>; +} +/// Wrapper message for `bytes`. +/// +/// The JSON representation for `BytesValue` is JSON string. +#[derive(Clone, Debug, Default)] +pub struct BytesValueView<'a> { + /// The bytes value. + /// + /// Field 1: `value` + pub value: &'a [u8], + pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, +} +impl<'a> BytesValueView<'a> { + /// Decode from `buf`, enforcing a recursion depth limit for nested messages. + /// + /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] + /// and by generated sub-message decode arms with `depth - 1`. + /// + /// **Not part of the public API.** Named with a leading underscore to + /// signal that it is for generated-code use only. + #[doc(hidden)] + pub fn _decode_depth( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + let mut view = Self::default(); + view._merge_into_view(buf, depth)?; + ::core::result::Result::Ok(view) + } + /// Merge fields from `buf` into this view (proto merge semantics). + /// + /// Repeated fields append; singular fields last-wins; singular + /// MESSAGE fields merge recursively. Used by sub-message decode + /// arms when the same field appears multiple times on the wire. + /// + /// **Not part of the public API.** + #[doc(hidden)] + pub fn _merge_into_view( + &mut self, + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result<(), ::buffa::DecodeError> { + let _ = depth; + #[allow(unused_variables)] + let view = self; + let mut cur: &'a [u8] = buf; + while !cur.is_empty() { + let before_tag = cur; + let tag = ::buffa::encoding::Tag::decode(&mut cur)?; + match tag.field_number() { + 1u32 => { + if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { + return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { + field_number: 1u32, + expected: 2u8, + actual: tag.wire_type() as u8, + }); + } + view.value = ::buffa::types::borrow_bytes(&mut cur)?; + } + _ => { + ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; + let span_len = before_tag.len() - cur.len(); + view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); + } + } + } + ::core::result::Result::Ok(()) + } +} +impl<'a> ::buffa::MessageView<'a> for BytesValueView<'a> { + type Owned = super::BytesValue; + fn decode_view(buf: &'a [u8]) -> ::core::result::Result { + Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) + } + fn decode_view_with_limit( + buf: &'a [u8], + depth: u32, + ) -> ::core::result::Result { + Self::_decode_depth(buf, depth) + } + /// Convert this view to the owned message type. + #[allow(clippy::redundant_closure, clippy::useless_conversion)] + fn to_owned_message(&self) -> super::BytesValue { + #[allow(unused_imports)] + use ::buffa::alloc::string::ToString as _; + super::BytesValue { + value: (self.value).to_vec(), + __buffa_unknown_fields: self + .__buffa_unknown_fields + .to_owned() + .unwrap_or_default() + .into(), + ..::core::default::Default::default() + } + } +} +unsafe impl ::buffa::DefaultViewInstance for BytesValueView<'static> { + fn default_view_instance() -> &'static Self { + static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); + VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) + } +} +unsafe impl<'a> ::buffa::HasDefaultViewInstance for BytesValueView<'a> { + type Static = BytesValueView<'static>; +} diff --git a/buffa-types/src/generated/google.protobuf.wrappers.__view_oneofs.rs b/buffa-types/src/generated/google.protobuf.wrappers.__view_oneofs.rs new file mode 100644 index 0000000..ef8fdaa --- /dev/null +++ b/buffa-types/src/generated/google.protobuf.wrappers.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: google/protobuf/wrappers.proto + diff --git a/buffa-types/src/generated/google.protobuf.wrappers.rs b/buffa-types/src/generated/google.protobuf.wrappers.rs index 4667c4a..2b39fe3 100644 --- a/buffa-types/src/generated/google.protobuf.wrappers.rs +++ b/buffa-types/src/generated/google.protobuf.wrappers.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: google/protobuf/wrappers.proto /// Wrapper message for `double`. @@ -142,111 +142,6 @@ pub const __DOUBLE_VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buff text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `double`. -/// -/// The JSON representation for `DoubleValue` is JSON number. -#[derive(Clone, Debug, Default)] -pub struct DoubleValueView<'a> { - /// The double value. - /// - /// Field 1: `value` - pub value: f64, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> DoubleValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Fixed64 { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 1u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::decode_double(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for DoubleValueView<'a> { - type Owned = DoubleValue; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> DoubleValue { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - DoubleValue { - value: self.value, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for DoubleValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for DoubleValueView<'a> { - type Static = DoubleValueView<'static>; -} /// Wrapper message for `float`. /// /// The JSON representation for `FloatValue` is JSON number. @@ -388,111 +283,6 @@ pub const __FLOAT_VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `float`. -/// -/// The JSON representation for `FloatValue` is JSON number. -#[derive(Clone, Debug, Default)] -pub struct FloatValueView<'a> { - /// The float value. - /// - /// Field 1: `value` - pub value: f32, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> FloatValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Fixed32 { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 5u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::decode_float(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for FloatValueView<'a> { - type Owned = FloatValue; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> FloatValue { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - FloatValue { - value: self.value, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for FloatValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for FloatValueView<'a> { - type Static = FloatValueView<'static>; -} /// Wrapper message for `int64`. /// /// The JSON representation for `Int64Value` is JSON string. @@ -634,111 +424,6 @@ pub const __INT64VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa: text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `int64`. -/// -/// The JSON representation for `Int64Value` is JSON string. -#[derive(Clone, Debug, Default)] -pub struct Int64ValueView<'a> { - /// The int64 value. - /// - /// Field 1: `value` - pub value: i64, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> Int64ValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::decode_int64(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for Int64ValueView<'a> { - type Owned = Int64Value; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> Int64Value { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - Int64Value { - value: self.value, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for Int64ValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for Int64ValueView<'a> { - type Static = Int64ValueView<'static>; -} /// Wrapper message for `uint64`. /// /// The JSON representation for `UInt64Value` is JSON string. @@ -880,111 +565,6 @@ pub const __U_INT64VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buff text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `uint64`. -/// -/// The JSON representation for `UInt64Value` is JSON string. -#[derive(Clone, Debug, Default)] -pub struct UInt64ValueView<'a> { - /// The uint64 value. - /// - /// Field 1: `value` - pub value: u64, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> UInt64ValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::decode_uint64(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for UInt64ValueView<'a> { - type Owned = UInt64Value; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> UInt64Value { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - UInt64Value { - value: self.value, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for UInt64ValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for UInt64ValueView<'a> { - type Static = UInt64ValueView<'static>; -} /// Wrapper message for `int32`. /// /// The JSON representation for `Int32Value` is JSON number. @@ -1126,111 +706,6 @@ pub const __INT32VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa: text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `int32`. -/// -/// The JSON representation for `Int32Value` is JSON number. -#[derive(Clone, Debug, Default)] -pub struct Int32ValueView<'a> { - /// The int32 value. - /// - /// Field 1: `value` - pub value: i32, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> Int32ValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::decode_int32(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for Int32ValueView<'a> { - type Owned = Int32Value; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> Int32Value { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - Int32Value { - value: self.value, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for Int32ValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for Int32ValueView<'a> { - type Static = Int32ValueView<'static>; -} /// Wrapper message for `uint32`. /// /// The JSON representation for `UInt32Value` is JSON number. @@ -1372,111 +847,6 @@ pub const __U_INT32VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buff text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `uint32`. -/// -/// The JSON representation for `UInt32Value` is JSON number. -#[derive(Clone, Debug, Default)] -pub struct UInt32ValueView<'a> { - /// The uint32 value. - /// - /// Field 1: `value` - pub value: u32, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> UInt32ValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::decode_uint32(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for UInt32ValueView<'a> { - type Owned = UInt32Value; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> UInt32Value { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - UInt32Value { - value: self.value, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for UInt32ValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for UInt32ValueView<'a> { - type Static = UInt32ValueView<'static>; -} /// Wrapper message for `bool`. /// /// The JSON representation for `BoolValue` is JSON `true` and `false`. @@ -1618,111 +988,6 @@ pub const __BOOL_VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa: text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `bool`. -/// -/// The JSON representation for `BoolValue` is JSON `true` and `false`. -#[derive(Clone, Debug, Default)] -pub struct BoolValueView<'a> { - /// The bool value. - /// - /// Field 1: `value` - pub value: bool, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> BoolValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::Varint { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 0u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::decode_bool(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for BoolValueView<'a> { - type Owned = BoolValue; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> BoolValue { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - BoolValue { - value: self.value, - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for BoolValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for BoolValueView<'a> { - type Static = BoolValueView<'static>; -} /// Wrapper message for `string`. /// /// The JSON representation for `StringValue` is JSON string. @@ -1867,111 +1132,6 @@ pub const __STRING_VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buff text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `string`. -/// -/// The JSON representation for `StringValue` is JSON string. -#[derive(Clone, Debug, Default)] -pub struct StringValueView<'a> { - /// The string value. - /// - /// Field 1: `value` - pub value: &'a str, - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> StringValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::borrow_str(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for StringValueView<'a> { - type Owned = StringValue; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> StringValue { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - StringValue { - value: self.value.to_string(), - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for StringValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for StringValueView<'a> { - type Static = StringValueView<'static>; -} /// Wrapper message for `bytes`. /// /// The JSON representation for `BytesValue` is JSON string. @@ -2116,108 +1276,3 @@ pub const __BYTES_VALUE_TEXT_ANY: ::buffa::type_registry::TextAnyEntry = ::buffa text_encode: ::buffa::type_registry::any_encode_text::, text_merge: ::buffa::type_registry::any_merge_text::, }; -/// Wrapper message for `bytes`. -/// -/// The JSON representation for `BytesValue` is JSON string. -#[derive(Clone, Debug, Default)] -pub struct BytesValueView<'a> { - /// The bytes value. - /// - /// Field 1: `value` - pub value: &'a [u8], - pub __buffa_unknown_fields: ::buffa::UnknownFieldsView<'a>, -} -impl<'a> BytesValueView<'a> { - /// Decode from `buf`, enforcing a recursion depth limit for nested messages. - /// - /// Called by [`::buffa::MessageView::decode_view`] with [`::buffa::RECURSION_LIMIT`] - /// and by generated sub-message decode arms with `depth - 1`. - /// - /// **Not part of the public API.** Named with a leading underscore to - /// signal that it is for generated-code use only. - #[doc(hidden)] - pub fn _decode_depth( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - let mut view = Self::default(); - view._merge_into_view(buf, depth)?; - ::core::result::Result::Ok(view) - } - /// Merge fields from `buf` into this view (proto merge semantics). - /// - /// Repeated fields append; singular fields last-wins; singular - /// MESSAGE fields merge recursively. Used by sub-message decode - /// arms when the same field appears multiple times on the wire. - /// - /// **Not part of the public API.** - #[doc(hidden)] - pub fn _merge_into_view( - &mut self, - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result<(), ::buffa::DecodeError> { - let _ = depth; - #[allow(unused_variables)] - let view = self; - let mut cur: &'a [u8] = buf; - while !cur.is_empty() { - let before_tag = cur; - let tag = ::buffa::encoding::Tag::decode(&mut cur)?; - match tag.field_number() { - 1u32 => { - if tag.wire_type() != ::buffa::encoding::WireType::LengthDelimited { - return ::core::result::Result::Err(::buffa::DecodeError::WireTypeMismatch { - field_number: 1u32, - expected: 2u8, - actual: tag.wire_type() as u8, - }); - } - view.value = ::buffa::types::borrow_bytes(&mut cur)?; - } - _ => { - ::buffa::encoding::skip_field_depth(tag, &mut cur, depth)?; - let span_len = before_tag.len() - cur.len(); - view.__buffa_unknown_fields.push_raw(&before_tag[..span_len]); - } - } - } - ::core::result::Result::Ok(()) - } -} -impl<'a> ::buffa::MessageView<'a> for BytesValueView<'a> { - type Owned = BytesValue; - fn decode_view(buf: &'a [u8]) -> ::core::result::Result { - Self::_decode_depth(buf, ::buffa::RECURSION_LIMIT) - } - fn decode_view_with_limit( - buf: &'a [u8], - depth: u32, - ) -> ::core::result::Result { - Self::_decode_depth(buf, depth) - } - /// Convert this view to the owned message type. - #[allow(clippy::redundant_closure, clippy::useless_conversion)] - fn to_owned_message(&self) -> BytesValue { - #[allow(unused_imports)] - use ::buffa::alloc::string::ToString as _; - BytesValue { - value: (self.value).to_vec(), - __buffa_unknown_fields: self - .__buffa_unknown_fields - .to_owned() - .unwrap_or_default() - .into(), - ..::core::default::Default::default() - } - } -} -unsafe impl ::buffa::DefaultViewInstance for BytesValueView<'static> { - fn default_view_instance() -> &'static Self { - static VALUE: ::buffa::__private::OnceBox> = ::buffa::__private::OnceBox::new(); - VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) - } -} -unsafe impl<'a> ::buffa::HasDefaultViewInstance for BytesValueView<'a> { - type Static = BytesValueView<'static>; -} diff --git a/buffa-types/src/lib.rs b/buffa-types/src/lib.rs index 65283c5..2565447 100644 --- a/buffa-types/src/lib.rs +++ b/buffa-types/src/lib.rs @@ -75,6 +75,71 @@ pub mod google { include!("generated/google.protobuf.struct.rs"); include!("generated/google.protobuf.timestamp.rs"); include!("generated/google.protobuf.wrappers.rs"); + // View tree — `view::::*View` for every message. + // The inner `view::oneofs::::*` sub-module carries + // view-of-oneof enums (see DESIGN.md → "Generated code layout"). + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types + )] + pub mod view { + include!("generated/google.protobuf.any.__view.rs"); + include!("generated/google.protobuf.duration.__view.rs"); + include!("generated/google.protobuf.empty.__view.rs"); + include!("generated/google.protobuf.field_mask.__view.rs"); + include!("generated/google.protobuf.struct.__view.rs"); + include!("generated/google.protobuf.timestamp.__view.rs"); + include!("generated/google.protobuf.wrappers.__view.rs"); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + unused_imports + )] + pub mod oneofs { + include!("generated/google.protobuf.any.__view_oneofs.rs"); + include!("generated/google.protobuf.duration.__view_oneofs.rs"); + include!("generated/google.protobuf.empty.__view_oneofs.rs"); + include!("generated/google.protobuf.field_mask.__view_oneofs.rs"); + include!("generated/google.protobuf.struct.__view_oneofs.rs"); + include!("generated/google.protobuf.timestamp.__view_oneofs.rs"); + include!("generated/google.protobuf.wrappers.__view_oneofs.rs"); + } + } + // WKTs have no file-level extensions, so the .__ext.rs files are + // empty and register_types is suppressed. We still include them + // to keep the sibling-file invariant — simplifies diffs if any + // WKT gains an extension in a future revision. + #[allow(unused_imports, dead_code)] + pub mod ext { + include!("generated/google.protobuf.any.__ext.rs"); + include!("generated/google.protobuf.duration.__ext.rs"); + include!("generated/google.protobuf.empty.__ext.rs"); + include!("generated/google.protobuf.field_mask.__ext.rs"); + include!("generated/google.protobuf.struct.__ext.rs"); + include!("generated/google.protobuf.timestamp.__ext.rs"); + include!("generated/google.protobuf.wrappers.__ext.rs"); + } + // Owned oneofs tree — `oneofs::::Kind` for every + // oneof. All seven WKT files contribute (most are empty — only + // Value has a `kind` oneof, which lands at + // `oneofs::value::Kind` inside this module). + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + unused_imports + )] + pub mod oneofs { + include!("generated/google.protobuf.any.__oneofs.rs"); + include!("generated/google.protobuf.duration.__oneofs.rs"); + include!("generated/google.protobuf.empty.__oneofs.rs"); + include!("generated/google.protobuf.field_mask.__oneofs.rs"); + include!("generated/google.protobuf.struct.__oneofs.rs"); + include!("generated/google.protobuf.timestamp.__oneofs.rs"); + include!("generated/google.protobuf.wrappers.__oneofs.rs"); + } } } diff --git a/buffa-types/src/timestamp_ext.rs b/buffa-types/src/timestamp_ext.rs index 38e42ae..9534a48 100644 --- a/buffa-types/src/timestamp_ext.rs +++ b/buffa-types/src/timestamp_ext.rs @@ -540,7 +540,8 @@ mod tests { #[test] fn timestamp_view_round_trip() { - use crate::google::protobuf::{Timestamp, TimestampView}; + use crate::google::protobuf::view::TimestampView; + use crate::google::protobuf::Timestamp; use buffa::{Message, MessageView}; let ts = Timestamp { diff --git a/buffa-types/src/value_ext.rs b/buffa-types/src/value_ext.rs index ef1cb71..62d2511 100644 --- a/buffa-types/src/value_ext.rs +++ b/buffa-types/src/value_ext.rs @@ -5,13 +5,14 @@ use alloc::boxed::Box; use alloc::string::{String, ToString}; -use crate::google::protobuf::{value::KindOneof, ListValue, NullValue, Struct, Value}; +use crate::google::protobuf::oneofs::value::Kind; +use crate::google::protobuf::{ListValue, NullValue, Struct, Value}; impl Value { /// Construct a [`Value`] that represents a protobuf `null`. pub fn null() -> Self { Self { - kind: Some(KindOneof::NullValue(buffa::EnumValue::from( + kind: Some(Kind::NullValue(buffa::EnumValue::from( NullValue::NULL_VALUE, ))), ..Default::default() @@ -20,13 +21,13 @@ impl Value { /// Returns `true` if this value is the null variant. pub fn is_null(&self) -> bool { - matches!(self.kind, Some(KindOneof::NullValue(_))) + matches!(self.kind, Some(Kind::NullValue(_))) } /// Returns the `f64` value if this is a number, otherwise `None`. pub fn as_number(&self) -> Option { match &self.kind { - Some(KindOneof::NumberValue(n)) => Some(*n), + Some(Kind::NumberValue(n)) => Some(*n), _ => None, } } @@ -34,7 +35,7 @@ impl Value { /// Returns the string value if this is a string, otherwise `None`. pub fn as_str(&self) -> Option<&str> { match &self.kind { - Some(KindOneof::StringValue(s)) => Some(s.as_str()), + Some(Kind::StringValue(s)) => Some(s.as_str()), _ => None, } } @@ -42,7 +43,7 @@ impl Value { /// Returns the bool value if this is a bool, otherwise `None`. pub fn as_bool(&self) -> Option { match &self.kind { - Some(KindOneof::BoolValue(b)) => Some(*b), + Some(Kind::BoolValue(b)) => Some(*b), _ => None, } } @@ -50,7 +51,7 @@ impl Value { /// Returns a reference to the [`Struct`] if this is a struct value. pub fn as_struct(&self) -> Option<&Struct> { match &self.kind { - Some(KindOneof::StructValue(s)) => Some(s), + Some(Kind::StructValue(s)) => Some(s), _ => None, } } @@ -58,7 +59,7 @@ impl Value { /// Returns a reference to the [`ListValue`] if this is a list value. pub fn as_list(&self) -> Option<&ListValue> { match &self.kind { - Some(KindOneof::ListValue(l)) => Some(l), + Some(Kind::ListValue(l)) => Some(l), _ => None, } } @@ -66,7 +67,7 @@ impl Value { /// Returns a mutable reference to the [`Struct`] if this is a struct value. pub fn as_struct_mut(&mut self) -> Option<&mut Struct> { match &mut self.kind { - Some(KindOneof::StructValue(s)) => Some(s), + Some(Kind::StructValue(s)) => Some(s), _ => None, } } @@ -74,7 +75,7 @@ impl Value { /// Returns a mutable reference to the [`ListValue`] if this is a list value. pub fn as_list_mut(&mut self) -> Option<&mut ListValue> { match &mut self.kind { - Some(KindOneof::ListValue(l)) => Some(l), + Some(Kind::ListValue(l)) => Some(l), _ => None, } } @@ -83,7 +84,7 @@ impl Value { impl From for Value { fn from(n: f64) -> Self { Self { - kind: Some(KindOneof::NumberValue(n)), + kind: Some(Kind::NumberValue(n)), ..Default::default() } } @@ -92,7 +93,7 @@ impl From for Value { impl From for Value { fn from(s: String) -> Self { Self { - kind: Some(KindOneof::StringValue(s)), + kind: Some(Kind::StringValue(s)), ..Default::default() } } @@ -101,7 +102,7 @@ impl From for Value { impl From<&str> for Value { fn from(s: &str) -> Self { Self { - kind: Some(KindOneof::StringValue(s.to_string())), + kind: Some(Kind::StringValue(s.to_string())), ..Default::default() } } @@ -120,7 +121,7 @@ impl From for Value { impl From for Value { fn from(b: bool) -> Self { Self { - kind: Some(KindOneof::BoolValue(b)), + kind: Some(Kind::BoolValue(b)), ..Default::default() } } @@ -171,7 +172,7 @@ impl From for Value { impl From for Value { fn from(s: Struct) -> Self { Self { - kind: Some(KindOneof::StructValue(Box::new(s))), + kind: Some(Kind::StructValue(Box::new(s))), ..Default::default() } } @@ -180,7 +181,7 @@ impl From for Value { impl From for Value { fn from(l: ListValue) -> Self { Self { - kind: Some(KindOneof::ListValue(Box::new(l))), + kind: Some(Kind::ListValue(Box::new(l))), ..Default::default() } } @@ -308,8 +309,8 @@ impl serde::Serialize for Value { /// string encoding `"NaN"` / `"Infinity"` / `"-Infinity"`). fn serialize(&self, s: S) -> Result { match &self.kind { - None | Some(KindOneof::NullValue(_)) => s.serialize_unit(), - Some(KindOneof::NumberValue(n)) => { + None | Some(Kind::NullValue(_)) => s.serialize_unit(), + Some(Kind::NumberValue(n)) => { if !n.is_finite() { return Err(serde::ser::Error::custom( "Value.number_value must be finite; NaN and Infinity are not valid JSON numbers", @@ -317,10 +318,10 @@ impl serde::Serialize for Value { } s.serialize_f64(*n) } - Some(KindOneof::StringValue(v)) => s.serialize_str(v), - Some(KindOneof::BoolValue(b)) => s.serialize_bool(*b), - Some(KindOneof::StructValue(st)) => st.serialize(s), - Some(KindOneof::ListValue(l)) => l.serialize(s), + Some(Kind::StringValue(v)) => s.serialize_str(v), + Some(Kind::BoolValue(b)) => s.serialize_bool(*b), + Some(Kind::StructValue(st)) => st.serialize(s), + Some(Kind::ListValue(l)) => l.serialize(s), } } } diff --git a/buffa-types/tests/wkt_roundtrip.rs b/buffa-types/tests/wkt_roundtrip.rs index 055f891..c233c8e 100644 --- a/buffa-types/tests/wkt_roundtrip.rs +++ b/buffa-types/tests/wkt_roundtrip.rs @@ -233,7 +233,7 @@ fn timestamp_view_roundtrip() { ..Default::default() }; let bytes = ts.encode_to_vec(); - assert_eq!(view_roundtrip::(&bytes), ts); + assert_eq!(view_roundtrip::(&bytes), ts); } #[test] @@ -244,14 +244,14 @@ fn duration_view_roundtrip() { ..Default::default() }; let bytes = d.encode_to_vec(); - assert_eq!(view_roundtrip::(&bytes), d); + assert_eq!(view_roundtrip::(&bytes), d); } #[test] fn empty_view_roundtrip() { let e = wkt::Empty::default(); let bytes = e.encode_to_vec(); - assert_eq!(view_roundtrip::(&bytes), e); + assert_eq!(view_roundtrip::(&bytes), e); } #[test] @@ -262,7 +262,7 @@ fn any_view_roundtrip() { ..Default::default() }; let bytes = any.encode_to_vec(); - let owned = view_roundtrip::(&bytes); + let owned = view_roundtrip::(&bytes); assert_eq!(owned.type_url, any.type_url); assert_eq!(owned.value, any.value); } @@ -274,7 +274,7 @@ fn field_mask_view_roundtrip() { ..Default::default() }; let bytes = fm.encode_to_vec(); - assert_eq!(view_roundtrip::(&bytes), fm); + assert_eq!(view_roundtrip::(&bytes), fm); } #[test] @@ -284,7 +284,7 @@ fn string_value_view_roundtrip() { ..Default::default() }; let bytes = w.encode_to_vec(); - assert_eq!(view_roundtrip::(&bytes), w); + assert_eq!(view_roundtrip::(&bytes), w); } #[test] @@ -294,7 +294,7 @@ fn bytes_value_view_roundtrip() { ..Default::default() }; let bytes = w.encode_to_vec(); - assert_eq!(view_roundtrip::(&bytes), w); + assert_eq!(view_roundtrip::(&bytes), w); } #[test] @@ -304,5 +304,5 @@ fn int64_value_view_roundtrip() { ..Default::default() }; let bytes = w.encode_to_vec(); - assert_eq!(view_roundtrip::(&bytes), w); + assert_eq!(view_roundtrip::(&bytes), w); } diff --git a/conformance/Cargo.lock b/conformance/Cargo.lock index 6d56441..bf034d1 100644 --- a/conformance/Cargo.lock +++ b/conformance/Cargo.lock @@ -22,7 +22,7 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "buffa" -version = "0.2.0" +version = "0.3.0" dependencies = [ "base64", "bytes", @@ -35,7 +35,7 @@ dependencies = [ [[package]] name = "buffa-build" -version = "0.2.0" +version = "0.3.0" dependencies = [ "buffa", "buffa-codegen", @@ -44,7 +44,7 @@ dependencies = [ [[package]] name = "buffa-codegen" -version = "0.2.0" +version = "0.3.0" dependencies = [ "buffa", "buffa-descriptor", @@ -57,16 +57,17 @@ dependencies = [ [[package]] name = "buffa-descriptor" -version = "0.2.0" +version = "0.3.0" dependencies = [ "buffa", ] [[package]] name = "buffa-types" -version = "0.2.0" +version = "0.3.0" dependencies = [ "buffa", + "bytes", "serde", "serde_json", "thiserror", diff --git a/conformance/src/main.rs b/conformance/src/main.rs index 0c2a810..31d1580 100644 --- a/conformance/src/main.rs +++ b/conformance/src/main.rs @@ -45,6 +45,58 @@ pub mod protobuf_test_messages { env!("OUT_DIR"), "/google.protobuf.test_messages_proto3.rs" )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod view { + include!(concat!( + env!("OUT_DIR"), + "/google.protobuf.test_messages_proto3.__view.rs" + )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/google.protobuf.test_messages_proto3.__view_oneofs.rs" + )); + } + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod ext { + include!(concat!( + env!("OUT_DIR"), + "/google.protobuf.test_messages_proto3.__ext.rs" + )); + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/google.protobuf.test_messages_proto3.__oneofs.rs" + )); + } } pub mod proto2 { @@ -53,6 +105,58 @@ pub mod protobuf_test_messages { env!("OUT_DIR"), "/google.protobuf.test_messages_proto2.rs" )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod view { + include!(concat!( + env!("OUT_DIR"), + "/google.protobuf.test_messages_proto2.__view.rs" + )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/google.protobuf.test_messages_proto2.__view_oneofs.rs" + )); + } + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod ext { + include!(concat!( + env!("OUT_DIR"), + "/google.protobuf.test_messages_proto2.__ext.rs" + )); + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/google.protobuf.test_messages_proto2.__oneofs.rs" + )); + } } } @@ -73,6 +177,58 @@ pub mod protobuf_test_messages_editions { env!("OUT_DIR"), "/editions.golden.test_messages_proto3_editions.rs" )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod view { + include!(concat!( + env!("OUT_DIR"), + "/editions.golden.test_messages_proto3_editions.__view.rs" + )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/editions.golden.test_messages_proto3_editions.__view_oneofs.rs" + )); + } + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod ext { + include!(concat!( + env!("OUT_DIR"), + "/editions.golden.test_messages_proto3_editions.__ext.rs" + )); + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/editions.golden.test_messages_proto3_editions.__oneofs.rs" + )); + } } pub mod proto2 { @@ -81,6 +237,58 @@ pub mod protobuf_test_messages_editions { env!("OUT_DIR"), "/editions.golden.test_messages_proto2_editions.rs" )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod view { + include!(concat!( + env!("OUT_DIR"), + "/editions.golden.test_messages_proto2_editions.__view.rs" + )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/editions.golden.test_messages_proto2_editions.__view_oneofs.rs" + )); + } + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod ext { + include!(concat!( + env!("OUT_DIR"), + "/editions.golden.test_messages_proto2_editions.__ext.rs" + )); + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/editions.golden.test_messages_proto2_editions.__oneofs.rs" + )); + } } // Pure edition 2023: file-level DELIMITED message encoding. Binary-only @@ -90,6 +298,58 @@ pub mod protobuf_test_messages_editions { env!("OUT_DIR"), "/conformance.test_protos.test_messages_edition2023.rs" )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod view { + include!(concat!( + env!("OUT_DIR"), + "/conformance.test_protos.test_messages_edition2023.__view.rs" + )); + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/conformance.test_protos.test_messages_edition2023.__view_oneofs.rs" + )); + } + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod ext { + include!(concat!( + env!("OUT_DIR"), + "/conformance.test_protos.test_messages_edition2023.__ext.rs" + )); + } + #[allow( + clippy::derivable_impls, + clippy::match_single_binding, + non_camel_case_types, + dead_code, + unused_imports + )] + pub mod oneofs { + include!(concat!( + env!("OUT_DIR"), + "/conformance.test_protos.test_messages_edition2023.__oneofs.rs" + )); + } } #[cfg(has_editions_protos)] @@ -130,15 +390,15 @@ fn setup_type_registry() { // (JSON + text) + extension entries. `test_messages_proto3.proto` // has no extensions, so its register_types is Any-only; // `test_messages_proto2.proto` declares `extension_int32` at field 120. - proto3::register_types(&mut reg); - proto2::register_types(&mut reg); + proto3::ext::register_types(&mut reg); + proto2::ext::register_types(&mut reg); #[cfg(has_editions_protos)] { - editions_proto3::register_types(&mut reg); - editions_proto2::register_types(&mut reg); + editions_proto3::ext::register_types(&mut reg); + editions_proto2::ext::register_types(&mut reg); // Edition2023's `groupliketype` / `delimited_ext` extensions — // needed for the text `[pkg.ext] { ... }` bracket tests. - protobuf_test_messages_editions::register_types(&mut reg); + protobuf_test_messages_editions::ext::register_types(&mut reg); } set_type_registry(reg); @@ -344,21 +604,21 @@ fn process_via_view(req: &envelope::Request) -> envelope::Response { match req.message_type.as_str() { MSG_PROTO3 => roundtrip_proto3( - || decode_binary_via_view::>(b), + || decode_binary_via_view::>(b), encode_proto3_binary, ), MSG_PROTO2 => roundtrip_proto2( - || decode_binary_via_view::>(b), + || decode_binary_via_view::>(b), encode_proto2_binary, ), #[cfg(has_editions_protos)] MSG_EDITIONS_PROTO3 => roundtrip( - || decode_binary_via_view::>(b), + || decode_binary_via_view::>(b), encode_binary, ), #[cfg(has_editions_protos)] MSG_EDITIONS_PROTO2 => roundtrip( - || decode_binary_via_view::>(b), + || decode_binary_via_view::>(b), encode_binary, ), other => Response::Skipped(format!("message type '{other}' not in view dispatch")), diff --git a/examples/addressbook/Cargo.lock b/examples/addressbook/Cargo.lock index 2485965..7379a9f 100644 --- a/examples/addressbook/Cargo.lock +++ b/examples/addressbook/Cargo.lock @@ -60,6 +60,7 @@ name = "buffa-types" version = "0.3.0" dependencies = [ "buffa", + "bytes", "serde", "serde_json", "thiserror", diff --git a/examples/addressbook/src/main.rs b/examples/addressbook/src/main.rs index 12456e0..98be771 100644 --- a/examples/addressbook/src/main.rs +++ b/examples/addressbook/src/main.rs @@ -8,7 +8,7 @@ //! addressbook dump Print the address book in textproto // `#[allow(deprecated)]` silences codegen-internal references to -// deprecated fields (here: `AddressOneof::FreeformAddress`, which +// deprecated fields (here: `Address::FreeformAddress`, which // build.rs marks `#[deprecated]`). Generated encoders/decoders match // on every variant regardless of deprecation, so the warnings fire // inside the generated module itself; we only want them in *our* @@ -20,7 +20,8 @@ mod proto { use buffa::{EnumValue, Message}; use proto::buffa::examples::addressbook::v1::{ - person::{AddressOneof, PhoneNumber, PhoneType}, + oneofs::person::Address, + person::{PhoneNumber, PhoneType}, AddressBook, Person, StructuredAddress, }; use std::io::{self, BufRead, Write}; @@ -125,16 +126,14 @@ fn cmd_add(file_path: &str) { let state = prompt(" State"); let zip_code = prompt(" Zip code"); let country = prompt(" Country"); - Some(AddressOneof::StructuredAddress(Box::new( - StructuredAddress { - street, - city, - state, - zip_code, - country, - ..Default::default() - }, - ))) + Some(Address::StructuredAddress(Box::new(StructuredAddress { + street, + city, + state, + zip_code, + country, + ..Default::default() + }))) } else { None }; @@ -220,13 +219,13 @@ fn cmd_show(file_path: &str, id: i32) { // warnings still fire on any accidental *writes* elsewhere. #[allow(deprecated)] match &person.address { - Some(AddressOneof::StructuredAddress(addr)) => { + Some(Address::StructuredAddress(addr)) => { println!("Address:"); println!(" {}", addr.street); println!(" {}, {} {}", addr.city, addr.state, addr.zip_code); println!(" {}", addr.country); } - Some(AddressOneof::FreeformAddress(addr)) => { + Some(Address::FreeformAddress(addr)) => { println!("Address: {addr} (legacy freeform format)"); } None => {} diff --git a/examples/envelope/Cargo.lock b/examples/envelope/Cargo.lock index 9b54992..9e7cf05 100644 --- a/examples/envelope/Cargo.lock +++ b/examples/envelope/Cargo.lock @@ -22,7 +22,7 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "buffa" -version = "0.2.0" +version = "0.3.0" dependencies = [ "base64", "bytes", @@ -35,7 +35,7 @@ dependencies = [ [[package]] name = "buffa-build" -version = "0.2.0" +version = "0.3.0" dependencies = [ "buffa", "buffa-codegen", @@ -44,9 +44,10 @@ dependencies = [ [[package]] name = "buffa-codegen" -version = "0.2.0" +version = "0.3.0" dependencies = [ "buffa", + "buffa-descriptor", "prettyplease", "proc-macro2", "quote", @@ -54,6 +55,13 @@ dependencies = [ "thiserror", ] +[[package]] +name = "buffa-descriptor" +version = "0.3.0" +dependencies = [ + "buffa", +] + [[package]] name = "bytes" version = "1.11.1" diff --git a/examples/envelope/src/main.rs b/examples/envelope/src/main.rs index 5de5f43..1ef6ae9 100644 --- a/examples/envelope/src/main.rs +++ b/examples/envelope/src/main.rs @@ -7,9 +7,8 @@ mod proto { } use buffa::{ExtensionSet, Message}; -use proto::buffa::examples::envelope::{ - Envelope, TraceContext, PRIORITY, RETRY_COUNT, ROUTING_HOPS, TRACE, -}; +use proto::buffa::examples::envelope::ext::{PRIORITY, RETRY_COUNT, ROUTING_HOPS, TRACE}; +use proto::buffa::examples::envelope::{Envelope, TraceContext}; fn main() { binary_roundtrip(); @@ -135,7 +134,7 @@ fn json_roundtrip() { let mut reg = TypeRegistry::new(); // Codegen emits this per file. It registers extension JSON converters, // extension text converters, and `Any` type entries — one call covers all. - proto::buffa::examples::envelope::register_types(&mut reg); + proto::buffa::examples::envelope::ext::register_types(&mut reg); set_type_registry(reg); let mut env = Envelope { diff --git a/examples/logging/Cargo.lock b/examples/logging/Cargo.lock index 252b0cf..b319be1 100644 --- a/examples/logging/Cargo.lock +++ b/examples/logging/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "buffa" -version = "0.2.0" +version = "0.3.0" dependencies = [ "bytes", "hashbrown", @@ -16,9 +16,10 @@ dependencies = [ [[package]] name = "buffa-types" -version = "0.2.0" +version = "0.3.0" dependencies = [ "buffa", + "bytes", "serde", "serde_json", "thiserror", diff --git a/examples/logging/src/gen/context.v1.context.__ext.rs b/examples/logging/src/gen/context.v1.context.__ext.rs new file mode 100644 index 0000000..361f829 --- /dev/null +++ b/examples/logging/src/gen/context.v1.context.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: context/v1/context.proto + diff --git a/examples/logging/src/gen/context.v1.context.__oneofs.rs b/examples/logging/src/gen/context.v1.context.__oneofs.rs new file mode 100644 index 0000000..8c047f2 --- /dev/null +++ b/examples/logging/src/gen/context.v1.context.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: context/v1/context.proto + diff --git a/examples/logging/src/gen/context.v1.context.__view.rs b/examples/logging/src/gen/context.v1.context.__view.rs new file mode 100644 index 0000000..78c8c8c --- /dev/null +++ b/examples/logging/src/gen/context.v1.context.__view.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents, empty) +// source: context/v1/context.proto + diff --git a/examples/logging/src/gen/context.v1.context.__view_oneofs.rs b/examples/logging/src/gen/context.v1.context.__view_oneofs.rs new file mode 100644 index 0000000..aba5ea5 --- /dev/null +++ b/examples/logging/src/gen/context.v1.context.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: context/v1/context.proto + diff --git a/examples/logging/src/gen/context.v1.context.rs b/examples/logging/src/gen/context.v1.context.rs index c49f247..a21f28d 100644 --- a/examples/logging/src/gen/context.v1.context.rs +++ b/examples/logging/src/gen/context.v1.context.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: context/v1/context.proto /// Contextual information about the request that produced a log entry. diff --git a/examples/logging/src/gen/log.v1.log.__ext.rs b/examples/logging/src/gen/log.v1.log.__ext.rs new file mode 100644 index 0000000..566ee2f --- /dev/null +++ b/examples/logging/src/gen/log.v1.log.__ext.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (ext:: module contents, empty) +// source: log/v1/log.proto + diff --git a/examples/logging/src/gen/log.v1.log.__oneofs.rs b/examples/logging/src/gen/log.v1.log.__oneofs.rs new file mode 100644 index 0000000..7a4ecaf --- /dev/null +++ b/examples/logging/src/gen/log.v1.log.__oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (oneofs:: module contents, empty) +// source: log/v1/log.proto + diff --git a/examples/logging/src/gen/log.v1.log.__view.rs b/examples/logging/src/gen/log.v1.log.__view.rs new file mode 100644 index 0000000..e4db9cf --- /dev/null +++ b/examples/logging/src/gen/log.v1.log.__view.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view:: module contents, empty) +// source: log/v1/log.proto + diff --git a/examples/logging/src/gen/log.v1.log.__view_oneofs.rs b/examples/logging/src/gen/log.v1.log.__view_oneofs.rs new file mode 100644 index 0000000..5182629 --- /dev/null +++ b/examples/logging/src/gen/log.v1.log.__view_oneofs.rs @@ -0,0 +1,3 @@ +// @generated by protoc-gen-buffa. DO NOT EDIT. (view::oneofs:: module contents, empty) +// source: log/v1/log.proto + diff --git a/examples/logging/src/gen/log.v1.log.rs b/examples/logging/src/gen/log.v1.log.rs index d544d95..316e664 100644 --- a/examples/logging/src/gen/log.v1.log.rs +++ b/examples/logging/src/gen/log.v1.log.rs @@ -1,4 +1,4 @@ -// @generated by protoc-gen-buffa. DO NOT EDIT. +// @generated by protoc-gen-buffa. DO NOT EDIT. (package-level owned items) // source: log/v1/log.proto /// Severity level for a log entry. @@ -55,6 +55,16 @@ impl ::buffa::Enumeration for Severity { _ => ::core::option::Option::None, } } + fn values() -> &'static [Self] { + &[ + Self::SEVERITY_UNSPECIFIED, + Self::DEBUG, + Self::INFO, + Self::WARN, + Self::ERROR, + Self::FATAL, + ] + } } /// A single structured log entry. #[derive(Clone, PartialEq, Default)] diff --git a/examples/logging/src/gen/mod.rs b/examples/logging/src/gen/mod.rs index 09ea485..9aa2951 100644 --- a/examples/logging/src/gen/mod.rs +++ b/examples/logging/src/gen/mod.rs @@ -14,6 +14,22 @@ pub mod buffa { pub mod v1 { use super::*; include!("context.v1.context.rs"); + #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] + pub mod view { + include!("context.v1.context.__view.rs"); + #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] + pub mod oneofs { + include!("context.v1.context.__view_oneofs.rs"); + } + } + #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] + pub mod ext { + include!("context.v1.context.__ext.rs"); + } + #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] + pub mod oneofs { + include!("context.v1.context.__oneofs.rs"); + } } } #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] @@ -23,6 +39,22 @@ pub mod buffa { pub mod v1 { use super::*; include!("log.v1.log.rs"); + #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] + pub mod view { + include!("log.v1.log.__view.rs"); + #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] + pub mod oneofs { + include!("log.v1.log.__view_oneofs.rs"); + } + } + #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] + pub mod ext { + include!("log.v1.log.__ext.rs"); + } + #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation)] + pub mod oneofs { + include!("log.v1.log.__oneofs.rs"); + } } } } diff --git a/protoc-gen-buffa-packaging/src/main.rs b/protoc-gen-buffa-packaging/src/main.rs index dc7fc61..1dce1d4 100644 --- a/protoc-gen-buffa-packaging/src/main.rs +++ b/protoc-gen-buffa-packaging/src/main.rs @@ -132,8 +132,61 @@ fn generate(request: &CodeGeneratorRequest) -> Result, String>>()?; - let borrowed: Vec<(&str, &str)> = entries.iter().map(|(f, p)| (f.as_str(), *p)).collect(); - let content = buffa_codegen::generate_module_tree(&borrowed, "", true); + // For each owned file, also include its four ancillary siblings: + // `.__view.rs`, `.__ext.rs`, `.__oneofs.rs`, `.__view_oneofs.rs`. + // protoc-gen-buffa always emits all five siblings (empty-bodied if + // there's nothing to put in them) specifically so this packager can + // produce stable `include!` paths without inspecting the filesystem. + let file_names: Vec<(String, String, String, String, String, &str)> = entries + .iter() + .map(|(owned, pkg)| { + let stem = owned + .strip_suffix(".rs") + .unwrap_or(owned.as_str()) + .to_string(); + ( + owned.clone(), + format!("{stem}.__view.rs"), + format!("{stem}.__ext.rs"), + format!("{stem}.__oneofs.rs"), + format!("{stem}.__view_oneofs.rs"), + *pkg, + ) + }) + .collect(); + let module_entries: Vec> = file_names + .iter() + .flat_map(|(owned, view, ext, oneofs, view_oneofs, pkg)| { + [ + buffa_codegen::ModuleTreeEntry { + file_name: owned.as_str(), + package: pkg, + kind: buffa_codegen::GeneratedFileKind::Owned, + }, + buffa_codegen::ModuleTreeEntry { + file_name: view.as_str(), + package: pkg, + kind: buffa_codegen::GeneratedFileKind::View, + }, + buffa_codegen::ModuleTreeEntry { + file_name: ext.as_str(), + package: pkg, + kind: buffa_codegen::GeneratedFileKind::Ext, + }, + buffa_codegen::ModuleTreeEntry { + file_name: oneofs.as_str(), + package: pkg, + kind: buffa_codegen::GeneratedFileKind::Oneofs, + }, + buffa_codegen::ModuleTreeEntry { + file_name: view_oneofs.as_str(), + package: pkg, + kind: buffa_codegen::GeneratedFileKind::ViewOneofs, + }, + ] + }) + .collect(); + let content = buffa_codegen::generate_module_tree(&module_entries, "", true); Ok(CodeGeneratorResponse { supported_features: Some(feature_flags()), diff --git a/stress/googleapis/gen_lib_rs.py b/stress/googleapis/gen_lib_rs.py index cbd17ef..36f2efa 100644 --- a/stress/googleapis/gen_lib_rs.py +++ b/stress/googleapis/gen_lib_rs.py @@ -22,7 +22,6 @@ """ import sys -import os from collections import defaultdict from pathlib import Path @@ -74,47 +73,77 @@ def main(): print("No .rs files found in", gen_dir, file=sys.stderr) sys.exit(1) - # Group files by package path (all segments except the last one, - # which is the proto file name). - # e.g. "google.api.expr.v1alpha1.checked.rs" -> package ["google", "api", "expr", "v1alpha1"] - packages = defaultdict(list) + # Each proto file produces five output siblings: + # - `.rs` (owned tree) + # - `.__view.rs` (view-tree contents, inside `pub mod view`) + # - `.__ext.rs` (ext-tree contents, inside `pub mod ext`) + # - `.__oneofs.rs` (owned oneofs, inside `pub mod oneofs`) + # - `.__view_oneofs.rs` (view-of-oneofs, inside `pub mod view { pub mod oneofs {} }`) + # where `` is the dotted proto path, e.g. + # `google.api.expr.v1alpha1.checked`. + # + # Group them per package and per kind. Inside the module tree we emit + # a single `pub mod view { include!(a.__view.rs); include!(b.__view.rs); }` + # per package (and likewise `pub mod ext { … }`, `pub mod oneofs { … }`) + # so sibling files share one wrapper module instead of colliding on + # per-file wrappers. The `view_oneofs` kind nests inside the `view` + # wrapper: `pub mod view { … pub mod oneofs { } }`. + packages = defaultdict(lambda: {"owned": [], "view": [], "ext": [], "oneofs": [], "view_oneofs": []}) excluded = [] + # Check the longer suffix before the shorter one — `.__view_oneofs` + # must match before `.__view`. for rs_file in rs_files: if rs_file.name in exclude_files: excluded.append(rs_file.name) continue stem = rs_file.stem # e.g. "google.api.expr.v1alpha1.checked" + kind = "owned" + if stem.endswith(".__view_oneofs"): + kind = "view_oneofs" + stem = stem[: -len(".__view_oneofs")] + elif stem.endswith(".__view"): + kind = "view" + stem = stem[: -len(".__view")] + elif stem.endswith(".__ext"): + kind = "ext" + stem = stem[: -len(".__ext")] + elif stem.endswith(".__oneofs"): + kind = "oneofs" + stem = stem[: -len(".__oneofs")] parts = stem.split(".") # The package is everything except the last segment. pkg = tuple(parts[:-1]) - packages[pkg].append(rs_file.name) + packages[pkg][kind].append(rs_file.name) if excluded: print(f"Excluded {len(excluded)} files: {', '.join(excluded)}", file=sys.stderr) - # Build a tree structure. - tree = {} - for pkg, files in packages.items(): - node = tree - for seg in pkg: - if seg not in node: - node[seg] = {"__children": {}, "__files": []} - node = node[seg]["__children"] - # We're past all segments; go back to the last node. - # Actually, let me restructure: the files go on the package node. - pass + # Build the tree directly. Each node tracks per-kind file lists so we + # can emit a single wrapper per kind per package. + def new_node(): + return { + "__owned": [], + "__view": [], + "__ext": [], + "__oneofs": [], + "__view_oneofs": [], + "__children": {}, + } - # Simpler approach: build the tree directly. - tree = {"__files": [], "__children": {}} + tree = new_node() - for pkg, files in packages.items(): + for pkg, kinds in packages.items(): node = tree for seg in pkg: if seg not in node["__children"]: - node["__children"][seg] = {"__files": [], "__children": {}} + node["__children"][seg] = new_node() node = node["__children"][seg] - node["__files"].extend(files) + node["__owned"].extend(kinds["owned"]) + node["__view"].extend(kinds["view"]) + node["__ext"].extend(kinds["ext"]) + node["__oneofs"].extend(kinds["oneofs"]) + node["__view_oneofs"].extend(kinds["view_oneofs"]) # Generate lib.rs. lines = [ @@ -127,8 +156,32 @@ def main(): def emit(node, indent=0): prefix = " " * indent - for filename in sorted(node["__files"]): + for filename in sorted(node["__owned"]): lines.append(f'{prefix}include!("{include_prefix}{filename}");') + if node["__view"] or node["__view_oneofs"]: + lines.append(f"{prefix}#[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding)]") + lines.append(f"{prefix}pub mod view {{") + for filename in sorted(node["__view"]): + lines.append(f'{prefix} include!("{include_prefix}{filename}");') + if node["__view_oneofs"]: + lines.append(f"{prefix} #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding)]") + lines.append(f"{prefix} pub mod oneofs {{") + for filename in sorted(node["__view_oneofs"]): + lines.append(f'{prefix} include!("{include_prefix}{filename}");') + lines.append(f"{prefix} }}") + lines.append(f"{prefix}}}") + if node["__ext"]: + lines.append(f"{prefix}#[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding)]") + lines.append(f"{prefix}pub mod ext {{") + for filename in sorted(node["__ext"]): + lines.append(f'{prefix} include!("{include_prefix}{filename}");') + lines.append(f"{prefix}}}") + if node["__oneofs"]: + lines.append(f"{prefix}#[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding)]") + lines.append(f"{prefix}pub mod oneofs {{") + for filename in sorted(node["__oneofs"]): + lines.append(f'{prefix} include!("{include_prefix}{filename}");') + lines.append(f"{prefix}}}") for seg in sorted(node["__children"].keys()): child = node["__children"][seg] escaped = escape_ident(seg)