diff --git a/.changes/0.8.1.md b/.changes/0.8.1.md new file mode 100644 index 00000000..a0bf6bf6 --- /dev/null +++ b/.changes/0.8.1.md @@ -0,0 +1,33 @@ +## [0.8.1] - 2026-07-01 + +A single-fix patch release: unknown-field limit accounting for zero-copy +views now matches owned conversion exactly, establishing the guarantee that +a view which decodes successfully always converts to an owned message. No +API changes, and regenerating code is not required — the fix lives in the +runtime. + +### Fixed + +- **Zero-copy view decoding now charges the unknown-field limit per + re-materializable field** — one per unknown record plus, for unknown group + records, one per nested field — instead of one per coalesced span, and + conversion replays under exactly the field budget and group-nesting depth + recorded at decode time (previously a fixed recursion limit, which could + reject deep unknown groups decoded under a raised `with_recursion_limit`). + Decode-time accounting now matches what `to_owned_message` re-materializes, + which gives views a guarantee: **a view produced by `decode_view` always + converts via `to_owned_message` without error** (the `Result` remains for + hand-written impls and `push_raw`-built views). **Behavioral tightening:** + payloads whose unknown-field count exceeds the limit but previously slipped + through view decode via span coalescing — e.g. a ~2 MiB run of >1M + contiguous 2-byte unknown records under the default limit, or an unknown + group with more nested fields than the limit — now fail at view decode with + `UnknownFieldLimitExceeded`. Consumers that converted such views already + got this error at conversion; view-only consumers that re-encoded such + payloads without converting (e.g. a zero-copy passthrough proxy) now see it + at decode — raise the bound with `DecodeOptions::with_unknown_field_limit` + if such payloads are trusted and expected. The accounting lives in the + runtime (`UnknownFieldsView::push_record`), so previously generated code is + fixed without regeneration. (#266) + +[0.8.1]: https://github.com/anthropics/buffa/compare/v0.8.0...v0.8.1 diff --git a/.changes/unreleased/breaking-changes-20260701-165230.yaml b/.changes/unreleased/breaking-changes-20260701-165230.yaml new file mode 100644 index 00000000..6b71bb9c --- /dev/null +++ b/.changes/unreleased/breaking-changes-20260701-165230.yaml @@ -0,0 +1,4 @@ +kind: Breaking changes +body: |- + `OwnedView::to_owned_message` and the generated `FooOwnedView::to_owned_message` are now **infallible**, returning the owned message directly instead of `Result<_, DecodeError>`. Every `OwnedView` constructor wire-decodes its view (or, for `unsafe from_parts`, requires wire-decode provenance as part of its strengthened safety contract), and since 0.8.1 a view produced by wire decoding always converts — so the `Result` only ever encoded an unreachable error path. Migration: on call sites whose receiver is an `OwnedView` or a generated `FooOwnedView` handle, delete the `?` / `.unwrap()` / `.expect(...)` — or otherwise unwrap the previously-returned `Result` (a `match` or `.map_err(...)` needs the same treatment). Call sites on plain view types (`FooView::to_owned_message` via the `MessageView` trait) are unchanged and stay fallible, since hand-written impls and `push_raw`-built views can still legitimately fail. **`unsafe from_parts` callers**: the safety contract now requires that the view was produced by wire-decoding the buffer, not merely that its borrows point into it — a hand-assembled view that was legal under the old wording still compiles but now panics at `to_owned_message`, so audit `from_parts` call sites for provenance. A contract violation by a buggy hand-written `MessageView` impl wrapped in `OwnedView` likewise panics with a descriptive message instead of surfacing an error that correct code could never observe. (#268) +time: 2026-07-01T16:52:30.000000000Z diff --git a/.changes/unreleased/fixed-20260701-041207.yaml b/.changes/unreleased/fixed-20260701-041207.yaml deleted file mode 100644 index 020f3d62..00000000 --- a/.changes/unreleased/fixed-20260701-041207.yaml +++ /dev/null @@ -1,4 +0,0 @@ -kind: Fixed -body: |- - Zero-copy view decoding now charges the unknown-field limit per re-materializable field — one per unknown record plus, for unknown group records, one per nested field — instead of one per coalesced span, and conversion replays under exactly the field budget and group-nesting depth recorded at decode time (previously a fixed recursion limit, which could reject deep unknown groups decoded under a raised `with_recursion_limit`). Decode-time accounting now matches what `to_owned_message` re-materializes, which gives views a guarantee: **a view produced by `decode_view` always converts via `to_owned_message` without error** (the `Result` remains for hand-written impls and `push_raw`-built views). **Behavioral tightening:** payloads whose unknown-field count exceeds the limit but previously slipped through view decode via span coalescing — e.g. a ~2 MiB run of >1M contiguous 2-byte unknown records under the default limit, or an unknown group with more nested fields than the limit — now fail at view decode with `UnknownFieldLimitExceeded`. Consumers that converted such views already got this error at conversion; view-only consumers that re-encoded such payloads without converting (e.g. a zero-copy passthrough proxy) now see it at decode — raise the bound with `DecodeOptions::with_unknown_field_limit` if such payloads are trusted and expected. The accounting lives in the runtime (`UnknownFieldsView::push_record`), so previously generated code is fixed without regeneration. (#266) -time: 2026-07-01T04:12:07.000000000Z diff --git a/CHANGELOG.md b/CHANGELOG.md index 7810be09..deddbd6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), Entries for unreleased changes live as fragment files under [`.changes/unreleased/`](.changes/unreleased/); run `task changelog-new` to add one. This file is assembled at release time — do not edit it directly. +## [0.8.1] - 2026-07-01 + +A single-fix patch release: unknown-field limit accounting for zero-copy +views now matches owned conversion exactly, establishing the guarantee that +a view which decodes successfully always converts to an owned message. No +API changes, and regenerating code is not required — the fix lives in the +runtime. + +### Fixed + +- **Zero-copy view decoding now charges the unknown-field limit per + re-materializable field** — one per unknown record plus, for unknown group + records, one per nested field — instead of one per coalesced span, and + conversion replays under exactly the field budget and group-nesting depth + recorded at decode time (previously a fixed recursion limit, which could + reject deep unknown groups decoded under a raised `with_recursion_limit`). + Decode-time accounting now matches what `to_owned_message` re-materializes, + which gives views a guarantee: **a view produced by `decode_view` always + converts via `to_owned_message` without error** (the `Result` remains for + hand-written impls and `push_raw`-built views). **Behavioral tightening:** + payloads whose unknown-field count exceeds the limit but previously slipped + through view decode via span coalescing — e.g. a ~2 MiB run of >1M + contiguous 2-byte unknown records under the default limit, or an unknown + group with more nested fields than the limit — now fail at view decode with + `UnknownFieldLimitExceeded`. Consumers that converted such views already + got this error at conversion; view-only consumers that re-encoded such + payloads without converting (e.g. a zero-copy passthrough proxy) now see it + at decode — raise the bound with `DecodeOptions::with_unknown_field_limit` + if such payloads are trusted and expected. The accounting lives in the + runtime (`UnknownFieldsView::push_record`), so previously generated code is + fixed without regeneration. (#266) + +[0.8.1]: https://github.com/anthropics/buffa/compare/v0.8.0...v0.8.1 + ## [0.8.0] - 2026-06-25 The headline of this release is that the owned representation of every field diff --git a/buffa-codegen/src/owned_view.rs b/buffa-codegen/src/owned_view.rs index 2b983a4e..8be495c1 100644 --- a/buffa-codegen/src/owned_view.rs +++ b/buffa-codegen/src/owned_view.rs @@ -283,13 +283,13 @@ pub(crate) fn generate_owned_view_wrapper( /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result<#owned_path, ::buffa::DecodeError> { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> #owned_path { self.0.to_owned_message() } diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs index 5deedcf3..dc04a62c 100644 --- a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.__view.rs @@ -246,13 +246,13 @@ impl VersionOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Version { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -708,16 +708,13 @@ impl CodeGeneratorRequestOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::CodeGeneratorRequest, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::CodeGeneratorRequest { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -1159,16 +1156,13 @@ impl CodeGeneratorResponseOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::CodeGeneratorResponse, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::CodeGeneratorResponse { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -1619,16 +1613,15 @@ pub mod code_generator_response { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::code_generator_response::File, - ::buffa::DecodeError, - > { + ) -> super::super::super::code_generator_response::File { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs index 90f9355b..1d9fde9e 100644 --- a/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs @@ -214,13 +214,13 @@ impl FileDescriptorSetOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FileDescriptorSet { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -955,16 +955,13 @@ impl FileDescriptorProtoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::FileDescriptorProto, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FileDescriptorProto { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -1712,13 +1709,13 @@ impl DescriptorProtoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::DescriptorProto { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -2143,16 +2140,15 @@ pub mod descriptor_proto { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::descriptor_proto::ExtensionRange, - ::buffa::DecodeError, - > { + ) -> super::super::super::descriptor_proto::ExtensionRange { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -2443,16 +2439,15 @@ pub mod descriptor_proto { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::descriptor_proto::ReservedRange, - ::buffa::DecodeError, - > { + ) -> super::super::super::descriptor_proto::ReservedRange { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -2871,16 +2866,13 @@ impl ExtensionRangeOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::ExtensionRangeOptions, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::ExtensionRangeOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -3265,16 +3257,15 @@ pub mod extension_range_options { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::extension_range_options::Declaration, - ::buffa::DecodeError, - > { + ) -> super::super::super::extension_range_options::Declaration { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -3848,16 +3839,13 @@ impl FieldDescriptorProtoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::FieldDescriptorProto, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FieldDescriptorProto { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -4258,16 +4246,13 @@ impl OneofDescriptorProtoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::OneofDescriptorProto, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::OneofDescriptorProto { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -4712,16 +4697,13 @@ impl EnumDescriptorProtoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::EnumDescriptorProto, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::EnumDescriptorProto { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -5047,16 +5029,15 @@ pub mod enum_descriptor_proto { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::enum_descriptor_proto::EnumReservedRange, - ::buffa::DecodeError, - > { + ) -> super::super::super::enum_descriptor_proto::EnumReservedRange { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -5384,16 +5365,13 @@ impl EnumValueDescriptorProtoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::EnumValueDescriptorProto, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::EnumValueDescriptorProto { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -5745,16 +5723,13 @@ impl ServiceDescriptorProtoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::ServiceDescriptorProto, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::ServiceDescriptorProto { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -6154,16 +6129,13 @@ impl MethodDescriptorProtoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::MethodDescriptorProto, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::MethodDescriptorProto { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -7011,13 +6983,13 @@ impl FileOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FileOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -7707,13 +7679,13 @@ impl MessageOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::MessageOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -8561,13 +8533,13 @@ impl FieldOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FieldOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -9008,16 +8980,15 @@ pub mod field_options { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::field_options::EditionDefault, - ::buffa::DecodeError, - > { + ) -> super::super::super::field_options::EditionDefault { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -9383,16 +9354,15 @@ pub mod field_options { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::field_options::FeatureSupport, - ::buffa::DecodeError, - > { + ) -> super::super::super::field_options::FeatureSupport { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -9747,13 +9717,13 @@ impl OneofOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::OneofOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -10168,13 +10138,13 @@ impl EnumOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::EnumOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -10646,13 +10616,13 @@ impl EnumValueOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::EnumValueOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -11051,13 +11021,13 @@ impl ServiceOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::ServiceOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -11473,13 +11443,13 @@ impl MethodOptionsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::MethodOptions { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -11923,16 +11893,13 @@ impl UninterpretedOptionOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result< - super::super::UninterpretedOption, - ::buffa::DecodeError, - > { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::UninterpretedOption { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -12255,16 +12222,15 @@ Distinguishes a field that was absent from one explicitly encoded with its defau } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::uninterpreted_option::NamePart, - ::buffa::DecodeError, - > { + ) -> super::super::super::uninterpreted_option::NamePart { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -12763,13 +12729,13 @@ impl FeatureSetOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FeatureSet { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -13054,16 +13020,15 @@ pub mod feature_set { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::feature_set::VisibilityFeature, - ::buffa::DecodeError, - > { + ) -> super::super::super::feature_set::VisibilityFeature { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -13397,13 +13362,13 @@ impl FeatureSetDefaultsOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FeatureSetDefaults { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -13822,16 +13787,15 @@ pub mod feature_set_defaults { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::feature_set_defaults::FeatureSetEditionDefault, - ::buffa::DecodeError, - > { + ) -> super::super::super::feature_set_defaults::FeatureSetEditionDefault { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -14168,13 +14132,13 @@ impl SourceCodeInfoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::SourceCodeInfo { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -14705,16 +14669,15 @@ pub mod source_code_info { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::source_code_info::Location, - ::buffa::DecodeError, - > { + ) -> super::super::super::source_code_info::Location { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -15086,13 +15049,13 @@ impl GeneratedCodeInfoOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::GeneratedCodeInfo { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -15484,16 +15447,15 @@ pub mod generated_code_info { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] pub fn to_owned_message( &self, - ) -> ::core::result::Result< - super::super::super::generated_code_info::Annotation, - ::buffa::DecodeError, - > { + ) -> super::super::super::generated_code_info::Annotation { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa-test/src/tests/box_type.rs b/buffa-test/src/tests/box_type.rs index 81f298db..3f4b8c5e 100644 --- a/buffa-test/src/tests/box_type.rs +++ b/buffa-test/src/tests/box_type.rs @@ -86,8 +86,7 @@ fn view_to_owned_round_trip() { let bytes = bytes::Bytes::from(sample().encode_to_vec()); let owned: Outer = crate::box_type::OuterOwnedView::decode(bytes) .expect("decode view") - .to_owned_message() - .expect("to_owned"); + .to_owned_message(); assert_eq!(owned, sample()); } @@ -145,7 +144,6 @@ fn oneof_view_to_owned_round_trip() { let bytes = bytes::Bytes::from(msg.encode_to_vec()); let owned: WithOneof = crate::box_type::WithOneofOwnedView::decode(bytes) .expect("decode view") - .to_owned_message() - .expect("to_owned"); + .to_owned_message(); assert_eq!(owned, msg); } diff --git a/buffa-test/src/tests/inline_field.rs b/buffa-test/src/tests/inline_field.rs index 7f3e4454..c6c2581e 100644 --- a/buffa-test/src/tests/inline_field.rs +++ b/buffa-test/src/tests/inline_field.rs @@ -83,7 +83,6 @@ fn view_to_owned_round_trip() { let bytes = bytes::Bytes::from(sample().encode_to_vec()); let owned: Outer = crate::inline_field::OuterOwnedView::decode(bytes) .expect("decode view") - .to_owned_message() - .expect("to_owned"); + .to_owned_message(); assert_eq!(owned, sample()); } diff --git a/buffa-test/src/tests/map_type.rs b/buffa-test/src/tests/map_type.rs index ec4de47d..b041f4e5 100644 --- a/buffa-test/src/tests/map_type.rs +++ b/buffa-test/src/tests/map_type.rs @@ -118,8 +118,7 @@ fn view_to_owned_round_trip() { let bytes = bytes::Bytes::from(sample().encode_to_vec()); let owned: Maps = crate::map_type::MapsOwnedView::decode(bytes) .expect("decode view") - .to_owned_message() - .expect("to_owned"); + .to_owned_message(); assert_eq!(owned, sample()); } diff --git a/buffa-test/src/tests/map_type_custom.rs b/buffa-test/src/tests/map_type_custom.rs index 65ff189b..8f77775a 100644 --- a/buffa-test/src/tests/map_type_custom.rs +++ b/buffa-test/src/tests/map_type_custom.rs @@ -81,7 +81,6 @@ fn view_to_owned_round_trip() { let bytes = bytes::Bytes::from(sample().encode_to_vec()); let owned: CustomMaps = crate::map_type_custom::CustomMapsOwnedView::decode(bytes) .expect("decode view") - .to_owned_message() - .expect("to_owned"); + .to_owned_message(); assert_eq!(owned, sample()); } diff --git a/buffa-test/src/tests/owned_view.rs b/buffa-test/src/tests/owned_view.rs index 9edb6ee0..252c3873 100644 --- a/buffa-test/src/tests/owned_view.rs +++ b/buffa-test/src/tests/owned_view.rs @@ -55,7 +55,7 @@ fn test_owned_view_wrapper_view_escape_hatch() { fn test_owned_view_wrapper_owned_roundtrip() { let msg = sample_person(); let owned = PersonOwnedView::from_owned(&msg).expect("from_owned"); - let back: Person = owned.to_owned_message().unwrap(); + let back: Person = owned.to_owned_message(); assert_eq!(back, msg); } @@ -183,7 +183,7 @@ mod view_family { let raw = handle.as_ref(); let _view = raw.reborrow(); let len = raw.bytes().len(); - (raw.to_owned_message().unwrap(), len) + (raw.to_owned_message(), len) } #[test] diff --git a/buffa-test/src/tests/repeated_type.rs b/buffa-test/src/tests/repeated_type.rs index 5e2f8827..5799d1e2 100644 --- a/buffa-test/src/tests/repeated_type.rs +++ b/buffa-test/src/tests/repeated_type.rs @@ -73,7 +73,6 @@ fn view_to_owned_round_trip() { let bytes = bytes::Bytes::from(sample().encode_to_vec()); let owned: Lists = crate::repeated_type::ListsOwnedView::decode(bytes) .expect("decode view") - .to_owned_message() - .expect("to_owned"); + .to_owned_message(); assert_eq!(owned, sample()); } diff --git a/buffa-test/tests/string_map.rs b/buffa-test/tests/string_map.rs index 92c60dd3..4f12d80d 100644 --- a/buffa-test/tests/string_map.rs +++ b/buffa-test/tests/string_map.rs @@ -117,8 +117,7 @@ fn view_to_owned_round_trip() { let bytes = bytes::Bytes::from(sample().encode_to_vec()); let owned: Maps = buffa_test::string_map::MapsOwnedView::decode(bytes) .expect("decode view") - .to_owned_message() - .expect("to_owned"); + .to_owned_message(); assert_eq!(owned, sample()); } diff --git a/buffa-types/src/any_ext.rs b/buffa-types/src/any_ext.rs index 724959f6..98e4cd07 100644 --- a/buffa-types/src/any_ext.rs +++ b/buffa-types/src/any_ext.rs @@ -376,7 +376,7 @@ mod tests { // through to_owned_from_source(Some(&self.bytes)), so the bytes field // is a zero-copy slice_ref into the retained buffer. let ov = OwnedView::>::decode(buf.clone()).unwrap(); - let owned2 = ov.to_owned_message().unwrap(); + let owned2 = ov.to_owned_message(); assert_eq!(owned2.value, src.value); assert!(buf_range.contains(&(owned2.value.as_ptr() as usize))); diff --git a/buffa-types/src/generated/google.protobuf.any.__view.rs b/buffa-types/src/generated/google.protobuf.any.__view.rs index 11738529..77fd6412 100644 --- a/buffa-types/src/generated/google.protobuf.any.__view.rs +++ b/buffa-types/src/generated/google.protobuf.any.__view.rs @@ -302,13 +302,13 @@ impl AnyOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Any { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa-types/src/generated/google.protobuf.duration.__view.rs b/buffa-types/src/generated/google.protobuf.duration.__view.rs index e6588216..ff22a398 100644 --- a/buffa-types/src/generated/google.protobuf.duration.__view.rs +++ b/buffa-types/src/generated/google.protobuf.duration.__view.rs @@ -251,13 +251,13 @@ impl DurationOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Duration { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa-types/src/generated/google.protobuf.empty.__view.rs b/buffa-types/src/generated/google.protobuf.empty.__view.rs index 704561b3..4b403f82 100644 --- a/buffa-types/src/generated/google.protobuf.empty.__view.rs +++ b/buffa-types/src/generated/google.protobuf.empty.__view.rs @@ -151,13 +151,13 @@ impl EmptyOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Empty { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa-types/src/generated/google.protobuf.field_mask.__view.rs b/buffa-types/src/generated/google.protobuf.field_mask.__view.rs index 31741ba8..7d16655e 100644 --- a/buffa-types/src/generated/google.protobuf.field_mask.__view.rs +++ b/buffa-types/src/generated/google.protobuf.field_mask.__view.rs @@ -387,13 +387,13 @@ impl FieldMaskOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FieldMask { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa-types/src/generated/google.protobuf.struct.__view.rs b/buffa-types/src/generated/google.protobuf.struct.__view.rs index f416bcbf..beddae53 100644 --- a/buffa-types/src/generated/google.protobuf.struct.__view.rs +++ b/buffa-types/src/generated/google.protobuf.struct.__view.rs @@ -249,13 +249,13 @@ impl StructOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Struct { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -753,13 +753,13 @@ impl ValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Value { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -1187,13 +1187,13 @@ impl ListValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::ListValue { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa-types/src/generated/google.protobuf.timestamp.__view.rs b/buffa-types/src/generated/google.protobuf.timestamp.__view.rs index dbbc94a6..d42db5af 100644 --- a/buffa-types/src/generated/google.protobuf.timestamp.__view.rs +++ b/buffa-types/src/generated/google.protobuf.timestamp.__view.rs @@ -288,13 +288,13 @@ impl TimestampOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Timestamp { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa-types/src/generated/google.protobuf.wrappers.__view.rs b/buffa-types/src/generated/google.protobuf.wrappers.__view.rs index 88ebff4d..292bef2e 100644 --- a/buffa-types/src/generated/google.protobuf.wrappers.__view.rs +++ b/buffa-types/src/generated/google.protobuf.wrappers.__view.rs @@ -167,13 +167,13 @@ impl DoubleValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::DoubleValue { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -472,13 +472,13 @@ impl FloatValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::FloatValue { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -777,13 +777,13 @@ impl Int64ValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Int64Value { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -1082,13 +1082,13 @@ impl UInt64ValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::UInt64Value { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -1387,13 +1387,13 @@ impl Int32ValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::Int32Value { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -1692,13 +1692,13 @@ impl UInt32ValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::UInt32Value { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -1997,13 +1997,13 @@ impl BoolValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::BoolValue { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -2302,13 +2302,13 @@ impl StringValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::StringValue { self.0.to_owned_message() } /// The underlying bytes buffer. @@ -2607,13 +2607,13 @@ impl BytesValueOwnedView { } /// Convert to the owned message type. /// - /// # Errors - /// - /// Returns an error if re-materializing preserved unknown fields - /// fails (e.g. the unknown-field limit is exceeded). - pub fn to_owned_message( - &self, - ) -> ::core::result::Result { + /// Infallible: this type's constructors wire-decode their + /// buffer, and a view produced by wire decoding always + /// converts. Delegates to [`::buffa::OwnedView::to_owned_message`], + /// whose contract also governs handles converted from a raw + /// [`::buffa::OwnedView`]. + #[must_use] + pub fn to_owned_message(&self) -> super::super::BytesValue { self.0.to_owned_message() } /// The underlying bytes buffer. diff --git a/buffa/src/view.rs b/buffa/src/view.rs index fae3e609..2db653ab 100644 --- a/buffa/src/view.rs +++ b/buffa/src/view.rs @@ -307,6 +307,10 @@ pub trait MessageView<'a>: Sized { /// budget and group-nesting depth recorded at decode time. The `Result` /// remains for hand-written view impls and for views holding manually /// pushed ([`UnknownFieldsView::push_raw`]) unknown-field spans. + /// [`OwnedView`], which always holds a wire-decoded view, exposes this + /// conversion infallibly — see + /// [`OwnedView::to_owned_message`](OwnedView::to_owned_message) (and the + /// generated `FooOwnedView` wrappers likewise). /// /// # Errors /// @@ -1934,8 +1938,9 @@ impl<'a> UnknownFieldsView<'a> { /// println!("name: {}", person.name); /// println!("id: {}", person.id); /// -/// // Convert to owned if you need to store or mutate -/// let owned: Person = view.to_owned_message()?; +/// // Convert to owned if you need to store or mutate — infallible, since +/// // an OwnedView always holds a wire-decoded view. +/// let owned: Person = view.to_owned_message(); /// ``` /// /// Generated code additionally provides a per-message `FooOwnedView` wrapper @@ -2029,6 +2034,18 @@ impl Drop for OwnedView { } } +/// Panic path for [`OwnedView::to_owned_message`], hoisted out of the +/// generic method so the format machinery is emitted once rather than per +/// monomorphization — the branch is unreachable for generated view types. +#[cold] +#[inline(never)] +fn convert_contract_violated(e: DecodeError) -> ! { + panic!( + "OwnedView conversion failed ({e:?}): the wrapped MessageView \ + impl violates the wire-decode => convert contract" + ) +} + impl OwnedView where V: MessageView<'static>, @@ -2104,12 +2121,25 @@ where /// into the retained buffer (zero-copy); other borrowed fields are /// allocated and copied. /// - /// # Errors + /// Infallible: every `OwnedView` constructor wire-decodes its view + /// ([`decode`](Self::decode), [`decode_with_options`](Self::decode_with_options), + /// [`from_owned`](Self::from_owned)) or requires wire-decode provenance + /// as part of its safety contract ([`from_parts`](Self::from_parts)), + /// and a view produced by wire decoding always converts (see + /// [`MessageView::to_owned_message`]). /// - /// Returns an error if re-materializing preserved unknown fields fails - /// (see [`MessageView::to_owned_message`]). - pub fn to_owned_message(&self) -> Result { - self.view.to_owned_from_source(Some(&self.bytes)) + /// # Panics + /// + /// Panics if `V`'s [`MessageView`] implementation violates the + /// wire-decode ⇒ convert contract. Generated view types cannot trigger + /// this; only a buggy hand-written impl (or a + /// [`from_parts`](Self::from_parts) view that breaches its safety + /// contract) can. + #[must_use] + pub fn to_owned_message(&self) -> V::Owned { + self.view + .to_owned_from_source(Some(&self.bytes)) + .unwrap_or_else(|e| convert_contract_violated(e)) } /// Get a reference to the underlying bytes buffer. @@ -2124,10 +2154,15 @@ where /// /// # Safety /// - /// The caller must ensure that **all** borrows in `view` point into the - /// data region of `bytes`. In practice, `view` must have been decoded - /// from `bytes` (or a sub-slice that `bytes` fully contains). Violating - /// this invariant causes undefined behavior (dangling references). + /// The caller must ensure that `view` was produced by **wire-decoding** + /// `bytes` (or a sub-slice that `bytes` fully contains) — e.g. via + /// [`MessageView::decode_view`]. This guarantees both that all borrows + /// in `view` point into the data region of `bytes` (violating that + /// causes undefined behavior — dangling references) and that + /// [`to_owned_message`](Self::to_owned_message)'s infallible-conversion + /// contract holds (a manually assembled view merely borrowing from + /// `bytes` satisfies the borrow requirement but can make conversion + /// panic). pub unsafe fn from_parts(bytes: Bytes, view: V) -> Self { Self { view: core::mem::ManuallyDrop::new(view), @@ -2674,8 +2709,8 @@ mod tests { fn to_owned_of_manual_flood_over_default_limit_fails() { // The replay limit stays live for manually built views: push_raw // spans get the default allowance, no more — the memory- - // amplification bound survives even though wire-decoded views can - // no longer hit it. + // amplification bound survives even though wire-decoded views + // cannot hit it (decode charges the allowance up front). let n = crate::DEFAULT_UNKNOWN_FIELD_LIMIT + 1; let mut buf = alloc::vec::Vec::with_capacity(2 * n); for _ in 0..n { @@ -3127,12 +3162,47 @@ mod tests { fn owned_view_to_owned_message() { let bytes = encode_simple(7, "world"); let view = OwnedView::>::decode(bytes).unwrap(); - let owned = view.to_owned_message().unwrap(); + let owned = view.to_owned_message(); assert_eq!(owned.id, 7); assert_eq!(owned.name, "world"); } + /// A view whose `to_owned_message` breaks the wire-decode ⇒ convert + /// contract, standing in for a buggy hand-written impl. + #[derive(Clone, Debug, Default, PartialEq)] + struct ContractBreakingView<'a>(core::marker::PhantomData<&'a ()>); + + crate::impl_view_reborrow!(ContractBreakingView); + + impl<'a> MessageView<'a> for ContractBreakingView<'a> { + type Owned = SimpleMessage; + fn merge_view_field( + &mut self, + _tag: crate::encoding::Tag, + cur: &'a [u8], + _before_tag: &'a [u8], + _ctx: crate::DecodeContext<'_>, + ) -> Result<&'a [u8], DecodeError> { + Ok(cur) + } + fn decode_view(_buf: &'a [u8]) -> Result { + Ok(Self(core::marker::PhantomData)) + } + fn to_owned_message(&self) -> Result { + Err(DecodeError::UnknownFieldLimitExceeded) + } + } + + #[test] + #[should_panic(expected = "wire-decode => convert contract")] + fn owned_view_panics_when_impl_breaks_convert_contract() { + // The infallible signature rests on the decode ⇒ convert invariant; + // an impl that violates it must fail loudly, not silently. + let view = OwnedView::>::decode(Bytes::new()).unwrap(); + let _ = view.to_owned_message(); + } + #[test] fn owned_view_debug_delegates_to_view() { let bytes = encode_simple(1, "test"); @@ -3193,7 +3263,7 @@ mod tests { assert_eq!(view.reborrow().id, 99); assert_eq!(view.reborrow().name, "roundtrip"); - let back = view.to_owned_message().unwrap(); + let back = view.to_owned_message(); assert_eq!(back, msg); } diff --git a/docs/guide.md b/docs/guide.md index c5754abe..e45619a3 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -1033,8 +1033,9 @@ let view = PersonView::decode_view(&bytes)?; println!("name: {}", view.name); // &str, no allocation // Convert to owned when needed (e.g., for storage or mutation). -// Infallible for views produced by decode_view; the Result covers -// hand-written view impls. +// Won't error for views from decode_view, but the signature stays +// Result because hand-written view impls can fail; the OwnedView +// handle below drops the Result entirely. let owned: Person = view.to_owned_message()?; ``` @@ -1123,8 +1124,9 @@ println!("id: {}", view.id()); let person = view.view(); for tag in person.tags.iter() { /* ... */ } -// Convert to owned if needed for storage or mutation -let owned: Person = view.to_owned_message()?; +// Convert to owned if needed for storage or mutation — infallible, since +// the handle always holds a wire-decoded view +let owned: Person = view.to_owned_message(); ``` When working with the generic `OwnedView` directly (for example, a request type handed to you by an RPC framework), reach the inner view with `reborrow()`, which ties the borrow to the `OwnedView` itself: `let person = view.reborrow();` then `person.name`. Field access directly on the handle is deliberately not provided — the stored view's lifetime is a synthetic `'static`, and exposing it would let field borrows outlive the buffer they point into. diff --git a/docs/migration-from-prost.md b/docs/migration-from-prost.md index d1d8d34f..3d35dbb8 100644 --- a/docs/migration-from-prost.md +++ b/docs/migration-from-prost.md @@ -251,7 +251,7 @@ use my_crate::pkg::__buffa::view::PersonView; let view = PersonView::decode_view(&bytes)?; println!("name: {}", view.name); // &str, no allocation -let owned: Person = view.to_owned_message(); +let owned: Person = view.to_owned_message()?; ``` ### `OwnedView` for async/RPC use