api_bones_protos/lib.rs
1// SPDX-License-Identifier: MIT
2//! Embedded bytes of the api-bones canonical proto shapes.
3//!
4//! This crate ships the `bones/v1/*.proto` files via `include_bytes!`
5//! and exposes a single accessor — [`files`] — that yields
6//! `(relative_path, bytes)` pairs ready for staging onto a protoc
7//! include path at consumer build time.
8//!
9//! **Zero runtime dependencies.** This crate exists purely as a
10//! distribution mechanism for the bytes. Pair it with any staging
11//! library (e.g. [`proto-build-kit`](https://crates.io/crates/proto-build-kit))
12//! to assemble a tempdir for codegen tools (connectrpc-build,
13//! tonic-prost-build, ...).
14//!
15//! # Example
16//!
17//! ```no_run
18//! # // We can't actually run this in a doctest because it depends on
19//! # // proto-build-kit, but it shows the shape.
20//! # mod proto_build_kit {
21//! # pub struct Stager;
22//! # impl Stager {
23//! # pub fn new() -> Self { Self }
24//! # pub fn with<I>(self, _: I) -> Self { self }
25//! # pub fn stage(self) -> Result<tempfile::TempDir, std::io::Error> {
26//! # tempfile::tempdir()
27//! # }
28//! # }
29//! # }
30//! let staged = proto_build_kit::Stager::new()
31//! .with(api_bones_protos::files())
32//! .stage()
33//! .expect("stage");
34//! // Add staged.path() to your protoc include path.
35//! ```
36//!
37//! # What's included
38//!
39//! The shapes match the proto/bones/v1/ directory in the api-bones
40//! repo. See the README for the canonical schema inventory.
41
42const PAGINATION_PROTO: &[u8] = include_bytes!("../proto/bones/v1/pagination.proto");
43const QUERIES_PROTO: &[u8] = include_bytes!("../proto/bones/v1/queries.proto");
44const ERRORS_PROTO: &[u8] = include_bytes!("../proto/bones/v1/errors.proto");
45const RATELIMIT_PROTO: &[u8] = include_bytes!("../proto/bones/v1/ratelimit.proto");
46
47/// Yield `(relative_path, bytes)` pairs for every `bones/v1/*.proto`
48/// file shipped by this crate.
49///
50/// `relative_path` is the protoc-style import path consumers use
51/// (`bones/v1/pagination.proto`, etc.). The order of the iterator is
52/// stable but unspecified — consumers should not rely on it.
53pub fn files() -> impl Iterator<Item = (&'static str, &'static [u8])> {
54 [
55 ("bones/v1/pagination.proto", PAGINATION_PROTO),
56 ("bones/v1/queries.proto", QUERIES_PROTO),
57 ("bones/v1/errors.proto", ERRORS_PROTO),
58 ("bones/v1/ratelimit.proto", RATELIMIT_PROTO),
59 ]
60 .into_iter()
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn ships_four_files() {
69 let v: Vec<_> = files().collect();
70 assert_eq!(v.len(), 4);
71 }
72
73 #[test]
74 fn relative_paths_are_unique() {
75 let mut paths: Vec<_> = files().map(|(p, _)| p).collect();
76 let n = paths.len();
77 paths.sort_unstable();
78 paths.dedup();
79 assert_eq!(paths.len(), n, "duplicate relative paths in files()");
80 }
81
82 #[test]
83 fn every_file_has_non_empty_bytes() {
84 for (path, bytes) in files() {
85 assert!(!bytes.is_empty(), "{path} embeds empty bytes");
86 }
87 }
88
89 #[test]
90 fn pagination_proto_declares_expected_messages() {
91 let body = std::str::from_utf8(PAGINATION_PROTO).expect("utf8");
92 for msg in [
93 "message PageRequest",
94 "message PageResponse",
95 "message OffsetPageRequest",
96 "message OffsetPageResponse",
97 ] {
98 assert!(body.contains(msg), "pagination.proto missing `{msg}`");
99 }
100 }
101
102 #[test]
103 fn queries_proto_declares_filter_op_enum() {
104 let body = std::str::from_utf8(QUERIES_PROTO).expect("utf8");
105 assert!(
106 body.contains("enum FilterOp"),
107 "queries.proto missing FilterOp enum"
108 );
109 assert!(
110 body.contains("FILTER_OP_EQ"),
111 "queries.proto missing FILTER_OP_EQ"
112 );
113 }
114}