1use std::fmt::{Display, Formatter};
2use std::path::PathBuf;
3
4#[derive(Debug)]
5pub enum Error {
6 Io(std::io::Error),
7 AlreadyExists(PathBuf),
8 NotFound(PathBuf),
9 NotADirectory(PathBuf),
10 UnsafePath(PathBuf),
11 Parse(String),
12}
13
14impl Display for Error {
15 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
16 match self {
17 Self::Io(error) => write!(
18 f,
19 "io error: {error}. check filesystem permissions, parent directories, and AK_STORE"
20 ),
21 Self::AlreadyExists(path) => write!(
22 f,
23 "path already exists: {}. choose a new path or overwrite intentionally",
24 path.display()
25 ),
26 Self::NotFound(path) => write!(
27 f,
28 "path not found: {}. check that the path is relative to the store root",
29 path.display()
30 ),
31 Self::NotADirectory(path) => {
32 write!(f, "path is not a directory: {}", path.display())
33 }
34 Self::UnsafePath(path) => write!(
35 f,
36 "unsafe path blocked: {}. ak paths must stay inside the store and cannot pass through symlinks",
37 path.display()
38 ),
39 Self::Parse(message) => write!(f, "invalid input: {message}"),
40 }
41 }
42}
43
44impl std::error::Error for Error {}
45
46impl From<std::io::Error> for Error {
47 fn from(value: std::io::Error) -> Self {
48 Self::Io(value)
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::Error;
55 use std::path::PathBuf;
56
57 #[test]
58 fn not_found_error_explains_path_interpretation() {
59 let error = Error::NotFound(PathBuf::from("/tmp/store/knowledge/missing.md"));
60 let rendered = error.to_string();
61
62 assert!(rendered.contains("path not found"));
63 assert!(rendered.contains("check that the path is relative to the store root"));
64 }
65
66 #[test]
67 fn already_exists_error_suggests_intentional_overwrite_or_new_path() {
68 let error = Error::AlreadyExists(PathBuf::from("/tmp/store/knowledge/existing.md"));
69 let rendered = error.to_string();
70
71 assert!(rendered.contains("path already exists"));
72 assert!(rendered.contains("choose a new path or overwrite intentionally"));
73 }
74}