Skip to main content

switchback_traits/
options.rs

1//! Shared renderer and parser option shapes (data only; no token parsing).
2
3use std::path::PathBuf;
4
5/// Page layout for generated markdown.
6///
7/// Controls how entity pages are grouped in the output book tree. In-memory only;
8/// not serialized in the switchback wire format.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
10pub enum Layout {
11    /// One page per package/group with nested entity links.
12    #[default]
13    Package,
14    /// One page per entity.
15    Entity,
16    /// Split large groups across multiple pages.
17    Split,
18}
19
20/// How to rewrite HTML-like tags in leading-comment prose.
21///
22/// Renderer-local option; not serialized on the wire.
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24pub enum EscapeTags {
25    /// Leave tags unchanged.
26    #[default]
27    Off,
28    /// Wrap tags in Markdown backticks.
29    Backticks,
30    /// Escape tags as HTML entities.
31    Entities,
32}
33
34/// How to label OpenAPI operations in SUMMARY and package index links.
35///
36/// Renderer-local option; not serialized on the wire.
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
38pub enum OpenApiSummaryLabel {
39    /// HTTP path only, without method (for example `/products`).
40    #[default]
41    Endpoint,
42    /// OpenAPI `summary` / `operationId` title from populate.
43    Summary,
44    /// Legacy `Operation /path` prefix plus path (no method).
45    Prefixed,
46}
47
48/// How to render the raw OpenAPI operation YAML/JSON on operation pages.
49///
50/// Renderer-local option; not serialized on the wire.
51#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
52pub enum OpenApiOperationSource {
53    /// Wrap the full operation definition in a collapsed HTML `<details>` block.
54    #[default]
55    Collapsed,
56    /// Omit fields already rendered in structured sections (parameters, responses, etc.).
57    Trimmed,
58    /// Omit the operation definition fence entirely.
59    Hidden,
60}
61
62/// Spine options shared across renderers (subset ported from protobuf-mdbook).
63///
64/// Passed to [`Contract::link_context`](crate::traits::Contract::link_context) and
65/// renderers at output time. In-memory configuration only.
66#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct Options {
68    /// When true, emit initial book scaffolding (SUMMARY, directories).
69    pub init: bool,
70    /// When true, regenerate the mdBook `SUMMARY.md`.
71    pub summary: bool,
72    /// Root directory of the mdBook project.
73    pub book_root: String,
74    /// Relative path from `book_root` to generated markdown pages.
75    pub markdown_root: String,
76    /// Relative path from `book_root` to `SUMMARY.md`.
77    pub summary_path: String,
78    /// Optional mdBook book title override.
79    pub book: Option<String>,
80    /// Optional output directory for the built book.
81    pub mdbook_out: Option<String>,
82    /// Optional title override for the reference manual.
83    pub title: Option<String>,
84    /// When true, skip git-based provenance checks.
85    pub ignore_git: bool,
86    /// Page layout strategy for generated markdown.
87    pub layout: Layout,
88    /// Extra filesystem paths searched when resolving links or companions.
89    pub search_paths: Vec<PathBuf>,
90    /// How to escape HTML-like tags in prose comments.
91    pub escape_tags: EscapeTags,
92    /// Selected link formatter name (default `mdbook-relative`).
93    pub link_format: Option<String>,
94    /// When true, skip copying companion proto markdown files.
95    pub no_proto_markdown: bool,
96    /// When true, skip protobuf syntax highlighting in init scaffold.
97    pub no_proto_highlight: bool,
98    /// When true, skip CEL syntax highlighting in init scaffold.
99    pub no_cel_highlight: bool,
100    /// Set when `markdown_root=` appears in plugin options (preserved under `book=`).
101    pub explicit_markdown_root: bool,
102    /// Set when `summary_path=` appears in plugin options (preserved under `book=`).
103    pub explicit_summary_path: bool,
104    /// Set when `book_root=` appears in plugin options (preserved under `book=`).
105    pub explicit_book_root: bool,
106    /// When true, sort package-layout `Services` headings by entity title (mdBook).
107    pub alphabetize_services: bool,
108    /// When true, sort package-layout `Messages and enums` headings by entity title
109    /// (mdBook).
110    pub alphabetize_messages: bool,
111    /// How to render raw OpenAPI operation source on operation pages.
112    pub openapi_operation_source: OpenApiOperationSource,
113    /// How to label OpenAPI operations in SUMMARY and index navigation.
114    pub openapi_summary_label: OpenApiSummaryLabel,
115}
116
117impl Default for Options {
118    fn default() -> Self {
119        Self {
120            init: false,
121            summary: false,
122            book_root: ".".into(),
123            markdown_root: "src/packages".into(),
124            summary_path: "src/SUMMARY.md".into(),
125            book: None,
126            mdbook_out: None,
127            title: None,
128            ignore_git: true,
129            layout: Layout::default(),
130            search_paths: Vec::new(),
131            escape_tags: EscapeTags::default(),
132            link_format: None,
133            no_proto_markdown: false,
134            no_proto_highlight: false,
135            no_cel_highlight: false,
136            explicit_markdown_root: false,
137            explicit_summary_path: false,
138            explicit_book_root: false,
139            alphabetize_services: false,
140            alphabetize_messages: false,
141            openapi_operation_source: OpenApiOperationSource::default(),
142            openapi_summary_label: OpenApiSummaryLabel::default(),
143        }
144    }
145}
146
147impl Options {
148    /// Default link formatter name.
149    pub fn link_format_name(&self) -> &str {
150        self.link_format.as_deref().unwrap_or("mdbook-relative")
151    }
152
153    /// Join `book_root` with a relative output path.
154    pub fn output_path(&self, rel: &str) -> String {
155        let rel = rel.trim_start_matches('/');
156        if self.book_root == "." || self.book_root.is_empty() {
157            rel.to_string()
158        } else {
159            format!("{}/{rel}", self.book_root.trim_end_matches('/'))
160        }
161    }
162
163    /// Returns true when protobuf highlight preprocessor should be configured.
164    pub fn proto_highlight(&self) -> bool {
165        self.init && !self.no_proto_highlight
166    }
167
168    /// Returns true when CEL highlight preprocessor should be configured.
169    pub fn cel_highlight(&self) -> bool {
170        self.init && !self.no_cel_highlight
171    }
172    /// Returns true when a SUMMARY file should be rendered (`summary` or `init`).
173    pub fn render_summary(&self) -> bool {
174        self.summary || self.init
175    }
176
177    /// Returns true when only package-level SUMMARY entries are needed (`init` mode).
178    pub fn package_only_summary(&self) -> bool {
179        self.init && matches!(self.layout, Layout::Package)
180    }
181}