Skip to main content

asimov_cli/commands/proxy/
install.rs

1// This is free and unencumbered software released into the public domain.
2
3use crate::{BoxError, StandardOptions};
4use clap::ValueEnum;
5use std::path::{Path, PathBuf};
6
7#[derive(Clone, Copy, Debug, ValueEnum)]
8pub enum ProxyInstallTarget {
9    /// Cursor (https://zed.dev).
10    #[cfg(feature = "unstable")]
11    Cursor,
12
13    /// Obsidian (https://obsidian.md).
14    #[cfg(feature = "unstable")]
15    Obsidian,
16
17    /// Visual Studio Code (https://code.visualstudio.com).
18    #[cfg(feature = "unstable")]
19    VSCode,
20
21    /// Zed (https://zed.dev).
22    Zed,
23}
24
25pub async fn install(
26    mut apps: Vec<ProxyInstallTarget>,
27    flags: &StandardOptions,
28) -> Result<(), BoxError> {
29    use ProxyInstallTarget::*;
30    let home_path = dirs::home_dir().expect("HOME should be set");
31    if apps.is_empty() {
32        apps.extend_from_slice(&[
33            #[cfg(feature = "unstable")]
34            Cursor,
35            #[cfg(feature = "unstable")]
36            Obsidian,
37            #[cfg(feature = "unstable")]
38            VSCode,
39            Zed,
40        ]);
41    }
42    for app in apps {
43        install_app(app, &home_path, flags).await?;
44    }
45    Ok(())
46}
47
48pub async fn install_app(
49    app: ProxyInstallTarget,
50    home_path: &PathBuf,
51    flags: &StandardOptions,
52) -> Result<(), BoxError> {
53    use ProxyInstallTarget::*;
54    match app {
55        #[cfg(feature = "unstable")]
56        Cursor => {
57            // See: https://www.jackyoustra.com/blog/cursor-settings-location
58            todo!() // TODO
59        },
60
61        #[cfg(feature = "unstable")]
62        Obsidian => {
63            todo!() // TODO
64        },
65
66        #[cfg(feature = "unstable")]
67        VSCode => {
68            todo!() // TODO
69        },
70
71        Zed => {
72            // See: https://zed.dev/docs/reference/all-settings#language-models
73            if flags.verbose > 0 {
74                eprintln!("Configuring Zed...");
75            }
76            let path = home_path.join(".config/zed/settings.json");
77            if !path.exists() {
78                eprintln!("error: {} not found.", path.display());
79                return Ok(());
80            }
81            patch_jsonc_file_with_edikt(
82                &path,
83                &["language_models", "openai_compatible", "ASIMOV"],
84                include_str!("config/zed-provider.jsonc"),
85            )?;
86            if flags.verbose > 0 {
87                eprintln!("Configured Zed: {}", path.display());
88            }
89        },
90    };
91    Ok(())
92}
93
94fn patch_jsonc_file_with_edikt(
95    file_path: impl AsRef<Path>,
96    json_path: &[&str],
97    patch: &str,
98) -> Result<(), BoxError> {
99    use edikt_core::{Document, Step};
100    let file_path = file_path.as_ref();
101    let input = std::fs::read_to_string(&file_path).unwrap_or_else(|_| "{}".to_string());
102    let mut cst = edikt_jsonc::parse(&input)?;
103    cst.set(
104        json_path
105            .into_iter()
106            .map(ToString::to_string)
107            .map(Step::Field)
108            .collect::<Vec<Step>>()
109            .as_slice(),
110        &edikt_jsonc::parse(patch)?.to_value(),
111    )?;
112    let output = cst.to_source();
113    std::fs::write(&file_path, output)?;
114    Ok(())
115}
116
117#[cfg(false)]
118fn patch_jsonc_file_with_jsonc_parser(
119    file_path: impl AsRef<Path>,
120    json_path: &[&str],
121    _patch: &str,
122) -> Result<(), BoxError> {
123    use jsonc_parser::cst::CstRootNode;
124    let file_path = file_path.as_ref();
125    let input = std::fs::read_to_string(&file_path).unwrap_or_else(|_| "{}".to_string());
126    let cst = CstRootNode::parse(&input, &Default::default())?;
127    let mut cursor = cst.object_value_or_set();
128    for key in json_path {
129        cursor = cursor.object_value_or_set(key);
130    }
131    //cursor.replace_with(/* ...?... */); // TODO: how to parse patch into a CstInputValue?
132    let output = cst.to_string();
133    std::fs::write(&file_path, output)?;
134    Ok(())
135}