config.rs

 1use anyhow::Context;
 2use async_gen::{AsyncIter, r#gen};
 3use clap::Parser;
 4use rmcp::serde_json;
 5use std::{
 6    fs::FileType,
 7    path::{Path, PathBuf},
 8};
 9
10#[derive(Parser, Debug)]
11#[command(version, about)]
12pub struct CommandLineArgs {
13    #[arg(short, long, value_name = "FILE")]
14    pub config: PathBuf,
15}
16
17#[derive(serde::Deserialize, Debug)]
18pub struct Command {
19    pub command: String,
20    pub args: Vec<String>,
21}
22
23#[derive(serde::Deserialize, Debug)]
24pub struct Config {
25    pub lsp_server_command: Command,
26
27    /// An absolute path pointing to the root of the project on disk.
28    pub project_root: PathBuf,
29}
30
31impl Config {
32    pub async fn load(path: &PathBuf) -> anyhow::Result<Self> {
33        let config_content = tokio::fs::read_to_string(path).await?;
34        let config: Config = serde_json::from_str(&config_content)?;
35        Ok(config)
36    }
37
38    /// Returns a `Some` containing an absolute `` to the file if it is in the
39    /// project, otherwise returns `None`
40    pub async fn path_in_project<P>(&self, path: P) -> anyhow::Result<Option<PathBuf>>
41    where
42        P: AsRef<Path>,
43    {
44        let joined = self.project_root.join(&path);
45        match tokio::fs::try_exists(joined.as_path()).await {
46            Ok(true) => anyhow::Ok(Some(joined)),
47            Ok(false) => anyhow::Ok(None),
48            Err(err) => anyhow::bail!(
49                "Could not look for path {:?} in project {:?}, error: {}",
50                path.as_ref(),
51                self.project_root,
52                err
53            ),
54        }
55    }
56}