dynoxide/import/notice.rs
1//! What an import has to say, and how much it matters.
2//!
3//! Every message the importer raises carries a [`Concern`], decided at the
4//! point it is raised. That decision is the only thing standing between "the
5//! run printed a warning" and "the run refused to say it succeeded", and for a
6//! while it was made afresh at each of thirty call sites. Now a message is
7//! either something the run saw happen or it is not, and one function turns
8//! that into an exit code.
9
10/// How much a message matters.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Concern {
13 /// An original value was seen reaching the output. The run does not
14 /// report success while any of these stand: `dynoxide import` exits 3,
15 /// `--serve` and `--mcp` refuse to start, and `ImportSummary::exposures`
16 /// lists them.
17 ///
18 /// Only for what the run observed, never for what it could not rule out.
19 /// A caution raised on every ordinary import would make the non-zero
20 /// exit the normal outcome, and the flag that suppresses it the default.
21 Exposure,
22 /// Something that could have gone wrong, or that the run could not
23 /// check. Reported, and the exit code is unchanged.
24 Caution,
25}
26
27/// One message from an import.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct Notice {
30 pub concern: Concern,
31 pub message: String,
32}
33
34impl Notice {
35 /// A value the run saw reach the output.
36 pub fn exposure(message: impl Into<String>) -> Self {
37 Self {
38 concern: Concern::Exposure,
39 message: message.into(),
40 }
41 }
42
43 /// Something worth saying that does not change the exit code.
44 pub fn caution(message: impl Into<String>) -> Self {
45 Self {
46 concern: Concern::Caution,
47 message: message.into(),
48 }
49 }
50
51 /// The same notice, named for the table it came from.
52 pub fn for_table(mut self, table: &str) -> Self {
53 self.message = format!("table '{table}': {}", self.message);
54 self
55 }
56}
57
58/// A notice prints as its message. It does not deref to one: the concern is
59/// the point of the type, and a notice that passed for a string anywhere a
60/// string is expected would lose it without a compile error.
61impl std::fmt::Display for Notice {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.write_str(&self.message)
64 }
65}