Skip to content

Commit 7eb23d9

Browse files
WillLillisclason
authored andcommitted
refactor(config)!: transition from anyhow to thiserror
1 parent db2d221 commit 7eb23d9

File tree

3 files changed

+66
-17
lines changed

3 files changed

+66
-17
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/config/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ path = "src/tree_sitter_config.rs"
2020
workspace = true
2121

2222
[dependencies]
23-
anyhow.workspace = true
2423
etcetera.workspace = true
2524
log.workspace = true
2625
serde.workspace = true
2726
serde_json.workspace = true
27+
thiserror.workspace = true

crates/config/src/tree_sitter_config.rs

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,54 @@
11
#![cfg_attr(not(any(test, doctest)), doc = include_str!("../README.md"))]
22

3-
use std::{env, fs, path::PathBuf};
3+
use std::{
4+
env, fs,
5+
path::{Path, PathBuf},
6+
};
47

5-
use anyhow::{Context, Result};
68
use etcetera::BaseStrategy as _;
79
use log::warn;
810
use serde::{Deserialize, Serialize};
911
use serde_json::Value;
12+
use thiserror::Error;
13+
14+
pub type ConfigResult<T> = Result<T, ConfigError>;
15+
16+
#[derive(Debug, Error)]
17+
pub enum ConfigError {
18+
#[error("Bad JSON config {0} -- {1}")]
19+
ConfigRead(String, serde_json::Error),
20+
#[error(transparent)]
21+
HomeDir(#[from] etcetera::HomeDirError),
22+
#[error(transparent)]
23+
IO(IoError),
24+
#[error(transparent)]
25+
Serialization(#[from] serde_json::Error),
26+
}
27+
28+
#[derive(Debug, Error)]
29+
pub struct IoError {
30+
pub error: std::io::Error,
31+
pub path: Option<String>,
32+
}
33+
34+
impl IoError {
35+
fn new(error: std::io::Error, path: Option<&Path>) -> Self {
36+
Self {
37+
error,
38+
path: path.map(|p| p.to_string_lossy().to_string()),
39+
}
40+
}
41+
}
42+
43+
impl std::fmt::Display for IoError {
44+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45+
write!(f, "{}", self.error)?;
46+
if let Some(ref path) = self.path {
47+
write!(f, " ({path})")?;
48+
}
49+
Ok(())
50+
}
51+
}
1052

1153
/// Holds the contents of tree-sitter's configuration file.
1254
///
@@ -23,7 +65,7 @@ pub struct Config {
2365
}
2466

2567
impl Config {
26-
pub fn find_config_file() -> Result<Option<PathBuf>> {
68+
pub fn find_config_file() -> ConfigResult<Option<PathBuf>> {
2769
if let Ok(path) = env::var("TREE_SITTER_DIR") {
2870
let mut path = PathBuf::from(path);
2971
path.push("config.json");
@@ -46,8 +88,12 @@ impl Config {
4688
.join("tree-sitter")
4789
.join("config.json");
4890
if legacy_apple_path.is_file() {
49-
fs::create_dir_all(xdg_path.parent().unwrap())?;
50-
fs::rename(&legacy_apple_path, &xdg_path)?;
91+
let xdg_dir = xdg_path.parent().unwrap();
92+
fs::create_dir_all(xdg_dir)
93+
.map_err(|e| ConfigError::IO(IoError::new(e, Some(xdg_dir))))?;
94+
fs::rename(&legacy_apple_path, &xdg_path).map_err(|e| {
95+
ConfigError::IO(IoError::new(e, Some(legacy_apple_path.as_path())))
96+
})?;
5197
warn!(
5298
"Your config.json file has been automatically migrated from \"{}\" to \"{}\"",
5399
legacy_apple_path.display(),
@@ -67,7 +113,7 @@ impl Config {
67113
Ok(None)
68114
}
69115

70-
fn xdg_config_file() -> Result<PathBuf> {
116+
fn xdg_config_file() -> ConfigResult<PathBuf> {
71117
let xdg_path = etcetera::choose_base_strategy()?
72118
.config_dir()
73119
.join("tree-sitter")
@@ -84,7 +130,7 @@ impl Config {
84130
/// [`etcetera::choose_base_strategy`](https://docs.rs/etcetera/*/etcetera/#basestrategy)
85131
/// - `$HOME/.tree-sitter/config.json` as a fallback from where tree-sitter _used_ to store
86132
/// its configuration
87-
pub fn load(path: Option<PathBuf>) -> Result<Self> {
133+
pub fn load(path: Option<PathBuf>) -> ConfigResult<Self> {
88134
let location = if let Some(path) = path {
89135
path
90136
} else if let Some(path) = Self::find_config_file()? {
@@ -94,9 +140,9 @@ impl Config {
94140
};
95141

96142
let content = fs::read_to_string(&location)
97-
.with_context(|| format!("Failed to read {}", location.to_string_lossy()))?;
143+
.map_err(|e| ConfigError::IO(IoError::new(e, Some(location.as_path()))))?;
98144
let config = serde_json::from_str(&content)
99-
.with_context(|| format!("Bad JSON config {}", location.to_string_lossy()))?;
145+
.map_err(|e| ConfigError::ConfigRead(location.to_string_lossy().to_string(), e))?;
100146
Ok(Self { location, config })
101147
}
102148

@@ -106,7 +152,7 @@ impl Config {
106152
/// disk.
107153
///
108154
/// (Note that this is typically only done by the `tree-sitter init-config` command.)
109-
pub fn initial() -> Result<Self> {
155+
pub fn initial() -> ConfigResult<Self> {
110156
let location = if let Ok(path) = env::var("TREE_SITTER_DIR") {
111157
let mut path = PathBuf::from(path);
112158
path.push("config.json");
@@ -119,17 +165,20 @@ impl Config {
119165
}
120166

121167
/// Saves this configuration to the file that it was originally loaded from.
122-
pub fn save(&self) -> Result<()> {
168+
pub fn save(&self) -> ConfigResult<()> {
123169
let json = serde_json::to_string_pretty(&self.config)?;
124-
fs::create_dir_all(self.location.parent().unwrap())?;
125-
fs::write(&self.location, json)?;
170+
let config_dir = self.location.parent().unwrap();
171+
fs::create_dir_all(config_dir)
172+
.map_err(|e| ConfigError::IO(IoError::new(e, Some(config_dir))))?;
173+
fs::write(&self.location, json)
174+
.map_err(|e| ConfigError::IO(IoError::new(e, Some(self.location.as_path()))))?;
126175
Ok(())
127176
}
128177

129178
/// Parses a component-specific configuration from the configuration file. The type `C` must
130179
/// be [deserializable](https://docs.rs/serde/*/serde/trait.Deserialize.html) from a JSON
131180
/// object, and must only include the fields relevant to that component.
132-
pub fn get<C>(&self) -> Result<C>
181+
pub fn get<C>(&self) -> ConfigResult<C>
133182
where
134183
C: for<'de> Deserialize<'de>,
135184
{
@@ -140,7 +189,7 @@ impl Config {
140189
/// Adds a component-specific configuration to the configuration file. The type `C` must be
141190
/// [serializable](https://docs.rs/serde/*/serde/trait.Serialize.html) into a JSON object, and
142191
/// must only include the fields relevant to that component.
143-
pub fn add<C>(&mut self, config: C) -> Result<()>
192+
pub fn add<C>(&mut self, config: C) -> ConfigResult<()>
144193
where
145194
C: Serialize,
146195
{

0 commit comments

Comments
 (0)