1use std::process::ExitCode;
19
20use bynk_syntax::diagnostics::{self, Explain};
21
22enum Lookup {
24 Explained(&'static Explain),
26 KnownUnexplained(&'static str),
29 Unknown,
31}
32
33fn lookup(code: &str) -> Lookup {
34 if let Some(e) = diagnostics::explain(code) {
35 Lookup::Explained(e)
36 } else if let Some(info) = diagnostics::REGISTRY.iter().find(|d| d.code == code) {
37 Lookup::KnownUnexplained(info.summary)
38 } else {
39 Lookup::Unknown
40 }
41}
42
43fn render_explained(e: &Explain) -> String {
45 let mut out = String::new();
46 out.push_str(e.code);
47 out.push_str("\n\n");
48 out.push_str(e.blurb);
49 out.push_str("\n\nExample:\n\n");
50 for line in e.example.lines() {
51 out.push_str(" ");
52 out.push_str(line);
53 out.push('\n');
54 }
55 out.push_str("\nLearn more: ");
56 out.push_str(&e.href());
57 out.push('\n');
58 out
59}
60
61fn render_known_unexplained(code: &str, summary: &str) -> String {
63 format!(
64 "{code}\n\n{summary}\n\n\
65 No extended explanation is written for this code yet. See the diagnostic \
66 index for every code and its summary:\n\n {}/book/reference/diagnostics/\n",
67 diagnostics::BOOK_BASE_URL
68 )
69}
70
71pub fn run(code: &str) -> ExitCode {
74 match lookup(code) {
75 Lookup::Explained(e) => {
76 print!("{}", render_explained(e));
77 ExitCode::SUCCESS
78 }
79 Lookup::KnownUnexplained(summary) => {
80 print!("{}", render_known_unexplained(code, summary));
81 ExitCode::SUCCESS
82 }
83 Lookup::Unknown => {
84 eprintln!(
85 "error: `{code}` is not a diagnostic code the compiler emits.\n\
86 Diagnostic codes look like `bynk.resolve.unknown_type`; see \
87 {}/book/reference/diagnostics/ for the full list.",
88 diagnostics::BOOK_BASE_URL
89 );
90 ExitCode::FAILURE
91 }
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn explained_code_prints_blurb_example_and_href() {
101 let e = diagnostics::explain("bynk.resolve.unknown_type").expect("curated");
102 let out = render_explained(e);
103 assert!(out.contains("bynk.resolve.unknown_type"));
104 assert!(out.contains(e.blurb));
105 assert!(out.contains("Example:"));
106 assert!(out.contains("https://bynk-lang.org/book/reference/types/"));
108 }
109
110 #[test]
111 fn known_but_unexplained_code_is_graceful() {
112 let code = "bynk.resolve.duplicate_type";
115 assert!(diagnostics::explain(code).is_none());
116 assert!(matches!(lookup(code), Lookup::KnownUnexplained(_)));
117 let out = render_known_unexplained(code, "Two types share a name.");
118 assert!(out.contains(code));
119 assert!(out.contains("No extended explanation"));
120 }
121
122 #[test]
123 fn unknown_code_is_not_found() {
124 assert!(matches!(lookup("bynk.not.a_real_code"), Lookup::Unknown));
125 assert!(matches!(lookup("nonsense"), Lookup::Unknown));
126 }
127}