Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,41 @@ pub enum DependencyError {
},
}

/// Errors related to configuration file operations.
#[derive(Debug, Error, Diagnostic)]
pub enum ConfigError {
#[error("failed to parse config file: {}", path.display())]
#[diagnostic(code(pitchfork::config::parse_error))]
ParseError {
path: std::path::PathBuf,
#[help]
details: Option<String>,
},

#[error("failed to read config file: {}", path.display())]
#[diagnostic(code(pitchfork::config::read_error))]
ReadError {
path: std::path::PathBuf,
#[help]
details: Option<String>,
},

#[error("failed to write config file: {}", path.display())]
#[diagnostic(code(pitchfork::config::write_error))]
WriteError {
path: std::path::PathBuf,
#[help]
details: Option<String>,
},

#[error("no config file path specified")]
#[diagnostic(
code(pitchfork::config::no_path),
help("ensure a pitchfork.toml file exists in your project or specify a path")
)]
NoPath,
}

/// Find the most similar daemon name for suggestions.
pub fn find_similar_daemon<'a>(
name: &str,
Expand Down Expand Up @@ -169,4 +204,17 @@ mod tests {
let suggestion = find_similar_daemon("xyz123", daemons.iter().copied());
assert!(suggestion.is_none());
}

#[test]
fn test_config_error_display() {
let err = ConfigError::ParseError {
path: std::path::PathBuf::from("/path/to/config.toml"),
details: Some("invalid key".to_string()),
};
assert!(err.to_string().contains("failed to parse"));
assert!(err.to_string().contains("config.toml"));

let err = ConfigError::NoPath;
assert!(err.to_string().contains("no config file path"));
}
}
24 changes: 18 additions & 6 deletions src/pitchfork_toml.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::error::ConfigError;
use crate::{Result, env};
use indexmap::IndexMap;
use miette::{IntoDiagnostic, bail};
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -51,8 +51,14 @@ impl PitchforkToml {
return Ok(Self::new(path.to_path_buf()));
}
let _lock = xx::fslock::get(path, false)?;
let raw = xx::file::read_to_string(path)?;
let mut pt: Self = toml::from_str(&raw).into_diagnostic()?;
let raw = xx::file::read_to_string(path).map_err(|e| ConfigError::ReadError {
path: path.to_path_buf(),
details: Some(e.to_string()),
})?;
let mut pt: Self = toml::from_str(&raw).map_err(|e| ConfigError::ParseError {
path: path.to_path_buf(),
details: Some(e.to_string()),
})?;
pt.path = Some(path.to_path_buf());
for (_id, d) in pt.daemons.iter_mut() {
d.path = pt.path.clone();
Expand All @@ -63,11 +69,17 @@ impl PitchforkToml {
pub fn write(&self) -> Result<()> {
if let Some(path) = &self.path {
let _lock = xx::fslock::get(path, false)?;
let raw = toml::to_string(self).into_diagnostic()?;
xx::file::write(path, raw)?;
let raw = toml::to_string(self).map_err(|e| ConfigError::WriteError {
path: path.clone(),
details: Some(format!("serialization failed: {}", e)),
})?;
xx::file::write(path, &raw).map_err(|e| ConfigError::WriteError {

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The raw string is passed as &raw in line 76, but the same variable is passed without the reference operator in line 66 of state_file.rs. For consistency, use the same pattern in both locations.

Suggested change
xx::file::write(path, &raw).map_err(|e| ConfigError::WriteError {
xx::file::write(path, raw).map_err(|e| ConfigError::WriteError {

Copilot uses AI. Check for mistakes.
path: path.clone(),
details: Some(e.to_string()),
})?;
Ok(())
} else {
bail!("no path to write to");
Err(ConfigError::NoPath.into())
}
}

Expand Down
12 changes: 9 additions & 3 deletions src/state_file.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::daemon::Daemon;
use crate::error::ConfigError;
use crate::{Result, env};
use miette::IntoDiagnostic;
use once_cell::sync::Lazy;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Debug;
Expand Down Expand Up @@ -59,8 +59,14 @@ impl StateFile {

pub fn write(&self) -> Result<()> {
let _lock = xx::fslock::get(&self.path, false)?;
let raw = toml::to_string(self).into_diagnostic()?;
xx::file::write(&self.path, raw)?;
let raw = toml::to_string(self).map_err(|e| ConfigError::WriteError {
path: self.path.clone(),
details: Some(format!("serialization failed: {}", e)),
})?;
xx::file::write(&self.path, &raw).map_err(|e| ConfigError::WriteError {
path: self.path.clone(),
details: Some(e.to_string()),
})?;
Comment thread
cursor[bot] marked this conversation as resolved.
Ok(())
}
}