1use std::path::PathBuf;
4
5#[derive(Debug)]
7#[non_exhaustive]
8pub enum Error {
9 EntryNotFound(PathBuf, std::io::Error),
11 EntryIsDirectory(PathBuf),
13 UnsupportedFileType(String),
15 EntryNotInGraph(PathBuf),
17 SnapshotRead(PathBuf, std::io::Error),
19 SnapshotParse(PathBuf, serde_json::Error),
21 SnapshotWrite(PathBuf, std::io::Error),
23 MutuallyExclusiveFlags(String),
25 TargetIsEntryPoint(String),
27 EntryRequired,
29 NotAGitRepo,
31 NotSnapshotOrRef(String),
33 DiffFileNotFound(String),
35 GitError(String),
37}
38
39impl Error {
40 pub fn hint(&self) -> Option<&str> {
42 match self {
43 Self::UnsupportedFileType(_) => {
44 Some("chainsaw supports TypeScript/JavaScript and Python files")
45 }
46 Self::EntryNotInGraph(_) => Some("is it reachable from the project root?"),
47 Self::TargetIsEntryPoint(flag) => Some(if flag == "--chain" {
48 "--chain finds import chains from the entry to a dependency"
49 } else {
50 "--cut finds where to sever import chains to a dependency"
51 }),
52 Self::EntryRequired => {
53 Some("use --entry to specify the entry point to trace")
54 }
55 _ => None,
56 }
57 }
58}
59
60impl std::fmt::Display for Error {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 match self {
65 Self::EntryNotFound(path, source) => {
66 write!(f, "cannot find entry file '{}': {source}", path.display())
67 }
68 Self::EntryIsDirectory(path) => {
69 write!(f, "'{}' is a directory, not a source file", path.display())
70 }
71 Self::UnsupportedFileType(ext) => {
72 write!(f, "unsupported file type '.{ext}'")
73 }
74 Self::EntryNotInGraph(path) => {
75 write!(f, "entry file '{}' not found in graph", path.display())
76 }
77 Self::SnapshotRead(path, source) => {
78 write!(f, "cannot read snapshot '{}': {source}", path.display())
79 }
80 Self::SnapshotParse(path, source) => {
81 write!(f, "invalid snapshot '{}': {source}", path.display())
82 }
83 Self::SnapshotWrite(path, source) => {
84 write!(f, "cannot write snapshot '{}': {source}", path.display())
85 }
86 Self::MutuallyExclusiveFlags(flags) => {
87 write!(f, "{flags} cannot be used together")
88 }
89 Self::TargetIsEntryPoint(flag) => {
90 write!(f, "{flag} target is the entry point itself")
91 }
92 Self::EntryRequired => {
93 write!(f, "--entry is required when diffing against a git ref or the working tree")
94 }
95 Self::NotAGitRepo => write!(f, "not inside a git repository"),
96 Self::NotSnapshotOrRef(arg) => {
97 write!(f, "'{arg}' is not a snapshot file or a valid git ref")
98 }
99 Self::DiffFileNotFound(arg) => write!(f, "file not found: {arg}"),
100 Self::GitError(msg) => write!(f, "git: {msg}"),
101 }
102 }
103}
104
105impl std::error::Error for Error {
107 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
108 match self {
109 Self::EntryNotFound(_, e)
110 | Self::SnapshotRead(_, e)
111 | Self::SnapshotWrite(_, e) => Some(e),
112 Self::SnapshotParse(_, e) => Some(e),
113 _ => None,
114 }
115 }
116}