1use std::fmt;
2
3pub type Result<T, E = Error> = std::result::Result<T, E>;
5
6#[derive(Debug)]
11pub enum Error {
12 Io(std::io::Error),
14 Config { file: String, line: usize, message: String },
16 Json { line: usize, column: usize, message: String },
18 Template { file: String, line: usize, column: usize, message: String },
23 Protocol(String),
25 Unavailable(String),
32 Message(String),
34}
35
36impl Error {
37 pub fn msg(message: impl Into<String>) -> Self {
38 Error::Message(message.into())
39 }
40
41 pub fn status(&self) -> u16 {
43 match self {
44 Error::Protocol(_) => 400,
45 Error::Unavailable(_) => 503,
48 _ => 500,
49 }
50 }
51
52 pub fn title(&self) -> &'static str {
54 match self {
55 Error::Io(_) => "I/O Error",
56 Error::Config { .. } => "Configuration Error",
57 Error::Json { .. } => "JSON Error",
58 Error::Template { .. } => "Template Error",
59 Error::Protocol(_) => "Protocol Error",
60 Error::Unavailable(_) => "Dependency Unavailable",
61 Error::Message(_) => "Application Error",
62 }
63 }
64
65 pub fn hint(&self) -> Option<String> {
67 match self {
68 Error::Config { file, .. } => Some(format!(
69 "Check the syntax of `{file}`. Each line should look like `KEY=value`."
70 )),
71 Error::Json { .. } => {
72 Some("Verify the payload is valid JSON — trailing commas are not allowed.".into())
73 }
74 Error::Template { file, line, .. } => Some(format!(
75 "Look at `{file}` around line {line}. Every `@if`, `@foreach` and `@section` \
76 needs its matching `@end...`."
77 )),
78 Error::Io(e) if e.kind() == std::io::ErrorKind::NotFound => {
79 Some("The file does not exist. Did you run `rustlavel new` in this directory?".into())
80 }
81 Error::Io(e) if e.kind() == std::io::ErrorKind::AddrInUse => {
82 Some("That port is already in use. Try `rustlavel serve --port 8001`.".into())
83 }
84 _ => None,
85 }
86 }
87}
88
89impl fmt::Display for Error {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self {
92 Error::Io(e) => write!(f, "{e}"),
93 Error::Config { file, line, message } => {
94 write!(f, "{file}:{line}: {message}")
95 }
96 Error::Json { line, column, message } => {
97 write!(f, "invalid JSON at line {line}, column {column}: {message}")
98 }
99 Error::Template { file, line, column, message } => {
100 write!(f, "{file}:{line}:{column}: {message}")
101 }
102 Error::Protocol(m) => write!(f, "malformed request: {m}"),
103 Error::Unavailable(m) => f.write_str(m),
104 Error::Message(m) => f.write_str(m),
105 }
106 }
107}
108
109impl std::error::Error for Error {
110 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
111 match self {
112 Error::Io(e) => Some(e),
113 _ => None,
114 }
115 }
116}
117
118impl From<std::io::Error> for Error {
119 fn from(e: std::io::Error) -> Self {
120 Error::Io(e)
121 }
122}
123
124impl From<String> for Error {
125 fn from(s: String) -> Self {
126 Error::Message(s)
127 }
128}
129
130impl From<&str> for Error {
131 fn from(s: &str) -> Self {
132 Error::Message(s.to_string())
133 }
134}