tiger_lib/report/
builder.rs

1//! By splitting the builder up into stages, we achieve two goals.
2//! - The order of calls is enforced, leading to more consistent code. E.g. calls to `weak()` or
3//!   `strong()` should always directly follow the opening call.
4//! - The user is forced to add at least one pointer, making it impossible to create a report
5//!   without pointers, which would lead to panics.
6
7use crate::report::{
8    Confidence, ErrorKey, ErrorLoc, LogReportMetadata, LogReportPointers, LogReportStyle,
9    PointedMessage, Severity, log,
10};
11
12// =================================================================================================
13// =============== Starting points:
14// =================================================================================================
15
16pub fn tips(key: ErrorKey) -> ReportBuilderStage1 {
17    ReportBuilderStage1::new(key, Severity::Tips)
18}
19
20pub fn untidy(key: ErrorKey) -> ReportBuilderStage1 {
21    ReportBuilderStage1::new(key, Severity::Untidy)
22}
23
24pub fn warn(key: ErrorKey) -> ReportBuilderStage1 {
25    ReportBuilderStage1::new(key, Severity::Warning)
26}
27
28pub fn err(key: ErrorKey) -> ReportBuilderStage1 {
29    ReportBuilderStage1::new(key, Severity::Error)
30}
31
32pub fn fatal(key: ErrorKey) -> ReportBuilderStage1 {
33    ReportBuilderStage1::new(key, Severity::Fatal)
34}
35
36pub fn report(key: ErrorKey, severity: Severity) -> ReportBuilderStage1 {
37    ReportBuilderStage1::new(key, severity)
38}
39
40// =================================================================================================
41// =============== Builder internals:
42// =================================================================================================
43
44#[derive(Debug, Clone, Copy)]
45#[must_use]
46pub struct ReportBuilderStage1(ErrorKey, Severity, Confidence);
47
48impl ReportBuilderStage1 {
49    /// For internal use only.
50    fn new(key: ErrorKey, severity: Severity) -> Self {
51        Self(key, severity, Confidence::Reasonable)
52    }
53
54    /// Optional step. Confidence defaults to Reasonable but this overrides it to Weak.
55    pub fn weak(mut self) -> Self {
56        self.2 = Confidence::Weak;
57        self
58    }
59
60    /// Optional step. Confidence defaults to Reasonable but this overrides it to Strong.
61    pub fn strong(mut self) -> Self {
62        self.2 = Confidence::Strong;
63        self
64    }
65
66    /// Optional step for when confidence is not known at compile time.
67    pub fn conf(mut self, conf: Confidence) -> Self {
68        self.2 = conf;
69        self
70    }
71
72    /// Sets the main report message.
73    pub fn msg<S: Into<String>>(self, msg: S) -> ReportBuilderStage2 {
74        ReportBuilderStage2 { stage1: self, msg: msg.into(), info: None, wiki: None }
75    }
76}
77
78#[derive(Debug)]
79#[must_use]
80pub struct ReportBuilderStage2 {
81    stage1: ReportBuilderStage1,
82    msg: String,
83    info: Option<String>,
84    wiki: Option<String>,
85}
86
87impl ReportBuilderStage2 {
88    /// Optional step. Adds an info section to the report.
89    pub fn info<S: Into<String>>(mut self, info: S) -> Self {
90        let info = info.into();
91        self.info = if info.is_empty() { None } else { Some(info) };
92        self
93    }
94
95    /// Optional step. Adds an info section to the report if the `info` parameter is `Some`.
96    pub fn opt_info<S: Into<String>>(mut self, info: Option<S>) -> Self {
97        self.info = info.map(Into::into);
98        self
99    }
100
101    /// Optional step. Adds a wiki section to the report.
102    #[allow(dead_code)]
103    pub fn wiki<S: Into<String>>(mut self, wiki: S) -> Self {
104        let wiki = wiki.into();
105        self.wiki = if wiki.is_empty() { None } else { Some(wiki) };
106        self
107    }
108
109    pub fn loc<E: ErrorLoc>(self, eloc: E) -> ReportBuilderFull {
110        let length = eloc.loc_length();
111        ReportBuilderFull {
112            stage1: self.stage1,
113            msg: self.msg,
114            info: self.info,
115            wiki: self.wiki,
116            pointers: vec![PointedMessage { loc: eloc.into_loc(), length, msg: None }],
117        }
118    }
119
120    pub fn loc_msg<E: ErrorLoc, S: Into<String>>(self, eloc: E, msg: S) -> ReportBuilderFull {
121        let length = eloc.loc_length();
122        ReportBuilderFull {
123            stage1: self.stage1,
124            msg: self.msg,
125            info: self.info,
126            wiki: self.wiki,
127            pointers: vec![PointedMessage { loc: eloc.into_loc(), length, msg: Some(msg.into()) }],
128        }
129    }
130
131    pub fn pointers(self, pointers: LogReportPointers) -> ReportBuilderFull {
132        ReportBuilderFull {
133            stage1: self.stage1,
134            msg: self.msg,
135            info: self.info,
136            wiki: self.wiki,
137            pointers,
138        }
139    }
140
141    pub fn abbreviated<E: ErrorLoc>(self, eloc: E) -> ReportBuilderAbbreviated {
142        ReportBuilderAbbreviated {
143            stage1: self.stage1,
144            msg: self.msg,
145            info: self.info,
146            wiki: self.wiki,
147            pointers: vec![PointedMessage { loc: eloc.into_loc(), length: 0, msg: None }],
148        }
149    }
150}
151
152#[derive(Debug)]
153#[must_use]
154pub struct ReportBuilderFull {
155    stage1: ReportBuilderStage1,
156    msg: String,
157    info: Option<String>,
158    wiki: Option<String>,
159    pointers: LogReportPointers,
160}
161
162impl ReportBuilderFull {
163    pub fn loc_msg<E: ErrorLoc, S: Into<String>>(mut self, eloc: E, msg: S) -> Self {
164        let length = eloc.loc_length();
165        self.pointers.push(PointedMessage { loc: eloc.into_loc(), length, msg: Some(msg.into()) });
166        self
167    }
168    pub fn opt_loc_msg<E: ErrorLoc, S: Into<String>>(mut self, eloc: Option<E>, msg: S) -> Self {
169        if let Some(eloc) = eloc {
170            let length = eloc.loc_length();
171            self.pointers.push(PointedMessage {
172                loc: eloc.into_loc(),
173                length,
174                msg: Some(msg.into()),
175            });
176        }
177        self
178    }
179    /// Build the report and return it.
180    pub fn build(self) -> (LogReportMetadata, LogReportPointers) {
181        (
182            LogReportMetadata {
183                key: self.stage1.0,
184                severity: self.stage1.1,
185                confidence: self.stage1.2,
186                msg: self.msg,
187                info: self.info,
188                wiki: self.wiki,
189                style: LogReportStyle::Full,
190            },
191            self.pointers,
192        )
193    }
194    /// Build the report and push it to be printed.
195    pub fn push(self) {
196        log(self.build());
197    }
198}
199
200#[derive(Debug)]
201#[must_use]
202pub struct ReportBuilderAbbreviated {
203    stage1: ReportBuilderStage1,
204    msg: String,
205    info: Option<String>,
206    wiki: Option<String>,
207    pointers: LogReportPointers,
208}
209
210impl ReportBuilderAbbreviated {
211    /// Build the report and return it.
212    pub fn build(self) -> (LogReportMetadata, LogReportPointers) {
213        (
214            LogReportMetadata {
215                key: self.stage1.0,
216                severity: self.stage1.1,
217                confidence: self.stage1.2,
218                msg: self.msg,
219                info: self.info,
220                wiki: self.wiki,
221                style: LogReportStyle::Abbreviated,
222            },
223            self.pointers,
224        )
225    }
226    /// Build the report and push it to be printed.
227    pub fn push(self) {
228        log(self.build());
229    }
230}