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