Skip to main content

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");
46const ANNOTATIONS_PROTO: &[u8] = include_bytes!("../proto/bones/v1/annotations.proto");
47
48/// Yield `(relative_path, bytes)` pairs for every `bones/v1/*.proto`
49/// file shipped by this crate.
50///
51/// `relative_path` is the protoc-style import path consumers use
52/// (`bones/v1/pagination.proto`, etc.). The order of the iterator is
53/// stable but unspecified — consumers should not rely on it.
54pub fn files() -> impl Iterator<Item = (&'static str, &'static [u8])> {
55    [
56        ("bones/v1/pagination.proto", PAGINATION_PROTO),
57        ("bones/v1/queries.proto", QUERIES_PROTO),
58        ("bones/v1/errors.proto", ERRORS_PROTO),
59        ("bones/v1/ratelimit.proto", RATELIMIT_PROTO),
60        ("bones/v1/annotations.proto", ANNOTATIONS_PROTO),
61    ]
62    .into_iter()
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn ships_five_files() {
71        let v: Vec<_> = files().collect();
72        assert_eq!(v.len(), 5);
73    }
74
75    #[test]
76    fn relative_paths_are_unique() {
77        let mut paths: Vec<_> = files().map(|(p, _)| p).collect();
78        let n = paths.len();
79        paths.sort_unstable();
80        paths.dedup();
81        assert_eq!(paths.len(), n, "duplicate relative paths in files()");
82    }
83
84    #[test]
85    fn every_file_has_non_empty_bytes() {
86        for (path, bytes) in files() {
87            assert!(!bytes.is_empty(), "{path} embeds empty bytes");
88        }
89    }
90
91    #[test]
92    fn pagination_proto_declares_expected_messages() {
93        let body = std::str::from_utf8(PAGINATION_PROTO).expect("utf8");
94        for msg in [
95            "message PageRequest",
96            "message PageResponse",
97            "message OffsetPageRequest",
98            "message OffsetPageResponse",
99        ] {
100            assert!(body.contains(msg), "pagination.proto missing `{msg}`");
101        }
102    }
103
104    #[test]
105    fn annotations_proto_declares_authz_option() {
106        let body = std::str::from_utf8(ANNOTATIONS_PROTO).expect("utf8");
107        assert!(
108            body.contains("AuthzRule authz = 5102348;"),
109            "annotations.proto missing the authz method option"
110        );
111        for value in [
112            "AUTHZ_KIND_UNSPECIFIED",
113            "AUTHZ_KIND_SERVICE",
114            "AUTHZ_KIND_USER",
115            "AUTHZ_KIND_PUBLIC",
116        ] {
117            assert!(body.contains(value), "annotations.proto missing `{value}`");
118        }
119    }
120
121    #[test]
122    fn queries_proto_declares_filter_op_enum() {
123        let body = std::str::from_utf8(QUERIES_PROTO).expect("utf8");
124        assert!(
125            body.contains("enum FilterOp"),
126            "queries.proto missing FilterOp enum"
127        );
128        assert!(
129            body.contains("FILTER_OP_EQ"),
130            "queries.proto missing FILTER_OP_EQ"
131        );
132    }
133}