1mod server;
7
8use std::path::{Path, PathBuf};
9
10use serde::{Deserialize, Serialize};
11pub use server::LspServerConfig;
12
13use crate::error::{Error, Result};
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(deny_unknown_fields)]
18pub struct ServerConfig {
19 #[serde(default)]
21 pub workspace: WorkspaceConfig,
22
23 #[serde(default)]
25 pub lsp_servers: Vec<LspServerConfig>,
26}
27
28#[derive(Debug, Clone, Default, Serialize, Deserialize)]
30#[serde(deny_unknown_fields)]
31pub struct WorkspaceConfig {
32 #[serde(default)]
34 pub roots: Vec<PathBuf>,
35
36 #[serde(default = "default_position_encodings")]
39 pub position_encodings: Vec<String>,
40}
41
42fn default_position_encodings() -> Vec<String> {
43 vec!["utf-8".to_string(), "utf-16".to_string()]
44}
45
46impl ServerConfig {
47 pub fn load() -> Result<Self> {
59 if let Ok(path) = std::env::var("MCPLS_CONFIG") {
60 return Self::load_from(Path::new(&path));
61 }
62
63 let local_config = PathBuf::from("mcpls.toml");
64 if local_config.exists() {
65 return Self::load_from(&local_config);
66 }
67
68 if let Some(config_dir) = dirs::config_dir() {
69 let user_config = config_dir.join("mcpls").join("mcpls.toml");
70 if user_config.exists() {
71 return Self::load_from(&user_config);
72 }
73 }
74
75 Ok(Self::default())
77 }
78
79 pub fn load_from(path: &Path) -> Result<Self> {
85 let content = std::fs::read_to_string(path).map_err(|e| {
86 if e.kind() == std::io::ErrorKind::NotFound {
87 Error::ConfigNotFound(path.to_path_buf())
88 } else {
89 Error::Io(e)
90 }
91 })?;
92
93 let config: Self = toml::from_str(&content)?;
94 config.validate()?;
95 Ok(config)
96 }
97
98 fn validate(&self) -> Result<()> {
100 for server in &self.lsp_servers {
101 if server.language_id.is_empty() {
102 return Err(Error::InvalidConfig(
103 "language_id cannot be empty".to_string(),
104 ));
105 }
106 if server.command.is_empty() {
107 return Err(Error::InvalidConfig(format!(
108 "command cannot be empty for language '{}'",
109 server.language_id
110 )));
111 }
112 }
113 Ok(())
114 }
115}
116
117impl Default for ServerConfig {
118 fn default() -> Self {
119 Self {
120 workspace: WorkspaceConfig::default(),
121 lsp_servers: vec![LspServerConfig::rust_analyzer()],
122 }
123 }
124}