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}