1use miette::Diagnostic;
4use std::path::PathBuf;
5use thiserror::Error;
6
7#[derive(Debug, Error, Diagnostic)]
9pub enum FlakeLockError {
10 #[error("Failed to parse flake.lock: {0}")]
12 #[diagnostic(
13 code(cuenv::ci::flake::parse),
14 help("Ensure flake.lock is valid JSON and follows Nix flake.lock schema v7")
15 )]
16 ParseError(String),
17
18 #[error("Failed to read flake.lock at {path}: {message}")]
20 #[diagnostic(
21 code(cuenv::ci::flake::io),
22 help("Check that flake.lock exists and is readable")
23 )]
24 IoError {
25 path: PathBuf,
27 message: String,
29 },
30
31 #[error("Flake purity check failed with {count} unlocked input(s): {}", inputs.join(", "))]
33 #[diagnostic(
34 code(cuenv::ci::flake::impure_strict),
35 help(
36 "In strict mode, all flake inputs must be locked. Run 'nix flake lock' to fix, or use purity_mode: warning/override"
37 )
38 )]
39 StrictModeViolation {
40 count: usize,
42 inputs: Vec<String>,
44 },
45
46 #[error("No flake.lock file found at {path}")]
48 #[diagnostic(
49 code(cuenv::ci::flake::missing),
50 help("Run 'nix flake lock' to generate a flake.lock file")
51 )]
52 MissingLockFile {
53 path: PathBuf,
55 },
56}
57
58impl FlakeLockError {
59 #[must_use]
61 pub fn parse(message: impl Into<String>) -> Self {
62 Self::ParseError(message.into())
63 }
64
65 #[must_use]
67 pub fn io(path: impl Into<PathBuf>, message: impl Into<String>) -> Self {
68 Self::IoError {
69 path: path.into(),
70 message: message.into(),
71 }
72 }
73
74 #[must_use]
76 pub fn strict_violation(inputs: Vec<String>) -> Self {
77 Self::StrictModeViolation {
78 count: inputs.len(),
79 inputs,
80 }
81 }
82
83 #[must_use]
85 pub fn missing(path: impl Into<PathBuf>) -> Self {
86 Self::MissingLockFile { path: path.into() }
87 }
88}