Skip to main content

buffa_types/
lib.rs

1//! Protobuf well-known types for buffa.
2//!
3//! This crate provides Rust types for Google's well-known `.proto` types:
4//!
5//! - [`google::protobuf::Timestamp`] — Unix timestamp with nanosecond precision
6//! - [`google::protobuf::Duration`] — Signed duration with nanosecond precision
7//! - [`google::protobuf::Any`] — Any value with an attached type URL
8//! - [`google::protobuf::Struct`] / [`google::protobuf::Value`] / [`google::protobuf::ListValue`]
9//!   — JSON-like dynamic values
10//! - [`google::protobuf::FieldMask`] — Specifies a subset of fields referenced in a message
11//! - [`google::protobuf::Empty`] — A generic empty message
12//! - [`google::protobuf::Api`] / [`google::protobuf::Type`] / [`google::protobuf::Enum`] /
13//!   [`google::protobuf::SourceContext`] — API and type descriptors used by googleapis
14//!   (`api.proto`, `type.proto`, `source_context.proto`), with their parts
15//!   [`google::protobuf::Method`], [`google::protobuf::Mixin`], [`google::protobuf::Field`],
16//!   [`google::protobuf::EnumValue`], and [`google::protobuf::Option`]. That last one
17//!   shadows the prelude `Option` in any module that glob-imports `google::protobuf::*`;
18//!   it is not re-exported at the crate root.
19//! - Wrapper types: [`google::protobuf::BoolValue`], [`google::protobuf::Int32Value`],
20//!   [`google::protobuf::Int64Value`], [`google::protobuf::UInt32Value`],
21//!   [`google::protobuf::UInt64Value`], [`google::protobuf::FloatValue`],
22//!   [`google::protobuf::DoubleValue`], [`google::protobuf::StringValue`],
23//!   [`google::protobuf::BytesValue`]
24//!
25//! # Usage
26//!
27//! ```rust,no_run
28//! use buffa_types::google::protobuf::Timestamp;
29//! use buffa::Message;
30//!
31//! let ts = Timestamp { seconds: 1_000_000_000, nanos: 0, ..Default::default() };
32//! let bytes = ts.encode_to_vec();
33//! let decoded = Timestamp::decode_from_slice(&bytes).unwrap();
34//! assert_eq!(ts, decoded);
35//! ```
36//!
37//! # Ergonomic helpers
38//!
39//! Common Rust type conversions are provided as trait impls:
40//!
41//! - `Timestamp` ↔ [`std::time::SystemTime`] (requires `std` feature)
42//! - `Duration` ↔ [`std::time::Duration`] (requires `std` feature)
43//! - `Timestamp` ↔ [`chrono::DateTime`] (requires `chrono` feature; any time
44//!   zone in, `Utc` out)
45//! - `Duration` ↔ [`chrono::TimeDelta`] (requires `chrono` feature)
46//! - `Timestamp` ↔ [`jiff::Timestamp`] (requires `jiff` feature)
47//! - `Duration` ↔ [`jiff::SignedDuration`] (requires `jiff` feature)
48//! - `Any::pack` / `Any::unpack` helpers
49//! - `Value` constructors: [`Value::null`](google::protobuf::Value::null), `From<f64>`, `From<String>`, `From<bool>`, etc.
50//! - Wrapper type `From`/`Into` impls
51//!
52//! # Cargo features
53//!
54//! - **`std`** (default) — standard-library integration (`SystemTime`/`Duration`
55//!   conversions, `std::error::Error`). Without it the crate is `no_std` + `alloc`.
56//! - **`json`** — proto3 canonical JSON serde for the JSON-mappable WKTs
57//!   (`Timestamp`, `Duration`, `Any`, `Struct`/`Value`/`ListValue`, `FieldMask`,
58//!   `Empty`, wrappers). `Api`/`Type`/`Enum`/`SourceContext` and the messages
59//!   they contain have no serde impls; a `json = true` message embedding one
60//!   of them does not compile.
61//! - **`arbitrary`** — `arbitrary::Arbitrary` derives for fuzzing.
62//! - **`chrono`** — `Timestamp` ↔ `chrono::DateTime` and `Duration` ↔
63//!   `chrono::TimeDelta` conversions. `no_std`-compatible (`chrono` is pulled
64//!   with `default-features = false`).
65//! - **`jiff`** — `Timestamp` ↔ `jiff::Timestamp` and `Duration` ↔
66//!   `jiff::SignedDuration` conversions. `no_std`-compatible (`jiff` is pulled
67//!   with `default-features = false` + `alloc`).
68//! - **`reflect`** — runtime reflection: the WKT view types implement
69//!   `buffa_descriptor::reflect::ReflectMessage`, so a message that has a WKT
70//!   field can reflect over it. This pulls a `buffa-descriptor` dependency and
71//!   requires `std` (the embedded descriptor pool uses `std::sync::OnceLock`).
72//!   If you reach for `&view as &dyn ReflectMessage` on a WKT view and the
73//!   compiler says `ReflectMessage` is not implemented, enable this feature.
74
75#![cfg_attr(not(feature = "std"), no_std)]
76#![cfg_attr(docsrs, feature(doc_cfg))]
77#![deny(rustdoc::broken_intra_doc_links)]
78extern crate alloc;
79
80// Extension modules (ergonomic helpers — hand-written, not generated).
81mod any_ext;
82mod duration_ext;
83mod empty_ext;
84mod field_mask_ext;
85mod timestamp_ext;
86mod value_ext;
87#[cfg(feature = "json")]
88mod view_serde_ext;
89mod wrapper_ext;
90
91#[cfg(feature = "chrono")]
92mod duration_chrono;
93#[cfg(feature = "chrono")]
94mod timestamp_chrono;
95
96#[cfg(feature = "jiff")]
97mod duration_jiff;
98#[cfg(feature = "jiff")]
99mod timestamp_jiff;
100
101// Well-known type Rust structs — generated once by `gen_wkt_types`, checked
102// into src/generated/. These protos are Google-owned and frozen; regeneration
103// is only needed when buffa-codegen's output format changes. See the
104// `task gen-wkt-types` target and the `check-generated-code` CI job.
105//
106// The checked-in approach means consumers of buffa-types need only the
107// `buffa` runtime — NOT protoc, NOT buffa-build, NOT buffa-codegen.
108//
109// The allow attributes suppress lints that fire on generated code:
110//   derivable_impls      — enum Default impls are explicit rather than derived
111//   match_single_binding — empty messages generate a single-arm wildcard merge
112#[allow(
113    clippy::derivable_impls,
114    clippy::match_single_binding,
115    non_camel_case_types
116)]
117pub mod google {
118    pub mod protobuf {
119        include!("generated/google.protobuf.mod.rs");
120    }
121}
122
123// Convenience re-exports of the most commonly-used well-known types.
124// Full paths (`google::protobuf::*`) remain available for disambiguation.
125// Wrapper types (Int32Value, etc.) are NOT re-exported to avoid name
126// collisions with similarly-named types in user code.
127pub use google::protobuf::{
128    Any, Duration, Empty, FieldMask, ListValue, NullValue, Struct, Timestamp, Value,
129};
130
131// Re-export error types from extension modules (these are hand-written types
132// in private modules, so re-exporting is the only way to make them accessible).
133pub use duration_ext::DurationError;
134pub use timestamp_ext::TimestampError;
135
136#[cfg(feature = "chrono")]
137#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
138pub use duration_chrono::DurationChronoError;
139
140#[cfg(feature = "jiff")]
141#[cfg_attr(docsrs, doc(cfg(feature = "jiff")))]
142pub use duration_jiff::DurationJiffError;
143
144// Re-export the WKT registry function for `Any` JSON + text support.
145pub use any_ext::register_wkt_types;
146
147#[cfg(test)]
148mod full_name_tests {
149    use super::google::protobuf::*;
150    use buffa::MessageName;
151
152    // Regression test: the WKT FQNs are baked into Any type-URLs, JSON
153    // serialization, and the type registry. Codegen must keep emitting them
154    // verbatim — these strings are observable on the wire.
155    #[test]
156    fn well_known_types_full_names_match_proto() {
157        assert_eq!(Timestamp::FULL_NAME, "google.protobuf.Timestamp");
158        assert_eq!(Duration::FULL_NAME, "google.protobuf.Duration");
159        assert_eq!(Any::FULL_NAME, "google.protobuf.Any");
160        assert_eq!(Empty::FULL_NAME, "google.protobuf.Empty");
161        assert_eq!(FieldMask::FULL_NAME, "google.protobuf.FieldMask");
162        assert_eq!(Struct::FULL_NAME, "google.protobuf.Struct");
163        assert_eq!(Value::FULL_NAME, "google.protobuf.Value");
164        assert_eq!(ListValue::FULL_NAME, "google.protobuf.ListValue");
165        assert_eq!(Api::FULL_NAME, "google.protobuf.Api");
166        assert_eq!(Type::FULL_NAME, "google.protobuf.Type");
167        assert_eq!(Enum::FULL_NAME, "google.protobuf.Enum");
168        assert_eq!(EnumValue::FULL_NAME, "google.protobuf.EnumValue");
169        assert_eq!(Field::FULL_NAME, "google.protobuf.Field");
170        assert_eq!(Method::FULL_NAME, "google.protobuf.Method");
171        assert_eq!(Mixin::FULL_NAME, "google.protobuf.Mixin");
172        assert_eq!(SourceContext::FULL_NAME, "google.protobuf.SourceContext");
173        assert_eq!(Option::FULL_NAME, "google.protobuf.Option");
174    }
175}