Skip to main content

semver_analyzer_ts/worktree/
error.rs

1//! Error types for worktree operations.
2
3use semver_analyzer_core::error::ErrorTip;
4use std::path::PathBuf;
5use thiserror::Error;
6
7/// Errors that can occur during worktree management.
8#[derive(Debug, Error)]
9pub enum WorktreeError {
10    #[error("Not a git repository: {path}")]
11    NotAGitRepo { path: PathBuf },
12
13    #[error("Git ref does not exist: {git_ref}")]
14    RefNotFound { git_ref: String },
15
16    #[error("Failed to create git worktree at {path}: {reason}")]
17    WorktreeCreationFailed { path: PathBuf, reason: String },
18
19    #[error("Failed to remove git worktree at {path}: {reason}")]
20    WorktreeRemovalFailed { path: PathBuf, reason: String },
21
22    #[error("No lockfile found at ref {git_ref}. Expected one of: package-lock.json, yarn.lock, pnpm-lock.yaml")]
23    NoLockfileFound { git_ref: String },
24
25    #[error("Package install failed ({command}): {reason}")]
26    PackageInstallFailed { command: String, reason: String },
27
28    #[error("No tsconfig.json found at ref {git_ref}")]
29    NoTsconfigFound { git_ref: String },
30
31    #[error("tsconfig.json has noEmit: true, which conflicts with --declaration. Consider adding a separate tsconfig.build.json")]
32    NoEmitConflict,
33
34    #[error("tsc --declaration failed with {error_count} errors at ref {git_ref}: {reason}")]
35    TscFailed {
36        git_ref: String,
37        error_count: usize,
38        reason: String,
39    },
40
41    #[error("Dependencies not installed at ref {git_ref}. Import resolution errors in tsc output")]
42    MissingDependencies { git_ref: String },
43
44    #[error("Project references not built. Run tsc --build in the monorepo root first")]
45    ProjectReferencesNotBuilt,
46
47    #[error("Unsupported TypeScript syntax at ref {git_ref}: {reason}")]
48    UnsupportedSyntax { git_ref: String, reason: String },
49
50    #[error("Project build failed ({command}): {reason}")]
51    ProjectBuildFailed { command: String, reason: String },
52
53    #[error("Insufficient disk space: need approximately {needed_mb}MB, have {available_mb}MB")]
54    InsufficientDiskSpace { needed_mb: u64, available_mb: u64 },
55
56    #[error("Command execution failed: {0}")]
57    CommandFailed(String),
58
59    #[error(transparent)]
60    Io(#[from] std::io::Error),
61}
62
63impl ErrorTip for WorktreeError {
64    fn tip(&self) -> Option<String> {
65        Some(match self {
66            Self::NotAGitRepo { path } => format!(
67                "Verify that '{}' is a git repository (contains a .git directory).\n\
68                 If this is a subdirectory, point --repo to the repository root.",
69                path.display()
70            ),
71            Self::RefNotFound { git_ref } => format!(
72                "The ref '{}' was not found. Run 'git tag -l' or 'git branch -a' \
73                 in the repo to see available refs.\n\
74                 If using a short ref like 'v6', try the full tag name (e.g. 'v6.0.0').",
75                git_ref
76            ),
77            Self::WorktreeCreationFailed { path, .. } => format!(
78                "Failed to create a git worktree at '{}'.\n\
79                 Check that the path is writable and no stale worktree exists there.\n\
80                 Try running 'git worktree prune' in the repo to clean up stale entries.",
81                path.display()
82            ),
83            Self::NoLockfileFound { .. } => {
84                "The repo needs a package lockfile (package-lock.json, yarn.lock, \
85                 or pnpm-lock.yaml) at this ref.\n\
86                 If this ref predates lockfiles, try a later tag.\n\
87                 If the project uses a different package manager, specify --build-command."
88                    .to_string()
89            }
90            Self::PackageInstallFailed { command, .. } => format!(
91                "The package install command '{}' failed.\n\
92                 Try running it manually in the repo to see the full error.\n\
93                 Check that your Node.js version is compatible with this project.\n\
94                 Use --log-file debug.log for full output.",
95                command
96            ),
97            Self::NoTsconfigFound { .. } => {
98                "The repo needs a tsconfig.json for TypeScript declaration extraction.\n\
99                 If this is a monorepo, use --build-command to specify the project's \
100                 own build system that generates .d.ts files."
101                    .to_string()
102            }
103            Self::NoEmitConflict => {
104                "The tsconfig.json has 'noEmit: true' which conflicts with declaration \
105                 generation.\n\
106                 Options:\n\
107                 - Add a tsconfig.build.json without noEmit\n\
108                 - Use --build-command to specify a custom build that generates .d.ts files"
109                    .to_string()
110            }
111            Self::TscFailed { error_count, .. } => format!(
112                "TypeScript compilation failed with {} error(s).\n\
113                 Common causes:\n\
114                 - Missing dependencies: run 'npm ci' or 'yarn install' first\n\
115                 - Incompatible TypeScript version\n\
116                 - Project references not built: try 'tsc --build' in the monorepo root\n\
117                 Use --log-file debug.log to see full tsc output.",
118                error_count
119            ),
120            Self::MissingDependencies { .. } => {
121                "TypeScript cannot resolve imports — dependencies are not installed.\n\
122                 Ensure the package install step completed successfully.\n\
123                 If using a monorepo, dependencies may need to be hoisted or linked."
124                    .to_string()
125            }
126            Self::ProjectReferencesNotBuilt => {
127                "This monorepo uses TypeScript project references.\n\
128                 Run 'tsc --build' in the monorepo root to build all referenced \
129                 projects, then retry."
130                    .to_string()
131            }
132            Self::ProjectBuildFailed { command, .. } => format!(
133                "The build command '{}' failed.\n\
134                 Try running this command manually in the repo directory to debug.\n\
135                 Check that all prerequisites (Node.js version, native build tools) are met.\n\
136                 Use --log-file debug.log for full build output.",
137                command
138            ),
139            Self::InsufficientDiskSpace {
140                needed_mb,
141                available_mb,
142            } => format!(
143                "Need approximately {}MB of disk space but only {}MB available.\n\
144                 Free up disk space — each worktree needs room for node_modules \
145                 and build artifacts.",
146                needed_mb, available_mb
147            ),
148            Self::UnsupportedSyntax { .. } => {
149                "The TypeScript source at this ref uses syntax that cannot be parsed.\n\
150                 This may indicate a very old or experimental TypeScript version.\n\
151                 Check that the ref you specified is correct."
152                    .to_string()
153            }
154            // No actionable tip for generic command failures or IO errors
155            Self::CommandFailed(_) | Self::Io(_) | Self::WorktreeRemovalFailed { .. } => {
156                return None
157            }
158        })
159    }
160}