Skip to main content

harn_parser/builtin_methods/
reference.rs

1//! The generated method reference in `docs/src/builtins.md`, rendered from
2//! the registry so the docs table cannot drift from the methods that exist.
3
4use super::{methods_of, MethodReceiver, REMOVED_METHODS};
5
6pub const REFERENCE_BEGIN_MARKER: &str =
7    "<!-- BEGIN GENERATED: builtin methods (make gen-builtin-methods) -->";
8pub const REFERENCE_END_MARKER: &str = "<!-- END GENERATED: builtin methods -->";
9
10const SECTIONS: [(MethodReceiver, &str); 6] = [
11    (MethodReceiver::String, "String methods"),
12    (MethodReceiver::List, "List methods"),
13    (MethodReceiver::Set, "Set methods"),
14    (MethodReceiver::Dict, "Dict methods"),
15    (MethodReceiver::Range, "Range methods"),
16    (MethodReceiver::Iter, "Iterator methods"),
17];
18
19/// Markdown tables for every builtin receiver, without the region markers.
20pub fn render_reference() -> String {
21    let mut out = String::new();
22    for (receiver, title) in SECTIONS {
23        out.push_str(&format!("### {title}\n\n"));
24        out.push_str("| Method | Signature | Mutates | Description |\n");
25        out.push_str("|---|---|---|---|\n");
26        for method in methods_of(receiver) {
27            let mutates = if method.is_mutating() { "yes" } else { "" };
28            out.push_str(&format!(
29                "| `.{}()` | `{}` | {mutates} | {} |\n",
30                method.name(),
31                escape_cell(&method.signature.to_string()),
32                escape_cell(method.doc),
33            ));
34        }
35        out.push('\n');
36    }
37    out.push_str("### Removed method spellings\n\n");
38    out.push_str(
39        "Each of these is a type error (`HARN-NAM-013`).\n\
40         `harn fix --apply --safety behavior-preserving` rewrites the ones with a\n\
41         mechanical replacement.\n\n",
42    );
43    out.push_str("| Receiver | Removed | Use instead |\n|---|---|---|\n");
44    for removed in REMOVED_METHODS {
45        out.push_str(&format!(
46            "| `{}` | `.{}()` | {} |\n",
47            removed.receiver.type_name(),
48            removed.name,
49            escape_cell(&removed.repair_hint()),
50        ));
51    }
52    out
53}
54
55/// Replace the generated region of `document` with a fresh rendering.
56/// `None` when the document lacks the markers.
57pub fn splice_reference(document: &str) -> Option<String> {
58    let begin = document.find(REFERENCE_BEGIN_MARKER)?;
59    let end = document.find(REFERENCE_END_MARKER)?;
60    if end < begin {
61        return None;
62    }
63    let head = document.get(..begin + REFERENCE_BEGIN_MARKER.len())?;
64    let tail = document.get(end..)?;
65    Some(format!(
66        "{head}\n\n{}\n{tail}",
67        render_reference().trim_end()
68    ))
69}
70
71fn escape_cell(text: &str) -> String {
72    text.replace('|', "\\|")
73}