Skip to main content

prototext_core/
lib.rs

1// SPDX-FileCopyrightText: 2025 - 2026 Frederic Ruget <fred@atlant.is> <fred@s3ns.io> (GitHub: @douzebis)
2// SPDX-FileCopyrightText: 2025 - 2026 Thales Cloud Sécurisé
3//
4// SPDX-License-Identifier: MIT
5
6pub mod decoder;
7pub mod helpers;
8pub mod schema;
9pub mod serialize;
10
11pub use schema::{ParsedSchema, SchemaError};
12
13// ── Public API types ──────────────────────────────────────────────────────────
14
15/// Options controlling how a protobuf binary payload is rendered as text.
16#[non_exhaustive]
17#[derive(Debug, Clone)]
18pub struct RenderOpts {
19    /// When `true`, always treat the input as raw protobuf binary.
20    /// When `false`, auto-detect: if the payload already carries the
21    /// `#@ prototext:` header it is returned unchanged (zero-copy fast path).
22    pub assume_binary: bool,
23    /// Emit inline comments with schema field names and types.
24    pub include_annotations: bool,
25    /// Indentation step in spaces.
26    pub indent: usize,
27}
28
29impl Default for RenderOpts {
30    fn default() -> Self {
31        RenderOpts {
32            assume_binary: false,
33            include_annotations: false,
34            indent: 1,
35        }
36    }
37}
38
39impl RenderOpts {
40    pub fn new(assume_binary: bool, include_annotations: bool, indent: usize) -> Self {
41        RenderOpts {
42            assume_binary,
43            include_annotations,
44            indent,
45        }
46    }
47}
48
49/// Errors that can occur while decoding or encoding a protobuf payload.
50#[non_exhaustive]
51#[derive(Debug)]
52pub enum CodecError {
53    /// The input bytes could not be decoded as a protobuf wire payload.
54    DecodeFailed(String),
55    /// The input bytes could not be decoded as a textual prototext payload.
56    TextDecodeFailed(String),
57}
58
59impl std::fmt::Display for CodecError {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        match self {
62            CodecError::DecodeFailed(msg) => write!(f, "decode failed: {msg}"),
63            CodecError::TextDecodeFailed(msg) => write!(f, "text decode failed: {msg}"),
64        }
65    }
66}
67
68impl std::error::Error for CodecError {}
69
70// ── Public API functions ──────────────────────────────────────────────────────
71
72/// Decode a raw protobuf binary payload and render it as protoc-style text.
73///
74/// When `opts.assume_binary` is `false` and the data already carries the
75/// `#@ prototext:` header, the bytes are returned unchanged (zero-copy fast
76/// path).
77pub fn render_as_text(
78    data: &[u8],
79    schema: Option<&ParsedSchema>,
80    opts: RenderOpts,
81) -> Result<Vec<u8>, CodecError> {
82    if !opts.assume_binary && serialize::render_text::is_prototext_text(data) {
83        return Ok(data.to_vec());
84    }
85    Ok(serialize::render_text::decode_and_render(
86        data,
87        schema,
88        opts.include_annotations,
89        opts.indent,
90    ))
91}
92
93/// Encode a textual prototext payload back to raw protobuf binary wire bytes.
94///
95/// The input must carry the `#@ prototext:` header (i.e. be produced by
96/// `render_as_text`).  Raw binary input is passed through unchanged.
97pub fn render_as_bytes(data: &[u8], _opts: RenderOpts) -> Result<Vec<u8>, CodecError> {
98    if serialize::render_text::is_prototext_text(data) {
99        Ok(serialize::encode_text::encode_text_to_binary(data))
100    } else {
101        Ok(data.to_vec())
102    }
103}
104
105/// Parse a compiled `.pb` descriptor into a `ParsedSchema`.
106///
107/// Re-exported from `schema` for convenience so callers only need to import
108/// from the crate root.
109pub fn parse_schema(schema_bytes: &[u8], root_message: &str) -> Result<ParsedSchema, SchemaError> {
110    schema::parse_schema(schema_bytes, root_message)
111}