Skip to main content

setu_cli/
checker.rs

1use core::fmt;
2use std::path::PathBuf;
3
4use colored::Colorize;
5
6use crate::checker;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum LocalLinkStatus {
10    Valid,
11    DoesNotExist,
12    InvalidPrefix,
13}
14
15impl fmt::Display for LocalLinkStatus {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            LocalLinkStatus::Valid => write!(f, "The local link is valid"),
19            LocalLinkStatus::DoesNotExist => write!(f, "The local link does not exist"),
20            LocalLinkStatus::InvalidPrefix => {
21                write!(f, "The local link's path is malformatted")
22            }
23        }
24    }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum RemoteLinkStatus {
29    Reachable,       // Could this be changed?
30    Concern(u16),    // Status code - 404, 501 etc.
31    Invalid(String), // Err message - not to be confused with 404 which is a Concern
32}
33
34impl fmt::Display for RemoteLinkStatus {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            Self::Concern(code) => {
38                write!(f, "The URL returned an unsuccessful status code: {}", code)
39            }
40            Self::Invalid(err) => {
41                write!(f, "The URL could not be reached: {}", err)
42            }
43            Self::Reachable => write!(f, "The URL returns 200 OK"),
44        }
45    }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum LinkStatus {
50    Local(LocalLinkStatus),
51    Remote(RemoteLinkStatus),
52    Invalid(String),
53}
54
55impl LinkStatus {
56    pub fn is_broken(&self) -> bool {
57        match self {
58            LinkStatus::Local(local_link_status) => match local_link_status {
59                LocalLinkStatus::Valid => false,
60                _ => true,
61            },
62            LinkStatus::Remote(remote_link_status) => match remote_link_status {
63                RemoteLinkStatus::Reachable => false,
64                RemoteLinkStatus::Concern(_) => true, // concerns are specified by user input so must be deemed broken if they are found
65                _ => true,
66            },
67            _ => true,
68        }
69    }
70}
71
72impl fmt::Display for LinkStatus {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        match self {
75            LinkStatus::Local(status) => fmt::Display::fmt(status, f),
76            LinkStatus::Remote(status) => fmt::Display::fmt(status, f),
77            LinkStatus::Invalid(err) => fmt::Display::fmt(err, f),
78        }
79    }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct LinkCheckResult {
84    pub source_file: PathBuf,
85    pub raw_link: String,
86    pub status: LinkStatus,
87}
88
89pub struct MarkdownCheckResultFormatter<'a> {
90    result: &'a MarkdownCheckResult,
91    show_valid: bool,
92}
93
94impl MarkdownCheckResult {
95    pub fn display_with_config(&self, show_valid: bool) -> MarkdownCheckResultFormatter<'_> {
96        MarkdownCheckResultFormatter {
97            result: self,
98            show_valid,
99        }
100    }
101}
102
103#[derive(Debug)]
104pub struct MarkdownCheckResult {
105    pub success: bool,
106    pub checks: Vec<LinkCheckResult>,
107}
108
109impl<'a> fmt::Display for MarkdownCheckResultFormatter<'a> {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        if self.result.success {
112            for check in &self.result.checks {
113                match &check.status {
114                    checker::LinkStatus::Local(LocalLinkStatus::Valid) if self.show_valid => {
115                        write!(
116                            f,
117                            "{} Valid local link {} at {}\n",
118                            "OK".green().bold(),
119                            check.raw_link.cyan(),
120                            check.source_file.to_string_lossy().green(),
121                        )?;
122                    }
123                    checker::LinkStatus::Local(status) if *status != LocalLinkStatus::Valid => {
124                        write!(
125                            f,
126                            "{} Faulty local link {} at {} | {}\n",
127                            "FAIL".red().bold(),
128                            check.raw_link.cyan(),
129                            check.source_file.to_string_lossy().green(),
130                            check.status
131                        )?;
132                    }
133                    checker::LinkStatus::Remote(RemoteLinkStatus::Concern(_))
134                    | checker::LinkStatus::Remote(RemoteLinkStatus::Invalid(_)) => {
135                        write!(
136                            f,
137                            "{} Faulty remote URL {} at {} | {}\n",
138                            "FAIL".red().bold(),
139                            check.raw_link.cyan(),
140                            check.source_file.to_string_lossy().green(),
141                            check.status
142                        )?;
143                    }
144                    checker::LinkStatus::Invalid(err) => {
145                        write!(
146                            f,
147                            "{} Invalid URL {} at {} | {}\n",
148                            "FAIL".red().bold(),
149                            check.raw_link.cyan(),
150                            check.source_file.to_string_lossy().green(),
151                            err
152                        )?;
153                    }
154                    _ => {}
155                }
156            }
157        } else {
158            write!(
159                f,
160                "{} Failed to locate/parse markdown file",
161                "FAIL".red().bold()
162            )?;
163        }
164        Ok(())
165    }
166}
167
168pub mod invalid;
169pub mod local;
170pub mod remote;