Skip to main content

pharos_runtime_docs/
docs.rs

1use std::fs;
2use std::path::{Path, PathBuf};
3
4use saphyr::LoadableYamlNode;
5use serde_json::Value;
6
7/// Performs dbuf put tag open.
8///
9/// # Parameters
10///
11/// - `tag_name`: Input consumed by [`crate::docs::dbuf_put_tag_open`].
12///
13/// # Returns
14///
15/// Returns [`Some`] when [`crate::docs::dbuf_put_tag_open`] can produce a value and [`None`] otherwise.
16///
17/// # Examples
18///
19/// ```rust,ignore
20/// let _result = pharos_runtime_docs::docs::dbuf_put_tag_open(todo!());
21/// ```
22///
23/// # Related
24///
25/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
26pub fn dbuf_put_tag_open(tag_name: &str) -> Option<String> {
27    (!tag_name.is_empty()).then(|| format!("<{tag_name}>"))
28}
29
30/// Performs dbuf put anchor open.
31///
32/// # Parameters
33///
34/// - `href`: Input consumed by [`crate::docs::dbuf_put_anchor_open`].
35///
36/// # Returns
37///
38/// Returns [`Some`] when [`crate::docs::dbuf_put_anchor_open`] can produce a value and [`None`] otherwise.
39///
40/// # Examples
41///
42/// ```rust,ignore
43/// let _result = pharos_runtime_docs::docs::dbuf_put_anchor_open(todo!());
44/// ```
45///
46/// # Related
47///
48/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
49pub fn dbuf_put_anchor_open(href: &str) -> Option<String> {
50    Some(format!("<a href=\"{}\">", html_escape(href)))
51}
52
53/// Performs line len.
54///
55/// # Parameters
56///
57/// - `line`: Input consumed by [`crate::docs::line_len`].
58///
59/// # Returns
60///
61/// Returns the computed value produced by [`crate::docs::line_len`].
62///
63/// # Examples
64///
65/// ```rust,ignore
66/// let _result = pharos_runtime_docs::docs::line_len(todo!());
67/// ```
68///
69/// # Related
70///
71/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
72pub fn line_len(line: &str) -> usize {
73    line.len()
74}
75
76/// Performs line starts with.
77///
78/// # Parameters
79///
80/// - `line`: Input consumed by [`crate::docs::line_starts_with`].
81/// - `prefix`: Input consumed by [`crate::docs::line_starts_with`].
82///
83/// # Returns
84///
85/// Returns `true` when the condition checked by [`crate::docs::line_starts_with`] holds and `false` otherwise.
86///
87/// # Examples
88///
89/// ```rust,ignore
90/// let _result = pharos_runtime_docs::docs::line_starts_with(todo!(), todo!());
91/// ```
92///
93/// # Related
94///
95/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
96pub fn line_starts_with(line: &str, prefix: &str) -> bool {
97    line.starts_with(prefix)
98}
99
100/// Performs skip spaces.
101///
102/// # Parameters
103///
104/// - `s`: Input consumed by [`crate::docs::skip_spaces`].
105///
106/// # Returns
107///
108/// Returns the computed value produced by [`crate::docs::skip_spaces`].
109///
110/// # Examples
111///
112/// ```rust,ignore
113/// let _result = pharos_runtime_docs::docs::skip_spaces(todo!());
114/// ```
115///
116/// # Related
117///
118/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
119pub fn skip_spaces(s: &str) -> &str {
120    s.trim_start_matches([' ', '\t'])
121}
122
123/// Performs HTML escape.
124fn html_escape(input: &str) -> String {
125    input
126        .replace('&', "&amp;")
127        .replace('<', "&lt;")
128        .replace('>', "&gt;")
129        .replace('"', "&quot;")
130}
131
132/// Resets a documentation buffer and releases its previous allocation.
133///
134/// # Parameters
135///
136/// - `output`: Input consumed by [`crate::docs::dbuf_init`].
137///
138/// # Returns
139///
140/// Returns after [`crate::docs::dbuf_init`] applies its in-place effect.
141///
142/// # Examples
143///
144/// ```rust,ignore
145/// let _result = pharos_runtime_docs::docs::dbuf_init(todo!());
146/// ```
147///
148/// # Related
149///
150/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
151pub fn dbuf_init(output: &mut String) {
152    *output = String::new();
153}
154
155/// Ensures bounded capacity for one raw buffer append.
156///
157/// # Parameters
158///
159/// - `output`: Input consumed by [`crate::docs::dbuf_ensure`].
160/// - `additional`: Input consumed by [`crate::docs::dbuf_ensure`].
161///
162/// # Returns
163///
164/// Returns `true` when the condition checked by [`crate::docs::dbuf_ensure`] holds and `false` otherwise.
165///
166/// # Examples
167///
168/// ```rust,ignore
169/// let _result = pharos_runtime_docs::docs::dbuf_ensure(todo!(), todo!());
170/// ```
171///
172/// # Related
173///
174/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
175pub fn dbuf_ensure(output: &mut String, additional: usize) -> bool {
176    let Some(required_len) = output.len().checked_add(additional) else {
177        return false;
178    };
179    if required_len > MAX_DOC_TEXT_BYTES {
180        return false;
181    }
182    output.try_reserve(additional.saturating_add(1)).is_ok()
183}
184
185/// Appends one raw buffer fragment within the documentation size limit.
186///
187/// # Parameters
188///
189/// - `output`: Input consumed by [`crate::docs::dbuf_append`].
190/// - `value`: Input consumed by [`crate::docs::dbuf_append`].
191///
192/// # Returns
193///
194/// Returns `true` when the condition checked by [`crate::docs::dbuf_append`] holds and `false` otherwise.
195///
196/// # Examples
197///
198/// ```rust,ignore
199/// let _result = pharos_runtime_docs::docs::dbuf_append(todo!(), "value");
200/// ```
201///
202/// # Related
203///
204/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
205pub fn dbuf_append(output: &mut String, value: &str) -> bool {
206    if !dbuf_ensure(output, value.len()) {
207        return false;
208    }
209    output.push_str(value);
210    true
211}
212
213/// Appends one NUL-terminated C-string equivalent to the buffer.
214///
215/// # Parameters
216///
217/// - `output`: Input consumed by [`crate::docs::dbuf_puts`].
218/// - `value`: Input consumed by [`crate::docs::dbuf_puts`].
219///
220/// # Returns
221///
222/// Returns `true` when the condition checked by [`crate::docs::dbuf_puts`] holds and `false` otherwise.
223///
224/// # Examples
225///
226/// ```rust,ignore
227/// let _result = pharos_runtime_docs::docs::dbuf_puts(todo!(), "value");
228/// ```
229///
230/// # Related
231///
232/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
233pub fn dbuf_puts(output: &mut String, value: &str) -> bool {
234    dbuf_append(output, value)
235}
236
237/// Transfers the buffer contents, leaving an unallocated empty buffer.
238///
239/// # Parameters
240///
241/// - `output`: Input consumed by [`crate::docs::dbuf_detach`].
242///
243/// # Returns
244///
245/// Returns [`Some`] when [`crate::docs::dbuf_detach`] can produce a value and [`None`] otherwise.
246///
247/// # Examples
248///
249/// ```rust,ignore
250/// let _result = pharos_runtime_docs::docs::dbuf_detach(todo!());
251/// ```
252///
253/// # Related
254///
255/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
256pub fn dbuf_detach(output: &mut String) -> Option<String> {
257    (output.capacity() > 0).then(|| std::mem::take(output))
258}
259
260/// Performs replace placeholder.
261///
262/// # Parameters
263///
264/// - `out`: Input consumed by [`crate::docs::replace_placeholder`].
265/// - `template_text`: Input consumed by [`crate::docs::replace_placeholder`].
266/// - `placeholder`: Input consumed by [`crate::docs::replace_placeholder`].
267/// - `value`: Input consumed by [`crate::docs::replace_placeholder`].
268/// - `is_html`: Input consumed by [`crate::docs::replace_placeholder`].
269///
270/// # Returns
271///
272/// Returns [`Some`] when [`crate::docs::replace_placeholder`] can produce a value and [`None`] otherwise.
273///
274/// # Performance
275///
276/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::replace_placeholder`].
277///
278/// # Examples
279///
280/// ```rust,ignore
281/// let _result = pharos_runtime_docs::docs::replace_placeholder(todo!(), todo!(), todo!(), "value", todo!());
282/// ```
283///
284/// # Related
285///
286/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
287pub fn replace_placeholder<'a>(
288    out: &mut String,
289    template_text: &'a str,
290    placeholder: &str,
291    value: &str,
292    is_html: bool,
293) -> Option<&'a str> {
294    let mut cursor = 0usize;
295    while let Some(relative_open) = template_text[cursor..].find('{') {
296        let open = cursor + relative_open;
297        let key_start = open + 1;
298        let relative_close = template_text[key_start..].find('}')?;
299        let close = key_start + relative_close;
300        let mut effective_key = &template_text[key_start..close];
301        let mut this_is_html = is_html;
302        if let Some(stripped) = effective_key.strip_prefix("html:") {
303            effective_key = stripped;
304            this_is_html = true;
305        }
306        if effective_key == placeholder {
307            out.push_str(&template_text[..open]);
308            if this_is_html {
309                out.push_str(value);
310            } else {
311                out.push_str(&html_escape(value));
312            }
313            return Some(&template_text[close + 1..]);
314        }
315        cursor = close + 1;
316    }
317    None
318}
319
320/// Converts a bounded YAML scalar value into its canonical text representation.
321///
322/// # Parameters
323///
324/// - `value`: Input consumed by [`crate::docs::docs_yaml_value`].
325///
326/// # Returns
327///
328/// Returns [`Some`] when [`crate::docs::docs_yaml_value`] can produce a value and [`None`] otherwise.
329///
330/// # Examples
331///
332/// ```rust,ignore
333/// let _result = pharos_runtime_docs::docs::docs_yaml_value("value");
334/// ```
335///
336/// # Related
337///
338/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
339pub fn docs_yaml_value(value: &str) -> Option<String> {
340    let trimmed = value.trim();
341    if trimmed.is_empty() || trimmed.len() > 64 * 1024 {
342        return None;
343    }
344    Some(trimmed.trim_matches(['\"', '\'']).to_owned())
345}
346
347/// Completes one output remap item only when both sides are present.
348///
349/// # Parameters
350///
351/// - `source`: Input consumed by [`crate::docs::docs_output_remap_finish_item`].
352/// - `output`: Input consumed by [`crate::docs::docs_output_remap_finish_item`].
353/// - `) -> Option<(String`: Input consumed by [`crate::docs::docs_output_remap_finish_item`].
354/// - `String`: Input consumed by [`crate::docs::docs_output_remap_finish_item`].
355///
356/// # Returns
357///
358/// Returns [`Some`] when [`crate::docs::docs_output_remap_finish_item`] can produce a value and [`None`] otherwise.
359///
360/// # Examples
361///
362/// ```rust,ignore
363/// let _result = pharos_runtime_docs::docs::docs_output_remap_finish_item(todo!(), todo!(), todo!(), todo!());
364/// ```
365///
366/// # Related
367///
368/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
369pub fn docs_output_remap_finish_item(
370    source: Option<&str>,
371    output: Option<&str>,
372) -> Option<(String, String)> {
373    let source = source?.trim();
374    let output = output?;
375    (!source.is_empty()).then(|| (source.to_owned(), output.to_owned()))
376}
377
378/// Appends a bounded inline-markup fragment with safe link and code handling.
379///
380/// # Parameters
381///
382/// - `output`: Input consumed by [`crate::docs::append_inline_markup`].
383/// - `text`: Input consumed by [`crate::docs::append_inline_markup`].
384///
385/// # Returns
386///
387/// Returns `true` when the condition checked by [`crate::docs::append_inline_markup`] holds and `false` otherwise.
388///
389/// # Performance
390///
391/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::append_inline_markup`].
392///
393/// # Examples
394///
395/// ```rust,ignore
396/// let _result = pharos_runtime_docs::docs::append_inline_markup(todo!(), "{}");
397/// ```
398///
399/// # Related
400///
401/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
402pub fn append_inline_markup(output: &mut String, text: &str) -> bool {
403    if text.len() > 256 * 1024 || output.len().saturating_add(text.len()) > 2 * 1024 * 1024 {
404        return false;
405    }
406    let mut cursor = 0usize;
407    while cursor < text.len() {
408        let remainder = &text[cursor..];
409        if let Some(end) = remainder.find('`') {
410            output.push_str(&html_escape(&remainder[..end]));
411            let after_open = &remainder[end + 1..];
412            let Some(close) = after_open.find('`') else {
413                output.push('`');
414                output.push_str(&html_escape(after_open));
415                return true;
416            };
417            output.push_str("<code>");
418            output.push_str(&html_escape(&after_open[..close]));
419            output.push_str("</code>");
420            cursor += end + close + 2;
421        } else {
422            output.push_str(&html_escape(remainder));
423            break;
424        }
425    }
426    true
427}
428
429#[allow(non_snake_case)]
430/// Flushes one markdown paragraph into the output buffer.
431///
432/// # Parameters
433///
434/// - `output`: Input consumed by [`crate::docs::FLUSH_PARA`].
435/// - `paragraph`: Input consumed by [`crate::docs::FLUSH_PARA`].
436///
437/// # Returns
438///
439/// Returns `true` when the condition checked by [`crate::docs::FLUSH_PARA`] holds and `false` otherwise.
440///
441/// # Examples
442///
443/// ```rust,ignore
444/// let _result = pharos_runtime_docs::docs::FLUSH_PARA(todo!(), todo!());
445/// ```
446///
447/// # Related
448///
449/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
450pub fn FLUSH_PARA(output: &mut String, paragraph: &str) -> bool {
451    if paragraph.trim().is_empty() {
452        return true;
453    }
454    if !append_inline_markup(output, paragraph.trim()) {
455        return false;
456    }
457    output.push_str("\n\n");
458    true
459}
460
461/// Closes a validated markup tag.
462///
463/// # Parameters
464///
465/// - `tag_name`: Input consumed by [`crate::docs::dbuf_put_tag_close`].
466///
467/// # Returns
468///
469/// Returns [`Some`] when [`crate::docs::dbuf_put_tag_close`] can produce a value and [`None`] otherwise.
470///
471/// # Performance
472///
473/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::dbuf_put_tag_close`].
474///
475/// # Examples
476///
477/// ```rust,ignore
478/// let _result = pharos_runtime_docs::docs::dbuf_put_tag_close(todo!());
479/// ```
480///
481/// # Related
482///
483/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
484pub fn dbuf_put_tag_close(tag_name: &str) -> Option<String> {
485    (!tag_name.is_empty() && tag_name.bytes().all(|byte| byte.is_ascii_alphanumeric()))
486        .then(|| format!("</{tag_name}>"))
487}
488
489/// Rewrites one documentation link target while rejecting unsafe schemes.
490///
491/// # Parameters
492///
493/// - `target`: Input consumed by [`crate::docs::rewrite_link_target`].
494///
495/// # Returns
496///
497/// Returns [`Some`] when [`crate::docs::rewrite_link_target`] can produce a value and [`None`] otherwise.
498///
499/// # Performance
500///
501/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::rewrite_link_target`].
502///
503/// # Examples
504///
505/// ```rust,ignore
506/// let _result = pharos_runtime_docs::docs::rewrite_link_target("/");
507/// ```
508///
509/// # Related
510///
511/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
512pub fn rewrite_link_target(target: &str) -> Option<String> {
513    if target.is_empty() || target.chars().any(char::is_control) {
514        return None;
515    }
516    if target.starts_with("javascript:") || target.starts_with("data:") {
517        return None;
518    }
519    Some(target.to_owned())
520}
521
522/// Appends one escaped character to a documentation buffer.
523///
524/// # Parameters
525///
526/// - `output`: Input consumed by [`crate::docs::dbuf_putc`].
527/// - `character`: Input consumed by [`crate::docs::dbuf_putc`].
528///
529/// # Returns
530///
531/// Returns `true` when the condition checked by [`crate::docs::dbuf_putc`] holds and `false` otherwise.
532///
533/// # Examples
534///
535/// ```rust,ignore
536/// let _result = pharos_runtime_docs::docs::dbuf_putc(todo!(), todo!());
537/// ```
538///
539/// # Related
540///
541/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
542pub fn dbuf_putc(output: &mut String, character: char) -> bool {
543    if character.is_control() && character != '\n' {
544        return false;
545    }
546    let mut encoded = [0u8; 4];
547    dbuf_append(output, character.encode_utf8(&mut encoded))
548}
549
550/// Appends escaped HTML text to a documentation buffer.
551///
552/// # Parameters
553///
554/// - `output`: Input consumed by [`crate::docs::dbuf_append_html_escaped`].
555/// - `value`: Input consumed by [`crate::docs::dbuf_append_html_escaped`].
556///
557/// # Returns
558///
559/// Returns `true` when the condition checked by [`crate::docs::dbuf_append_html_escaped`] holds and `false` otherwise.
560///
561/// # Examples
562///
563/// ```rust,ignore
564/// let _result = pharos_runtime_docs::docs::dbuf_append_html_escaped(todo!(), "value");
565/// ```
566///
567/// # Related
568///
569/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
570pub fn dbuf_append_html_escaped(output: &mut String, value: &str) -> bool {
571    let escaped = html_escape(value);
572    dbuf_append(output, &escaped)
573}
574
575const MAX_DOC_FILES: usize = 4096;
576const MAX_DOC_TEXT_BYTES: usize = 2 * 1024 * 1024;
577
578#[derive(Debug, Clone, Default, Eq, PartialEq)]
579/// Public struct `DocsOutputRemap` used by this module.
580///
581/// # Fields
582///
583/// - `entries`: Stored field on `DocsOutputRemap`.
584///
585/// # Examples
586///
587/// ```rust,ignore
588/// let _ = std::mem::size_of::<pharos_runtime_docs::docs::DocsOutputRemap>();
589/// ```
590///
591/// # Related
592///
593/// - [`crate::docs`] for constructors, helpers, and neighboring runtime types.
594pub struct DocsOutputRemap {
595    pub entries: Vec<(String, String)>,
596}
597
598#[derive(Debug, Clone, Eq, PartialEq)]
599/// Public struct `DocsNavEntry` used by this module.
600///
601/// # Fields
602///
603/// - `path`: Stored field on `DocsNavEntry`.
604/// - `label`: Stored field on `DocsNavEntry`.
605/// - `depth`: Stored field on `DocsNavEntry`.
606///
607/// # Examples
608///
609/// ```rust,ignore
610/// let _ = std::mem::size_of::<pharos_runtime_docs::docs::DocsNavEntry>();
611/// ```
612///
613/// # Related
614///
615/// - [`crate::docs`] for constructors, helpers, and neighboring runtime types.
616pub struct DocsNavEntry {
617    pub path: String,
618    pub label: String,
619    pub depth: usize,
620}
621
622struct DocsNavNode {
623    path: String,
624    label: String,
625    depth: usize,
626    children: Vec<DocsNavNode>,
627}
628
629/// Performs dbuf free.
630///
631/// # Parameters
632///
633/// - `output`: Input consumed by [`crate::docs::dbuf_free`].
634///
635/// # Returns
636///
637/// Returns after [`crate::docs::dbuf_free`] applies its in-place effect.
638///
639/// # Examples
640///
641/// ```rust,ignore
642/// let _result = pharos_runtime_docs::docs::dbuf_free(todo!());
643/// ```
644///
645/// # Related
646///
647/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
648pub fn dbuf_free(output: &mut String) {
649    dbuf_init(output);
650}
651
652/// Performs docs output remap clear.
653///
654/// # Parameters
655///
656/// - `remap`: Input consumed by [`crate::docs::docs_output_remap_clear`].
657///
658/// # Returns
659///
660/// Returns after [`crate::docs::docs_output_remap_clear`] applies its in-place effect.
661///
662/// # Examples
663///
664/// ```rust,ignore
665/// let _result = pharos_runtime_docs::docs::docs_output_remap_clear(todo!());
666/// ```
667///
668/// # Related
669///
670/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
671pub fn docs_output_remap_clear(remap: &mut DocsOutputRemap) {
672    remap.entries.clear();
673}
674
675/// Performs docs output remap append.
676///
677/// # Parameters
678///
679/// - `remap`: Input consumed by [`crate::docs::docs_output_remap_append`].
680/// - `source`: Input consumed by [`crate::docs::docs_output_remap_append`].
681/// - `output`: Input consumed by [`crate::docs::docs_output_remap_append`].
682///
683/// # Returns
684///
685/// Returns `true` when the condition checked by [`crate::docs::docs_output_remap_append`] holds and `false` otherwise.
686///
687/// # Examples
688///
689/// ```rust,ignore
690/// let _result = pharos_runtime_docs::docs::docs_output_remap_append(todo!(), todo!(), todo!());
691/// ```
692///
693/// # Related
694///
695/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
696pub fn docs_output_remap_append(remap: &mut DocsOutputRemap, source: &str, output: &str) -> bool {
697    if source.trim().is_empty() || output.trim().is_empty() || remap.entries.len() >= MAX_DOC_FILES
698    {
699        return false;
700    }
701    remap.entries.push((source.to_owned(), output.to_owned()));
702    true
703}
704
705/// Performs docs output remap lookup source.
706///
707/// # Parameters
708///
709/// - `remap`: Input consumed by [`crate::docs::docs_output_remap_lookup_source`].
710/// - `source`: Input consumed by [`crate::docs::docs_output_remap_lookup_source`].
711///
712/// # Returns
713///
714/// Returns [`Some`] when [`crate::docs::docs_output_remap_lookup_source`] can produce a value and [`None`] otherwise.
715///
716/// # Performance
717///
718/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_output_remap_lookup_source`].
719///
720/// # Examples
721///
722/// ```rust,ignore
723/// let _result = pharos_runtime_docs::docs::docs_output_remap_lookup_source(todo!(), todo!());
724/// ```
725///
726/// # Related
727///
728/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
729pub fn docs_output_remap_lookup_source<'a>(
730    remap: &'a DocsOutputRemap,
731    source: &str,
732) -> Option<&'a str> {
733    remap
734        .entries
735        .iter()
736        .find_map(|(key, value)| (key == source).then_some(value.as_str()))
737}
738
739/// Performs docs output remap load JSON array.
740///
741/// # Parameters
742///
743/// - `entries`: Input consumed by [`crate::docs::docs_output_remap_load_json_array`].
744/// - `remap`: Input consumed by [`crate::docs::docs_output_remap_load_json_array`].
745///
746/// # Returns
747///
748/// Returns the computed value produced by [`crate::docs::docs_output_remap_load_json_array`].
749///
750/// # Performance
751///
752/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_output_remap_load_json_array`].
753///
754/// # Examples
755///
756/// ```rust,ignore
757/// let _result = pharos_runtime_docs::docs::docs_output_remap_load_json_array(todo!(), todo!());
758/// ```
759///
760/// # Related
761///
762/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
763pub fn docs_output_remap_load_json_array(entries: &Value, remap: &mut DocsOutputRemap) -> i32 {
764    let Some(entries) = entries.as_array() else {
765        return 66;
766    };
767    docs_output_remap_clear(remap);
768    for entry in entries {
769        let Some(source) = entry.get("source").and_then(Value::as_str) else {
770            docs_output_remap_clear(remap);
771            return 66;
772        };
773        let Some(output) = entry.get("output").and_then(Value::as_str) else {
774            docs_output_remap_clear(remap);
775            return 66;
776        };
777        if !docs_output_remap_append(remap, source, output) {
778            docs_output_remap_clear(remap);
779            return 69;
780        }
781    }
782    0
783}
784
785/// Performs docs output remap load YAML.
786///
787/// # Parameters
788///
789/// - `yaml_text`: Input consumed by [`crate::docs::docs_output_remap_load_yaml`].
790/// - `remap`: Input consumed by [`crate::docs::docs_output_remap_load_yaml`].
791///
792/// # Returns
793///
794/// Returns the computed value produced by [`crate::docs::docs_output_remap_load_yaml`].
795///
796/// # Performance
797///
798/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_output_remap_load_yaml`].
799///
800/// # Examples
801///
802/// ```rust,ignore
803/// let _result = pharos_runtime_docs::docs::docs_output_remap_load_yaml(todo!(), todo!());
804/// ```
805///
806/// # Related
807///
808/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
809pub fn docs_output_remap_load_yaml(yaml_text: &str, remap: &mut DocsOutputRemap) -> i32 {
810    let Ok(documents) = saphyr::Yaml::load_from_str(yaml_text) else {
811        return 66;
812    };
813    let Some(root) = documents.first() else {
814        docs_output_remap_clear(remap);
815        return 0;
816    };
817    let Some(items) = root.as_vec() else {
818        return 66;
819    };
820    let values: Vec<Value> = items
821        .iter()
822        .filter_map(|item| {
823            let saphyr::Yaml::Mapping(mapping) = item else {
824                return None;
825            };
826            let source = mapping.iter().find_map(|(key, value)| {
827                (key.as_str() == Some("source"))
828                    .then(|| value.as_str().map(ToOwned::to_owned))
829                    .flatten()
830            })?;
831            let output = mapping.iter().find_map(|(key, value)| {
832                (key.as_str() == Some("output"))
833                    .then(|| value.as_str().map(ToOwned::to_owned))
834                    .flatten()
835            })?;
836            Some(serde_json::json!({"source": source, "output": output}))
837        })
838        .collect();
839    docs_output_remap_load_json_array(&Value::Array(values), remap)
840}
841
842/// Performs docs default HTML output rel.
843fn docs_default_html_output_rel(rel_path: &str) -> Option<String> {
844    if rel_path.is_empty() {
845        return None;
846    }
847    let path = Path::new(rel_path);
848    let name = path.file_name()?.to_str()?;
849    let parent = path.parent().filter(|value| !value.as_os_str().is_empty());
850    let output_name = if matches!(name, "README.md" | "index.md") {
851        "index.html".to_owned()
852    } else if let Some(stem) = name.strip_suffix(".md") {
853        format!("{stem}.html")
854    } else {
855        name.to_owned()
856    };
857    Some(
858        parent
859            .map(|value| value.join(&output_name).to_string_lossy().into_owned())
860            .unwrap_or(output_name),
861    )
862}
863
864/// Performs docs output remap lookup default HTML.
865///
866/// # Parameters
867///
868/// - `remap`: Input consumed by [`crate::docs::docs_output_remap_lookup_default_html`].
869/// - `html_target`: Input consumed by [`crate::docs::docs_output_remap_lookup_default_html`].
870///
871/// # Returns
872///
873/// Returns [`Some`] when [`crate::docs::docs_output_remap_lookup_default_html`] can produce a value and [`None`] otherwise.
874///
875/// # Performance
876///
877/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_output_remap_lookup_default_html`].
878///
879/// # Examples
880///
881/// ```rust,ignore
882/// let _result = pharos_runtime_docs::docs::docs_output_remap_lookup_default_html(todo!(), todo!());
883/// ```
884///
885/// # Related
886///
887/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
888pub fn docs_output_remap_lookup_default_html<'a>(
889    remap: &'a DocsOutputRemap,
890    html_target: &str,
891) -> Option<&'a str> {
892    remap.entries.iter().find_map(|(source, output)| {
893        (docs_default_html_output_rel(source).as_deref() == Some(html_target))
894            .then_some(output.as_str())
895    })
896}
897
898/// Performs docs HTML output rel.
899///
900/// # Parameters
901///
902/// - `rel_path`: Input consumed by [`crate::docs::docs_html_output_rel`].
903///
904/// # Returns
905///
906/// Returns [`Some`] when [`crate::docs::docs_html_output_rel`] can produce a value and [`None`] otherwise.
907///
908/// # Examples
909///
910/// ```rust,ignore
911/// let _result = pharos_runtime_docs::docs::docs_html_output_rel(todo!());
912/// ```
913///
914/// # Related
915///
916/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
917pub fn docs_html_output_rel(rel_path: &str) -> Option<String> {
918    docs_default_html_output_rel(rel_path)
919}
920
921/// Performs docs HTML output rel with remap.
922///
923/// # Parameters
924///
925/// - `rel_path`: Input consumed by [`crate::docs::docs_html_output_rel_with_remap`].
926/// - `remap`: Input consumed by [`crate::docs::docs_html_output_rel_with_remap`].
927///
928/// # Returns
929///
930/// Returns [`Some`] when [`crate::docs::docs_html_output_rel_with_remap`] can produce a value and [`None`] otherwise.
931///
932/// # Examples
933///
934/// ```rust,ignore
935/// let _result = pharos_runtime_docs::docs::docs_html_output_rel_with_remap(todo!(), todo!());
936/// ```
937///
938/// # Related
939///
940/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
941pub fn docs_html_output_rel_with_remap(
942    rel_path: &str,
943    remap: Option<&DocsOutputRemap>,
944) -> Option<String> {
945    if rel_path.is_empty() {
946        return None;
947    }
948    if rel_path.ends_with(".html") {
949        return Some(rel_path.to_owned());
950    }
951    remap
952        .and_then(|value| docs_output_remap_lookup_source(value, rel_path))
953        .map(ToOwned::to_owned)
954        .or_else(|| docs_default_html_output_rel(rel_path))
955}
956
957/// Performs docs page depth.
958///
959/// # Parameters
960///
961/// - `page_rel`: Input consumed by [`crate::docs::docs_page_depth`].
962///
963/// # Returns
964///
965/// Returns the computed value produced by [`crate::docs::docs_page_depth`].
966///
967/// # Performance
968///
969/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_page_depth`].
970///
971/// # Examples
972///
973/// ```rust,ignore
974/// let _result = pharos_runtime_docs::docs::docs_page_depth(todo!());
975/// ```
976///
977/// # Related
978///
979/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
980pub fn docs_page_depth(page_rel: &str) -> usize {
981    page_rel.bytes().filter(|byte| *byte == b'/').count()
982}
983
984/// Performs docs relative prefix.
985///
986/// # Parameters
987///
988/// - `depth`: Input consumed by [`crate::docs::docs_relative_prefix`].
989///
990/// # Returns
991///
992/// Returns [`Some`] when [`crate::docs::docs_relative_prefix`] can produce a value and [`None`] otherwise.
993///
994/// # Examples
995///
996/// ```rust,ignore
997/// let _result = pharos_runtime_docs::docs::docs_relative_prefix(todo!());
998/// ```
999///
1000/// # Related
1001///
1002/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1003pub fn docs_relative_prefix(depth: usize) -> Option<String> {
1004    (depth <= MAX_DOC_FILES).then(|| "../".repeat(depth))
1005}
1006
1007/// Performs docs root href.
1008///
1009/// # Parameters
1010///
1011/// - `page_rel`: Input consumed by [`crate::docs::docs_root_href`].
1012///
1013/// # Returns
1014///
1015/// Returns [`Some`] when [`crate::docs::docs_root_href`] can produce a value and [`None`] otherwise.
1016///
1017/// # Examples
1018///
1019/// ```rust,ignore
1020/// let _result = pharos_runtime_docs::docs::docs_root_href(todo!());
1021/// ```
1022///
1023/// # Related
1024///
1025/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1026pub fn docs_root_href(page_rel: &str) -> Option<String> {
1027    if page_rel.is_empty() {
1028        return None;
1029    }
1030    let depth = docs_page_depth(page_rel);
1031    if depth == 0 {
1032        Some("index.html".to_owned())
1033    } else {
1034        Some(format!("{}index.html", docs_relative_prefix(depth)?))
1035    }
1036}
1037
1038/// Builds the relative href from output.
1039fn relative_href_from_output(from_page: &str, target: &str) -> Option<String> {
1040    let from_dir = from_page
1041        .rfind('/')
1042        .map(|index| &from_page[..=index])
1043        .unwrap_or("");
1044    let mut common = 0usize;
1045    let from_bytes = from_dir.as_bytes();
1046    let target_bytes = target.as_bytes();
1047    while common < from_bytes.len()
1048        && common < target_bytes.len()
1049        && from_bytes[common] == target_bytes[common]
1050    {
1051        common += 1;
1052    }
1053    while common > 0 && from_dir.as_bytes().get(common - 1) != Some(&b'/') {
1054        common -= 1;
1055    }
1056    let up = from_dir[common..]
1057        .bytes()
1058        .filter(|byte| *byte == b'/')
1059        .count();
1060    Some(format!(
1061        "{}{}",
1062        "../".repeat(up),
1063        &target[common.min(target.len())..]
1064    ))
1065}
1066
1067/// Resolves docs relative target.
1068fn resolve_docs_relative_target(page_rel: &str, target: &str) -> Option<String> {
1069    if page_rel.is_empty() || target.is_empty() {
1070        return None;
1071    }
1072    let page_dir = Path::new(page_rel)
1073        .parent()
1074        .unwrap_or_else(|| Path::new(""));
1075    let candidate = page_dir.join(target);
1076    let mut segments: Vec<String> = Vec::new();
1077    for component in candidate.components() {
1078        match component {
1079            std::path::Component::CurDir => {}
1080            std::path::Component::Normal(segment) => {
1081                segments.push(segment.to_str()?.to_owned());
1082            }
1083            std::path::Component::ParentDir => {
1084                segments.pop()?;
1085            }
1086            std::path::Component::RootDir | std::path::Component::Prefix(_) => return None,
1087        }
1088    }
1089    (!segments.is_empty()).then(|| segments.join("/"))
1090}
1091
1092#[allow(non_snake_case)]
1093/// Performs CLOSE LIST.
1094///
1095/// # Parameters
1096///
1097/// - `output`: Input consumed by [`crate::docs::CLOSE_LIST`].
1098/// - `ordered`: Input consumed by [`crate::docs::CLOSE_LIST`].
1099///
1100/// # Returns
1101///
1102/// Returns `true` when the condition checked by [`crate::docs::CLOSE_LIST`] holds and `false` otherwise.
1103///
1104/// # Examples
1105///
1106/// ```rust,ignore
1107/// let _result = pharos_runtime_docs::docs::CLOSE_LIST(todo!(), todo!());
1108/// ```
1109///
1110/// # Related
1111///
1112/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1113pub fn CLOSE_LIST(output: &mut String, ordered: bool) -> bool {
1114    if ordered {
1115        dbuf_append(output, "</ol>\n")
1116    } else {
1117        dbuf_append(output, "</ul>\n")
1118    }
1119}
1120
1121/// Performs docs markdown to HTML.
1122///
1123/// # Parameters
1124///
1125/// - `markdown_text`: Input consumed by [`crate::docs::docs_markdown_to_html`].
1126///
1127/// # Returns
1128///
1129/// Returns [`Some`] when [`crate::docs::docs_markdown_to_html`] can produce a value and [`None`] otherwise.
1130///
1131/// # Examples
1132///
1133/// ```rust,ignore
1134/// let _result = pharos_runtime_docs::docs::docs_markdown_to_html(todo!());
1135/// ```
1136///
1137/// # Related
1138///
1139/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1140pub fn docs_markdown_to_html(markdown_text: &str) -> Option<String> {
1141    docs_markdown_to_html_with_remap_for_page(markdown_text, None, "index.html")
1142}
1143
1144/// Performs docs markdown to HTML with remap.
1145///
1146/// # Parameters
1147///
1148/// - `markdown_text`: Input consumed by [`crate::docs::docs_markdown_to_html_with_remap`].
1149/// - `remap`: Input consumed by [`crate::docs::docs_markdown_to_html_with_remap`].
1150///
1151/// # Returns
1152///
1153/// Returns [`Some`] when [`crate::docs::docs_markdown_to_html_with_remap`] can produce a value and [`None`] otherwise.
1154///
1155/// # Examples
1156///
1157/// ```rust,ignore
1158/// let _result = pharos_runtime_docs::docs::docs_markdown_to_html_with_remap(todo!(), todo!());
1159/// ```
1160///
1161/// # Related
1162///
1163/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1164pub fn docs_markdown_to_html_with_remap(
1165    markdown_text: &str,
1166    remap: Option<&DocsOutputRemap>,
1167) -> Option<String> {
1168    docs_markdown_to_html_with_remap_for_page(markdown_text, remap, "index.html")
1169}
1170
1171/// Appends one required docs fragment and converts failure into a render failure.
1172fn append_docs_text(output: &mut String, value: &str) -> Option<()> {
1173    dbuf_append(output, value).then_some(())
1174}
1175
1176/// Converts the supported Markdown and authored HTML mixture used by Pharos docs.
1177///
1178/// # Parameters
1179///
1180/// - `markdown_text`: Input consumed by [`crate::docs::docs_markdown_to_html_with_remap_for_page`].
1181/// - `remap`: Input consumed by [`crate::docs::docs_markdown_to_html_with_remap_for_page`].
1182/// - `page_rel`: Input consumed by [`crate::docs::docs_markdown_to_html_with_remap_for_page`].
1183///
1184/// # Returns
1185///
1186/// Returns [`Some`] when [`crate::docs::docs_markdown_to_html_with_remap_for_page`] can produce a value and [`None`] otherwise.
1187///
1188/// # Performance
1189///
1190/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_markdown_to_html_with_remap_for_page`].
1191///
1192/// # Examples
1193///
1194/// ```rust,ignore
1195/// let _result = pharos_runtime_docs::docs::docs_markdown_to_html_with_remap_for_page(todo!(), todo!(), todo!());
1196/// ```
1197///
1198/// # Related
1199///
1200/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1201pub fn docs_markdown_to_html_with_remap_for_page(
1202    markdown_text: &str,
1203    remap: Option<&DocsOutputRemap>,
1204    page_rel: &str,
1205) -> Option<String> {
1206    if markdown_text.len() > MAX_DOC_TEXT_BYTES || page_rel.is_empty() {
1207        return None;
1208    }
1209    let mut output = String::new();
1210    let mut paragraph = String::new();
1211    let mut list_kind = None;
1212    let mut code_fence = None;
1213    for line in markdown_text.lines() {
1214        let trimmed = line.trim_end();
1215        if let Some(language) = trimmed.strip_prefix("```") {
1216            if code_fence.is_some() {
1217                append_docs_text(&mut output, "</code></pre>\n")?;
1218                code_fence = None;
1219            } else {
1220                flush_docs_paragraph(&mut output, &mut paragraph, page_rel, remap)?;
1221                close_docs_list(&mut output, &mut list_kind);
1222                let language = language.trim();
1223                if language.len() > 32
1224                    || !language
1225                        .bytes()
1226                        .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_'))
1227                {
1228                    return None;
1229                }
1230                if language.is_empty() {
1231                    append_docs_text(&mut output, "<pre><code>")?;
1232                } else {
1233                    append_docs_text(
1234                        &mut output,
1235                        &format!("<pre><code class=\"language-{language}\">"),
1236                    )?;
1237                }
1238                code_fence = Some(language.to_owned());
1239            }
1240            continue;
1241        }
1242        if code_fence.is_some() {
1243            append_docs_text(&mut output, &html_escape(line))?;
1244            append_docs_text(&mut output, "\n")?;
1245            continue;
1246        }
1247        if let Some((level, title)) = markdown_heading(trimmed) {
1248            flush_docs_paragraph(&mut output, &mut paragraph, page_rel, remap)?;
1249            close_docs_list(&mut output, &mut list_kind);
1250            append_docs_text(&mut output, &format!("<h{level}"))?;
1251            append_docs_text(&mut output, ">")?;
1252            append_inline_markup_for_page(&mut output, title, page_rel, remap)?;
1253            append_docs_text(&mut output, &format!("</h{level}>\n"))?;
1254            continue;
1255        }
1256        if let Some((ordered, item)) = markdown_list_item(trimmed) {
1257            flush_docs_paragraph(&mut output, &mut paragraph, page_rel, remap)?;
1258            if list_kind.is_some_and(|current| current != ordered) {
1259                close_docs_list(&mut output, &mut list_kind);
1260            }
1261            if list_kind.is_none() {
1262                append_docs_text(&mut output, if ordered { "<ol>\n" } else { "<ul>\n" })?;
1263                list_kind = Some(ordered);
1264            }
1265            append_docs_text(&mut output, "<li>")?;
1266            append_inline_markup_for_page(&mut output, item, page_rel, remap)?;
1267            append_docs_text(&mut output, "</li>\n")?;
1268            continue;
1269        }
1270        if trimmed.is_empty() {
1271            flush_docs_paragraph(&mut output, &mut paragraph, page_rel, remap)?;
1272            close_docs_list(&mut output, &mut list_kind);
1273            continue;
1274        }
1275        if trimmed.starts_with('<') {
1276            flush_docs_paragraph(&mut output, &mut paragraph, page_rel, remap)?;
1277            close_docs_list(&mut output, &mut list_kind);
1278            let rewritten = rewrite_docs_html_targets(trimmed, page_rel, remap)?;
1279            append_docs_text(&mut output, &rewritten)?;
1280            append_docs_text(&mut output, "\n")?;
1281            continue;
1282        }
1283        if !paragraph.is_empty() {
1284            paragraph.push(' ');
1285        }
1286        paragraph.push_str(trimmed);
1287    }
1288    if code_fence.is_some() {
1289        return None;
1290    }
1291    flush_docs_paragraph(&mut output, &mut paragraph, page_rel, remap)?;
1292    close_docs_list(&mut output, &mut list_kind);
1293    Some(output)
1294}
1295
1296/// Extracts one supported unordered or ordered Markdown list item.
1297fn markdown_list_item(line: &str) -> Option<(bool, &str)> {
1298    if let Some(item) = line.strip_prefix("- ") {
1299        return Some((false, item));
1300    }
1301    let (number, item) = line.split_once(". ")?;
1302    (!number.is_empty() && number.bytes().all(|byte| byte.is_ascii_digit())).then_some((true, item))
1303}
1304
1305/// Extracts a Markdown heading level and its title from one source line.
1306fn markdown_heading(line: &str) -> Option<(usize, &str)> {
1307    let level = line.bytes().take_while(|byte| *byte == b'#').count();
1308    (1..=6)
1309        .contains(&level)
1310        .then(|| {
1311            line.get(level..)?
1312                .strip_prefix(' ')
1313                .map(|title| (level, title.trim()))
1314        })
1315        .flatten()
1316}
1317
1318/// Flushes one paragraph through the page-aware inline renderer.
1319fn flush_docs_paragraph(
1320    output: &mut String,
1321    paragraph: &mut String,
1322    page_rel: &str,
1323    remap: Option<&DocsOutputRemap>,
1324) -> Option<()> {
1325    if paragraph.trim().is_empty() {
1326        paragraph.clear();
1327        return Some(());
1328    }
1329    append_docs_text(output, "<p>")?;
1330    append_inline_markup_for_page(output, paragraph.trim(), page_rel, remap)?;
1331    append_docs_text(output, "</p>\n")?;
1332    paragraph.clear();
1333    Some(())
1334}
1335
1336/// Closes an open Markdown list.
1337fn close_docs_list(output: &mut String, list_kind: &mut Option<bool>) {
1338    if let Some(ordered) = list_kind.take() {
1339        let _ = dbuf_append(output, if ordered { "</ol>\n" } else { "</ul>\n" });
1340    }
1341}
1342
1343/// Renders inline Markdown while rewriting local links for one output page.
1344fn append_inline_markup_for_page(
1345    output: &mut String,
1346    text: &str,
1347    page_rel: &str,
1348    remap: Option<&DocsOutputRemap>,
1349) -> Option<()> {
1350    if text.len() > 256 * 1024 {
1351        return None;
1352    }
1353    let mut cursor = 0usize;
1354    while cursor < text.len() {
1355        let remainder = &text[cursor..];
1356        if let Some(close) = remainder
1357            .strip_prefix('`')
1358            .and_then(|value| value.find('`'))
1359        {
1360            append_docs_text(output, "<code>")?;
1361            append_docs_text(output, &html_escape(&remainder[1..close + 1]))?;
1362            append_docs_text(output, "</code>")?;
1363            cursor += close + 2;
1364            continue;
1365        }
1366        if let Some(close) = remainder
1367            .strip_prefix("**")
1368            .and_then(|value| value.find("**"))
1369        {
1370            append_docs_text(output, "<strong>")?;
1371            append_inline_markup_for_page(output, &remainder[2..close + 2], page_rel, remap)?;
1372            append_docs_text(output, "</strong>")?;
1373            cursor += close + 4;
1374            continue;
1375        }
1376        if remainder.starts_with('[')
1377            && let Some(label_end) = remainder.find("](")
1378            && let Some(target_end) = remainder[label_end + 2..].find(')')
1379        {
1380            let target_end = label_end + 2 + target_end;
1381            let target = &remainder[label_end + 2..target_end];
1382            let href = rewrite_docs_target(target, page_rel, remap)?;
1383            append_docs_text(output, "<a href=\"")?;
1384            append_docs_text(output, &html_escape(&href))?;
1385            append_docs_text(output, "\">")?;
1386            append_inline_markup_for_page(output, &remainder[1..label_end], page_rel, remap)?;
1387            append_docs_text(output, "</a>")?;
1388            cursor += target_end + 1;
1389            continue;
1390        }
1391        let next = [
1392            remainder.find('`'),
1393            remainder.find("**"),
1394            remainder.find('['),
1395        ]
1396        .into_iter()
1397        .flatten()
1398        .min()
1399        .unwrap_or(remainder.len());
1400        append_docs_text(output, &html_escape(&remainder[..next]))?;
1401        cursor += next.max(1);
1402    }
1403    Some(())
1404}
1405
1406/// Rewrites local HTML attribute targets relative to a generated docs page.
1407fn rewrite_docs_html_targets(
1408    line: &str,
1409    page_rel: &str,
1410    remap: Option<&DocsOutputRemap>,
1411) -> Option<String> {
1412    let mut output = String::with_capacity(line.len() + 32);
1413    let mut cursor = 0usize;
1414    while cursor < line.len() {
1415        let remainder = &line[cursor..];
1416        let candidates = [remainder.find("src=\""), remainder.find("href=\"")];
1417        let Some(offset) = candidates.into_iter().flatten().min() else {
1418            output.push_str(remainder);
1419            break;
1420        };
1421        let attribute_end = offset + 5;
1422        output.push_str(&remainder[..attribute_end]);
1423        let value_start = attribute_end;
1424        let value = &remainder[value_start..];
1425        let close = value.find('\"')?;
1426        let target = &value[..close];
1427        output.push_str(&html_escape(&rewrite_docs_target(target, page_rel, remap)?));
1428        output.push('\"');
1429        cursor += value_start + close + 1;
1430    }
1431    Some(output)
1432}
1433
1434/// Rewrites one safe local docs link or asset target.
1435fn rewrite_docs_target(
1436    target: &str,
1437    page_rel: &str,
1438    remap: Option<&DocsOutputRemap>,
1439) -> Option<String> {
1440    let (base, suffix) = target.split_once('#').map_or((target, ""), |(base, hash)| {
1441        (base, &target[target.len() - hash.len() - 1..])
1442    });
1443    if base.is_empty()
1444        || base.starts_with('/')
1445        || base.starts_with('#')
1446        || base.contains("://")
1447        || base.starts_with("mailto:")
1448        || base.starts_with("javascript:")
1449        || base.starts_with("data:")
1450    {
1451        return Some(target.to_owned());
1452    }
1453    let resolved_base =
1454        resolve_docs_relative_target(page_rel, base).unwrap_or_else(|| base.to_owned());
1455    let output_target = if resolved_base.ends_with(".md") {
1456        docs_html_output_rel_with_remap(&resolved_base, remap)?
1457    } else {
1458        resolved_base
1459    };
1460    Some(format!(
1461        "{}{}",
1462        relative_href_from_output(page_rel, &output_target)?,
1463        suffix
1464    ))
1465}
1466
1467/// Renders template value.
1468fn render_template_value(template: &str, values: &[(&str, &str, bool)]) -> Option<String> {
1469    let mut output = String::with_capacity(template.len() + 128);
1470    let mut cursor = 0usize;
1471    while cursor < template.len() {
1472        let Some(relative_open) = template[cursor..].find('{') else {
1473            output.push_str(&template[cursor..]);
1474            break;
1475        };
1476        let open = cursor + relative_open;
1477        output.push_str(&template[cursor..open]);
1478        let key_start = open + 1;
1479        let close = template[key_start..].find('}')? + key_start;
1480        let raw_key = &template[key_start..close];
1481        let (key, explicit_html) = raw_key
1482            .strip_prefix("html:")
1483            .map_or((raw_key, false), |value| (value, true));
1484        if let Some((_, value, is_html)) = values.iter().find(|(name, _, _)| *name == key) {
1485            if explicit_html || *is_html {
1486                output.push_str(value);
1487            } else {
1488                output.push_str(&html_escape(value));
1489            }
1490        } else {
1491            output.push_str(&template[open..=close]);
1492        }
1493        if output.len() > MAX_DOC_TEXT_BYTES {
1494            return None;
1495        }
1496        cursor = close + 1;
1497    }
1498    Some(output)
1499}
1500
1501/// Performs docs render sidebar.
1502///
1503/// # Parameters
1504///
1505/// - `sidebar_template_html`: Input consumed by [`crate::docs::docs_render_sidebar`].
1506/// - `sidebar_item_template_html`: Input consumed by [`crate::docs::docs_render_sidebar`].
1507/// - `sidebar_title`: Input consumed by [`crate::docs::docs_render_sidebar`].
1508/// - `nav_entries`: Input consumed by [`crate::docs::docs_render_sidebar`].
1509/// - `current_page_html_rel`: Input consumed by [`crate::docs::docs_render_sidebar`].
1510///
1511/// # Returns
1512///
1513/// Returns [`Some`] when [`crate::docs::docs_render_sidebar`] can produce a value and [`None`] otherwise.
1514///
1515/// # Performance
1516///
1517/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_render_sidebar`].
1518///
1519/// # Examples
1520///
1521/// ```rust,ignore
1522/// let _result = pharos_runtime_docs::docs::docs_render_sidebar(todo!(), todo!(), todo!(), todo!(), todo!());
1523/// ```
1524///
1525/// # Related
1526///
1527/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1528pub fn docs_render_sidebar(
1529    sidebar_template_html: &str,
1530    sidebar_item_template_html: &str,
1531    sidebar_group_template_html: &str,
1532    sidebar_title: &str,
1533    nav_entries: &[DocsNavEntry],
1534    current_page_html_rel: &str,
1535) -> Option<String> {
1536    if sidebar_template_html.is_empty()
1537        || sidebar_item_template_html.is_empty()
1538        || current_page_html_rel.is_empty()
1539    {
1540        return None;
1541    }
1542    let nav_nodes = docs_nav_tree(nav_entries)?;
1543    let mut items = String::new();
1544    let mut group_id = 0usize;
1545    for node in nav_nodes.iter().take(MAX_DOC_FILES) {
1546        let item = docs_render_sidebar_node(
1547            sidebar_item_template_html,
1548            sidebar_group_template_html,
1549            node,
1550            current_page_html_rel,
1551            &mut group_id,
1552        )?;
1553        items.push_str(&item);
1554        if items.len() > MAX_DOC_TEXT_BYTES {
1555            return None;
1556        }
1557    }
1558    let title_block = if sidebar_title.is_empty() {
1559        String::new()
1560    } else {
1561        format!(
1562            "<div class=\"docs-sidebar-title\">{}</div>\n",
1563            html_escape(sidebar_title)
1564        )
1565    };
1566    render_template_value(
1567        sidebar_template_html,
1568        &[
1569            ("title", sidebar_title, false),
1570            ("title_block", &title_block, true),
1571            ("items", &items, true),
1572        ],
1573    )
1574}
1575
1576fn docs_nav_tree(nav_entries: &[DocsNavEntry]) -> Option<Vec<DocsNavNode>> {
1577    let mut index = 0usize;
1578    docs_nav_parse_nodes(nav_entries, &mut index, 0)
1579}
1580
1581fn docs_nav_parse_nodes(
1582    nav_entries: &[DocsNavEntry],
1583    index: &mut usize,
1584    depth: usize,
1585) -> Option<Vec<DocsNavNode>> {
1586    let mut nodes = Vec::new();
1587    while *index < nav_entries.len() {
1588        let entry = nav_entries.get(*index)?;
1589        if entry.depth < depth {
1590            break;
1591        }
1592        if entry.depth > depth {
1593            return None;
1594        }
1595        let mut node = DocsNavNode {
1596            path: entry.path.clone(),
1597            label: entry.label.clone(),
1598            depth: entry.depth,
1599            children: Vec::new(),
1600        };
1601        *index += 1;
1602        if *index < nav_entries.len() && nav_entries.get(*index)?.depth > depth {
1603            node.children = docs_nav_parse_nodes(nav_entries, index, depth + 1)?;
1604        }
1605        nodes.push(node);
1606    }
1607    Some(nodes)
1608}
1609
1610fn docs_sidebar_href(current_page_html_rel: &str, path: &str) -> Option<String> {
1611    if path == "index.html" {
1612        let depth = docs_page_depth(current_page_html_rel);
1613        if depth == 0 {
1614            Some("index.html".to_owned())
1615        } else {
1616            Some(format!("{}index.html", "../".repeat(depth)))
1617        }
1618    } else {
1619        relative_href_from_output(current_page_html_rel, path)
1620    }
1621}
1622
1623fn docs_nav_node_contains_current(node: &DocsNavNode, current_page_html_rel: &str) -> bool {
1624    node.path == current_page_html_rel
1625        || node
1626            .children
1627            .iter()
1628            .any(|child| docs_nav_node_contains_current(child, current_page_html_rel))
1629}
1630
1631fn docs_nav_default_open(node: &DocsNavNode) -> bool {
1632    node.depth == 0 && node.label == "Start Here"
1633}
1634
1635fn docs_render_sidebar_node(
1636    sidebar_item_template_html: &str,
1637    sidebar_group_template_html: &str,
1638    node: &DocsNavNode,
1639    current_page_html_rel: &str,
1640    group_id: &mut usize,
1641) -> Option<String> {
1642    let href = docs_sidebar_href(current_page_html_rel, &node.path)?;
1643    let current = node.path == current_page_html_rel;
1644    let contains_current = docs_nav_node_contains_current(node, current_page_html_rel);
1645    let depth_class = format!("docs-depth-{}", node.depth);
1646    let active_suffix = if current { " is-active" } else { "" };
1647    let active_attr = if current {
1648        " aria-current=\"page\""
1649    } else {
1650        ""
1651    };
1652    let item_link = render_template_value(
1653        sidebar_item_template_html,
1654        &[
1655            ("href", &href, false),
1656            ("label", &node.label, false),
1657            ("depth_class", &depth_class, false),
1658            ("active_suffix", active_suffix, false),
1659            ("active_attr", active_attr, true),
1660        ],
1661    )?;
1662    if node.children.is_empty() {
1663        return Some(item_link);
1664    }
1665    *group_id = group_id.saturating_add(1);
1666    let current_group_id = format!("docs-nav-group-{}", group_id);
1667    let is_open = docs_nav_default_open(node) || contains_current;
1668    let expanded_attr = if is_open { "true" } else { "false" };
1669    let open_class = if is_open { " is-open" } else { "" };
1670    let mut children_html = String::new();
1671    for child in node.children.iter().take(MAX_DOC_FILES) {
1672        let child_html = docs_render_sidebar_node(
1673            sidebar_item_template_html,
1674            sidebar_group_template_html,
1675            child,
1676            current_page_html_rel,
1677            group_id,
1678        )?;
1679        children_html.push_str(&child_html);
1680        if children_html.len() > MAX_DOC_TEXT_BYTES {
1681            return None;
1682        }
1683    }
1684    render_template_value(
1685        sidebar_group_template_html,
1686        &[
1687            ("depth", &node.depth.to_string(), false),
1688            ("open_class", open_class, false),
1689            ("expanded", expanded_attr, false),
1690            ("group_id", &current_group_id, false),
1691            ("label", &node.label, false),
1692            ("item_link", &item_link, true),
1693            ("children", &children_html, true),
1694        ],
1695    )
1696}
1697
1698#[allow(clippy::too_many_arguments)]
1699/// Performs docs render page.
1700///
1701/// # Parameters
1702///
1703/// - `template_html`: Input consumed by [`crate::docs::docs_render_page`].
1704/// - `title`: Input consumed by [`crate::docs::docs_render_page`].
1705/// - `sidebar_html`: Input consumed by [`crate::docs::docs_render_page`].
1706/// - `body_html`: Input consumed by [`crate::docs::docs_render_page`].
1707/// - `back_href`: Input consumed by [`crate::docs::docs_render_page`].
1708/// - `wordmark_src`: Input consumed by [`crate::docs::docs_render_page`].
1709/// - `favicon_src`: Input consumed by [`crate::docs::docs_render_page`].
1710/// - `stylesheet_src`: Input consumed by [`crate::docs::docs_render_page`].
1711///
1712/// # Returns
1713///
1714/// Returns [`Some`] when [`crate::docs::docs_render_page`] can produce a value and [`None`] otherwise.
1715///
1716/// # Examples
1717///
1718/// ```rust,ignore
1719/// let _result = pharos_runtime_docs::docs::docs_render_page(todo!(), todo!(), todo!(), todo!(), todo!(), todo!(), todo!(), todo!());
1720/// ```
1721///
1722/// # Related
1723///
1724/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1725pub fn docs_render_page(
1726    template_html: &str,
1727    title: &str,
1728    sidebar_html: &str,
1729    body_html: &str,
1730    back_href: &str,
1731    wordmark_src: &str,
1732    favicon_src: &str,
1733    stylesheet_src: &str,
1734) -> Option<String> {
1735    if template_html.is_empty() {
1736        return None;
1737    }
1738    render_template_value(
1739        template_html,
1740        &[
1741            ("title", title, false),
1742            ("sidebar", sidebar_html, true),
1743            ("body", body_html, true),
1744            ("back_href", back_href, false),
1745            ("wordmark_src", wordmark_src, false),
1746            ("favicon_src", favicon_src, false),
1747            ("stylesheet_src", stylesheet_src, false),
1748        ],
1749    )
1750}
1751
1752/// Collects md files.
1753///
1754/// # Parameters
1755///
1756/// - `dir_path`: Input consumed by [`crate::docs::collect_md_files`].
1757/// - `base_prefix`: Input consumed by [`crate::docs::collect_md_files`].
1758/// - `depth`: Input consumed by [`crate::docs::collect_md_files`].
1759/// - `max_depth`: Input consumed by [`crate::docs::collect_md_files`].
1760/// - `files`: Input consumed by [`crate::docs::collect_md_files`].
1761///
1762/// # Returns
1763///
1764/// Returns `true` when the condition checked by [`crate::docs::collect_md_files`] holds and `false` otherwise.
1765///
1766/// # Performance
1767///
1768/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::collect_md_files`].
1769///
1770/// # Examples
1771///
1772/// ```rust,ignore
1773/// let _result = pharos_runtime_docs::docs::collect_md_files(todo!(), todo!(), todo!(), todo!(), todo!());
1774/// ```
1775///
1776/// # Related
1777///
1778/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1779pub fn collect_md_files(
1780    dir_path: &Path,
1781    base_prefix: &str,
1782    depth: usize,
1783    max_depth: usize,
1784    files: &mut Vec<(String, PathBuf)>,
1785) -> bool {
1786    if depth > max_depth || files.len() > MAX_DOC_FILES {
1787        return false;
1788    }
1789    let Ok(entries) = fs::read_dir(dir_path) else {
1790        return true;
1791    };
1792    for entry in entries {
1793        let Ok(entry) = entry else {
1794            return false;
1795        };
1796        let path = entry.path();
1797        let Some(name) = entry.file_name().to_str().map(ToOwned::to_owned) else {
1798            continue;
1799        };
1800        if name == "." || name == ".." {
1801            continue;
1802        }
1803        let relative = if base_prefix.is_empty() {
1804            name.clone()
1805        } else {
1806            format!("{base_prefix}/{name}")
1807        };
1808        if path.is_dir() {
1809            if !collect_md_files(&path, &relative, depth + 1, max_depth, files) {
1810                return false;
1811            }
1812        } else if name.ends_with(".md") {
1813            if files.len() >= MAX_DOC_FILES {
1814                return false;
1815            }
1816            files.push((relative, path));
1817        }
1818    }
1819    true
1820}
1821
1822/// Performs docs page title.
1823///
1824/// # Parameters
1825///
1826/// - `markdown_text`: Input consumed by [`crate::docs::docs_page_title`].
1827/// - `rel_path`: Input consumed by [`crate::docs::docs_page_title`].
1828///
1829/// # Returns
1830///
1831/// Returns [`Some`] when [`crate::docs::docs_page_title`] can produce a value and [`None`] otherwise.
1832///
1833/// # Performance
1834///
1835/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_page_title`].
1836///
1837/// # Examples
1838///
1839/// ```rust,ignore
1840/// let _result = pharos_runtime_docs::docs::docs_page_title(todo!(), todo!());
1841/// ```
1842///
1843/// # Related
1844///
1845/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1846pub fn docs_page_title(markdown_text: Option<&str>, rel_path: Option<&str>) -> Option<String> {
1847    if let Some(text) = markdown_text {
1848        for line in text.lines() {
1849            if let Some(title) = line
1850                .strip_prefix("# ")
1851                .map(str::trim)
1852                .filter(|value| !value.is_empty())
1853            {
1854                return Some(title.to_owned());
1855            }
1856        }
1857    }
1858    let path = rel_path.unwrap_or_default();
1859    let name = Path::new(path)
1860        .file_name()
1861        .and_then(|value| value.to_str())
1862        .unwrap_or("");
1863    if name.is_empty() {
1864        return Some("Pharos Documentation".to_owned());
1865    }
1866    if matches!(name, "README.md" | "index.md") {
1867        return if let Some(parent) = Path::new(path)
1868            .parent()
1869            .filter(|value| !value.as_os_str().is_empty())
1870        {
1871            Some(
1872                parent
1873                    .file_name()?
1874                    .to_string_lossy()
1875                    .replace(['-', '_'], " "),
1876            )
1877        } else {
1878            Some("Documentation".to_owned())
1879        };
1880    }
1881    let stem = name.strip_suffix(".md").unwrap_or(name);
1882    let mut chars = stem.chars();
1883    let mut title = chars
1884        .next()
1885        .map(|value| value.to_ascii_uppercase())
1886        .into_iter()
1887        .chain(chars)
1888        .collect::<String>();
1889    title = title.replace(['-', '_'], " ");
1890    Some(title)
1891}
1892
1893/// Performs docs nav collect entries local.
1894///
1895/// # Parameters
1896///
1897/// - `items`: Input consumed by [`crate::docs::docs_nav_collect_entries_local`].
1898/// - `depth`: Input consumed by [`crate::docs::docs_nav_collect_entries_local`].
1899/// - `entries`: Input consumed by [`crate::docs::docs_nav_collect_entries_local`].
1900///
1901/// # Returns
1902///
1903/// Returns `true` when the condition checked by [`crate::docs::docs_nav_collect_entries_local`] holds and `false` otherwise.
1904///
1905/// # Performance
1906///
1907/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::docs::docs_nav_collect_entries_local`].
1908///
1909/// # Examples
1910///
1911/// ```rust,ignore
1912/// let _result = pharos_runtime_docs::docs::docs_nav_collect_entries_local(todo!(), todo!(), todo!());
1913/// ```
1914///
1915/// # Related
1916///
1917/// - [`crate::docs`] for adjacent helpers that share the same runtime surface.
1918pub fn docs_nav_collect_entries_local(
1919    items: &Value,
1920    depth: usize,
1921    entries: &mut Vec<DocsNavEntry>,
1922) -> bool {
1923    let Some(items) = items.as_array() else {
1924        return true;
1925    };
1926    for item in items {
1927        if let (Some(path), Some(label)) = (
1928            item.get("path")
1929                .and_then(Value::as_str)
1930                .filter(|value| !value.is_empty()),
1931            item.get("label")
1932                .and_then(Value::as_str)
1933                .filter(|value| !value.is_empty()),
1934        ) {
1935            if entries.len() >= MAX_DOC_FILES {
1936                return false;
1937            }
1938            let Some(path) = docs_html_output_rel_with_remap(path, None) else {
1939                return false;
1940            };
1941            entries.push(DocsNavEntry {
1942                path,
1943                label: label.to_owned(),
1944                depth,
1945            });
1946        }
1947        if let Some(children) = item.get("children")
1948            && !docs_nav_collect_entries_local(children, depth + 1, entries)
1949        {
1950            return false;
1951        }
1952    }
1953    true
1954}
1955
1956#[cfg(test)]
1957mod tests {
1958    use super::{
1959        docs_markdown_to_html, docs_markdown_to_html_with_remap_for_page, docs_root_href,
1960        docs_sidebar_href,
1961    };
1962
1963    /// Keeps authored HTML inert and readable inside fenced examples.
1964    #[test]
1965    fn markdown_renders_fenced_code_as_escaped_preformatted_text() {
1966        let html = docs_markdown_to_html("```html\n<form action=\"/cart\">\n```\n")
1967            .expect("rendered markdown");
1968        assert_eq!(
1969            html,
1970            "<pre><code class=\"language-html\">&lt;form action=&quot;/cart&quot;&gt;\n</code></pre>\n"
1971        );
1972    }
1973
1974    /// Preserves the semantic distinction between numbered steps and bullets.
1975    #[test]
1976    fn markdown_renders_ordered_and_unordered_lists_separately() {
1977        let html = docs_markdown_to_html("1. Run demo\n2. Add pack\n\n- Page\n- Form\n")
1978            .expect("rendered markdown");
1979        assert!(html.contains("<ol>\n<li>Run demo</li>\n<li>Add pack</li>\n</ol>"));
1980        assert!(html.contains("<ul>\n<li>Page</li>\n<li>Form</li>\n</ul>"));
1981    }
1982
1983    #[test]
1984    /// Performs markdown rewrites nested relative links against the current page.
1985    fn markdown_rewrites_nested_relative_links_against_the_current_page() {
1986        let html = docs_markdown_to_html_with_remap_for_page(
1987            "Continue into [Why Pharos changes the app boundary](../why-pharos.md).\n",
1988            None,
1989            "tutorials/ucal.html",
1990        )
1991        .expect("rendered markdown");
1992        assert!(
1993            html.contains("<a href=\"../why-pharos.html\">Why Pharos changes the app boundary</a>")
1994        );
1995        assert!(!html.contains("../../why-pharos.html"));
1996    }
1997
1998    #[test]
1999    /// Performs docs root href matches mounted docs home route shape.
2000    fn docs_root_href_matches_mounted_docs_home_route_shape() {
2001        assert_eq!(docs_root_href("index.html").as_deref(), Some("index.html"));
2002        assert_eq!(
2003            docs_root_href("why-pharos.html").as_deref(),
2004            Some("index.html")
2005        );
2006        assert_eq!(
2007            docs_root_href("tutorials/ucal.html").as_deref(),
2008            Some("../index.html")
2009        );
2010        assert_eq!(
2011            docs_root_href("reference/library-reference.html").as_deref(),
2012            Some("../index.html")
2013        );
2014    }
2015
2016    #[test]
2017    /// Keeps generated sidebar home links explicit instead of rendering dot-path hrefs.
2018    fn docs_sidebar_href_uses_explicit_home_file() {
2019        assert_eq!(
2020            docs_sidebar_href("index.html", "index.html").as_deref(),
2021            Some("index.html")
2022        );
2023        assert_eq!(
2024            docs_sidebar_href("reference/index.html", "index.html").as_deref(),
2025            Some("../index.html")
2026        );
2027    }
2028}