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