refactor(config)!: transition from anyhow to thiserror
This commit is contained in:
parent
db2d221ae9
commit
7eb23d9f3c
3 changed files with 66 additions and 17 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -2035,11 +2035,11 @@ dependencies = [
|
|||
name = "tree-sitter-config"
|
||||
version = "0.26.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"etcetera",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ path = "src/tree_sitter_config.rs"
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
etcetera.workspace = true
|
||||
log.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,12 +1,54 @@
|
|||
#![cfg_attr(not(any(test, doctest)), doc = include_str!("../README.md"))]
|
||||
|
||||
use std::{env, fs, path::PathBuf};
|
||||
use std::{
|
||||
env, fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use etcetera::BaseStrategy as _;
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type ConfigResult<T> = Result<T, ConfigError>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("Bad JSON config {0} -- {1}")]
|
||||
ConfigRead(String, serde_json::Error),
|
||||
#[error(transparent)]
|
||||
HomeDir(#[from] etcetera::HomeDirError),
|
||||
#[error(transparent)]
|
||||
IO(IoError),
|
||||
#[error(transparent)]
|
||||
Serialization(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub struct IoError {
|
||||
pub error: std::io::Error,
|
||||
pub path: Option<String>,
|
||||
}
|
||||
|
||||
impl IoError {
|
||||
fn new(error: std::io::Error, path: Option<&Path>) -> Self {
|
||||
Self {
|
||||
error,
|
||||
path: path.map(|p| p.to_string_lossy().to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IoError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.error)?;
|
||||
if let Some(ref path) = self.path {
|
||||
write!(f, " ({path})")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds the contents of tree-sitter's configuration file.
|
||||
///
|
||||
|
|
@ -23,7 +65,7 @@ pub struct Config {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
pub fn find_config_file() -> Result<Option<PathBuf>> {
|
||||
pub fn find_config_file() -> ConfigResult<Option<PathBuf>> {
|
||||
if let Ok(path) = env::var("TREE_SITTER_DIR") {
|
||||
let mut path = PathBuf::from(path);
|
||||
path.push("config.json");
|
||||
|
|
@ -46,8 +88,12 @@ impl Config {
|
|||
.join("tree-sitter")
|
||||
.join("config.json");
|
||||
if legacy_apple_path.is_file() {
|
||||
fs::create_dir_all(xdg_path.parent().unwrap())?;
|
||||
fs::rename(&legacy_apple_path, &xdg_path)?;
|
||||
let xdg_dir = xdg_path.parent().unwrap();
|
||||
fs::create_dir_all(xdg_dir)
|
||||
.map_err(|e| ConfigError::IO(IoError::new(e, Some(xdg_dir))))?;
|
||||
fs::rename(&legacy_apple_path, &xdg_path).map_err(|e| {
|
||||
ConfigError::IO(IoError::new(e, Some(legacy_apple_path.as_path())))
|
||||
})?;
|
||||
warn!(
|
||||
"Your config.json file has been automatically migrated from \"{}\" to \"{}\"",
|
||||
legacy_apple_path.display(),
|
||||
|
|
@ -67,7 +113,7 @@ impl Config {
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
fn xdg_config_file() -> Result<PathBuf> {
|
||||
fn xdg_config_file() -> ConfigResult<PathBuf> {
|
||||
let xdg_path = etcetera::choose_base_strategy()?
|
||||
.config_dir()
|
||||
.join("tree-sitter")
|
||||
|
|
@ -84,7 +130,7 @@ impl Config {
|
|||
/// [`etcetera::choose_base_strategy`](https://docs.rs/etcetera/*/etcetera/#basestrategy)
|
||||
/// - `$HOME/.tree-sitter/config.json` as a fallback from where tree-sitter _used_ to store
|
||||
/// its configuration
|
||||
pub fn load(path: Option<PathBuf>) -> Result<Self> {
|
||||
pub fn load(path: Option<PathBuf>) -> ConfigResult<Self> {
|
||||
let location = if let Some(path) = path {
|
||||
path
|
||||
} else if let Some(path) = Self::find_config_file()? {
|
||||
|
|
@ -94,9 +140,9 @@ impl Config {
|
|||
};
|
||||
|
||||
let content = fs::read_to_string(&location)
|
||||
.with_context(|| format!("Failed to read {}", location.to_string_lossy()))?;
|
||||
.map_err(|e| ConfigError::IO(IoError::new(e, Some(location.as_path()))))?;
|
||||
let config = serde_json::from_str(&content)
|
||||
.with_context(|| format!("Bad JSON config {}", location.to_string_lossy()))?;
|
||||
.map_err(|e| ConfigError::ConfigRead(location.to_string_lossy().to_string(), e))?;
|
||||
Ok(Self { location, config })
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +152,7 @@ impl Config {
|
|||
/// disk.
|
||||
///
|
||||
/// (Note that this is typically only done by the `tree-sitter init-config` command.)
|
||||
pub fn initial() -> Result<Self> {
|
||||
pub fn initial() -> ConfigResult<Self> {
|
||||
let location = if let Ok(path) = env::var("TREE_SITTER_DIR") {
|
||||
let mut path = PathBuf::from(path);
|
||||
path.push("config.json");
|
||||
|
|
@ -119,17 +165,20 @@ impl Config {
|
|||
}
|
||||
|
||||
/// Saves this configuration to the file that it was originally loaded from.
|
||||
pub fn save(&self) -> Result<()> {
|
||||
pub fn save(&self) -> ConfigResult<()> {
|
||||
let json = serde_json::to_string_pretty(&self.config)?;
|
||||
fs::create_dir_all(self.location.parent().unwrap())?;
|
||||
fs::write(&self.location, json)?;
|
||||
let config_dir = self.location.parent().unwrap();
|
||||
fs::create_dir_all(config_dir)
|
||||
.map_err(|e| ConfigError::IO(IoError::new(e, Some(config_dir))))?;
|
||||
fs::write(&self.location, json)
|
||||
.map_err(|e| ConfigError::IO(IoError::new(e, Some(self.location.as_path()))))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses a component-specific configuration from the configuration file. The type `C` must
|
||||
/// be [deserializable](https://docs.rs/serde/*/serde/trait.Deserialize.html) from a JSON
|
||||
/// object, and must only include the fields relevant to that component.
|
||||
pub fn get<C>(&self) -> Result<C>
|
||||
pub fn get<C>(&self) -> ConfigResult<C>
|
||||
where
|
||||
C: for<'de> Deserialize<'de>,
|
||||
{
|
||||
|
|
@ -140,7 +189,7 @@ impl Config {
|
|||
/// Adds a component-specific configuration to the configuration file. The type `C` must be
|
||||
/// [serializable](https://docs.rs/serde/*/serde/trait.Serialize.html) into a JSON object, and
|
||||
/// must only include the fields relevant to that component.
|
||||
pub fn add<C>(&mut self, config: C) -> Result<()>
|
||||
pub fn add<C>(&mut self, config: C) -> ConfigResult<()>
|
||||
where
|
||||
C: Serialize,
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue