oxiproto_codegen/lib.rs
1#![forbid(unsafe_code)]
2
3//! `oxiproto-codegen` — Generate plain Rust source code from a `FileDescriptorSet`.
4//!
5//! This crate walks a `prost_types::FileDescriptorSet` and emits plain Rust
6//! structs and enums — no prost derive, no gRPC stubs, no validators.
7//!
8//! ## Quick start
9//!
10//! ```no_run
11//! use prost_types::FileDescriptorSet;
12//!
13//! let fds: FileDescriptorSet = /* parse your .proto */ Default::default();
14//! let rust_src = oxiproto_codegen::generate(&fds).expect("codegen failed");
15//! println!("{rust_src}");
16//! ```
17
18pub(crate) mod builder_impl;
19mod emit;
20mod format;
21mod json_impl;
22mod message_impl;
23mod options;
24pub(crate) mod text_impl;
25pub(crate) mod type_registry;
26pub mod wkt_map;
27
28pub use options::{CodegenError, CodegenOptions};
29
30pub use emit::{emit_file_descriptor_set, emit_file_descriptor_set_with_options, ModuleTree};
31
32/// Generate Rust source code from a `FileDescriptorSet`.
33///
34/// Returns a `String` of Rust source containing `struct` and `enum` definitions
35/// for every message and enum found in the descriptor set. Fields are mapped
36/// to plain Rust types (no `prost` derive macros).
37pub fn generate(fds: &prost_types::FileDescriptorSet) -> Result<String, CodegenError> {
38 emit::emit_file_descriptor_set(fds)
39}
40
41/// Generate Rust source code with custom codegen options.
42///
43/// This allows enabling additional features like doc comment generation,
44/// `Default` impls, deprecation attributes, and BTreeMap for map fields.
45pub fn generate_with_options(
46 fds: &prost_types::FileDescriptorSet,
47 options: &CodegenOptions,
48) -> Result<String, CodegenError> {
49 let code = emit::emit_file_descriptor_set_with_options(fds, options)?;
50 #[cfg(feature = "format")]
51 let code = if options.format_output {
52 crate::format::format_source(&code)?
53 } else {
54 code
55 };
56 Ok(code)
57}
58
59/// Generate a structured module tree from a `FileDescriptorSet`.
60///
61/// Unlike [`generate_with_options`] which returns a flat `String`,
62/// this returns a [`ModuleTree`] that preserves the package hierarchy,
63/// enabling per-package or per-file output.
64///
65/// # Errors
66///
67/// Returns a [`CodegenError`] if the descriptor set is invalid.
68pub fn generate_module(
69 fds: &prost_types::FileDescriptorSet,
70 options: &CodegenOptions,
71) -> Result<ModuleTree, CodegenError> {
72 emit::generate_module_tree(fds, options)
73}
74
75/// Write generated code to a file path.
76///
77/// Equivalent to calling [`generate`] and then writing the resulting string to
78/// `path`.
79pub fn generate_to_file(
80 fds: &prost_types::FileDescriptorSet,
81 path: &std::path::Path,
82) -> Result<(), CodegenError> {
83 let code = generate(fds)?;
84 std::fs::write(path, code).map_err(CodegenError::Io)
85}
86
87/// Write generated code to a file path with custom options.
88pub fn generate_to_file_with_options(
89 fds: &prost_types::FileDescriptorSet,
90 path: &std::path::Path,
91 options: &CodegenOptions,
92) -> Result<(), CodegenError> {
93 let code = generate_with_options(fds, options)?;
94 std::fs::write(path, code).map_err(CodegenError::Io)
95}
96
97/// Stream generated code into any [`std::io::Write`] sink.
98///
99/// This is the lowest-allocation path: it generates the Rust source and
100/// writes it directly to `writer` without building an intermediate `String`
101/// copy beyond the one produced by `generate_with_options`.
102///
103/// # Note on streaming
104///
105/// Internally the code generator builds a `String` via string concatenation.
106/// This function writes that single buffer to `writer` without an extra copy,
107/// making it preferable to `generate_with_options` when the caller is writing
108/// to a file, socket, or other I/O sink.
109///
110/// For an in-memory buffer prefer [`generate_with_options`] directly.
111///
112/// # Errors
113///
114/// Returns [`CodegenError`] on descriptor errors or on I/O failure.
115pub fn generate_to_writer<W: std::io::Write>(
116 fds: &prost_types::FileDescriptorSet,
117 options: &CodegenOptions,
118 writer: &mut W,
119) -> Result<(), CodegenError> {
120 let code = generate_with_options(fds, options)?;
121 writer.write_all(code.as_bytes()).map_err(CodegenError::Io)
122}
123
124/// Stream the default generated output into any [`std::io::Write`] sink.
125///
126/// Convenience wrapper around [`generate_to_writer`] using default
127/// [`CodegenOptions`].
128pub fn generate_to_writer_default<W: std::io::Write>(
129 fds: &prost_types::FileDescriptorSet,
130 writer: &mut W,
131) -> Result<(), CodegenError> {
132 generate_to_writer(fds, &CodegenOptions::default(), writer)
133}