mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-09-12 13:26:51 +00:00
implement basic config
This commit is contained in:
parent
134f3fea54
commit
62ec21584b
3 changed files with 112 additions and 3 deletions
|
@ -9,10 +9,14 @@ djls-ipc = { workspace = true }
|
|||
djls-server = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
clap = { version = "4.5.23", features = ["derive", "env"] }
|
||||
dirs = "5.0.1"
|
||||
figment = { version = "0.10.19", features = ["env", "toml"] }
|
||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||
|
||||
[[bin]]
|
||||
|
|
94
crates/djlc-cli/src/config.rs
Normal file
94
crates/djlc-cli/src/config.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use clap::Args;
|
||||
use figment::providers::{Env, Format, Serialized, Toml};
|
||||
use figment::Figment;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const CONFIG_FILES: [&str; 3] = [
|
||||
"djls.toml", // highest priority
|
||||
".djls.toml",
|
||||
"pyproject.toml", // lowest priority
|
||||
];
|
||||
|
||||
#[derive(Args, Debug, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
/// Override the virtual environment path
|
||||
#[arg(long, env = "DJLS_VENV_PATH")]
|
||||
pub venv_path: Option<PathBuf>,
|
||||
|
||||
/// Django settings module
|
||||
#[arg(long, env = "DJANGO_SETTINGS_MODULE")]
|
||||
pub django_settings_module: String,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
venv_path: std::env::var("VIRTUAL_ENV").ok().map(PathBuf::from),
|
||||
django_settings_module: std::env::var("DJANGO_SETTINGS_MODULE").unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn validate(self) -> Result<Self, ConfigError> {
|
||||
if self.django_settings_module.is_empty() {
|
||||
return Err(ConfigError::MissingDjangoSettings);
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_config_up_tree(start_dir: &Path) -> Vec<PathBuf> {
|
||||
let mut configs = Vec::new();
|
||||
let mut current_dir = start_dir.to_path_buf();
|
||||
|
||||
while let Some(parent) = current_dir.parent() {
|
||||
for &config_name in CONFIG_FILES.iter() {
|
||||
let config_path = current_dir.join(config_name);
|
||||
if config_path.exists() {
|
||||
configs.push(config_path);
|
||||
}
|
||||
}
|
||||
current_dir = parent.to_path_buf();
|
||||
}
|
||||
|
||||
configs
|
||||
}
|
||||
|
||||
pub fn load_config(cli_config: &Config) -> Result<Config, ConfigError> {
|
||||
let platform_config = dirs::config_dir()
|
||||
.map(|dir| dir.join("djls/config.toml"))
|
||||
.filter(|p| p.exists());
|
||||
|
||||
let mut figment = Figment::new()
|
||||
.merge(Serialized::defaults(Config::default()))
|
||||
.merge(
|
||||
platform_config
|
||||
.map(|p| Toml::file(&p))
|
||||
.unwrap_or_else(|| Toml::file("/dev/null")),
|
||||
);
|
||||
|
||||
let current_dir = std::env::current_dir().map_err(ConfigError::CurrentDir)?;
|
||||
for path in find_config_up_tree(¤t_dir) {
|
||||
figment = figment.merge(Toml::file(&path));
|
||||
}
|
||||
|
||||
let config: Config = figment
|
||||
.merge(Env::raw().only(&["DJANGO_SETTINGS_MODULE"]))
|
||||
.merge(Env::prefixed("DJLS_").split("_"))
|
||||
.merge(Serialized::defaults(cli_config))
|
||||
.extract()?;
|
||||
|
||||
config.validate()
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("Django settings module not specified")]
|
||||
MissingDjangoSettings,
|
||||
#[error("could not determine current directory: {0}")]
|
||||
CurrentDir(std::io::Error),
|
||||
#[error("figment error: {0}")]
|
||||
Figment(#[from] figment::Error),
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
mod config;
|
||||
|
||||
use crate::config::{load_config, Config};
|
||||
use anyhow::Context;
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use djls_ipc::v1::*;
|
||||
use djls_ipc::{ProcessError, PythonProcess, TransportError};
|
||||
use djls_ipc::PythonProcess;
|
||||
use std::ffi::OsStr;
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -8,6 +11,9 @@ use std::time::Duration;
|
|||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
|
||||
#[command(flatten)]
|
||||
config: Config,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
|
@ -40,16 +46,21 @@ enum Commands {
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cli = Cli::parse();
|
||||
let config = load_config(&cli.config).context("failed to load configuration")?;
|
||||
|
||||
match cli.command {
|
||||
Commands::Serve(opts) => {
|
||||
println!("Starting LSP server...");
|
||||
println!("With config: {:?}", config);
|
||||
|
||||
let python = PythonProcess::new::<Vec<&OsStr>, &OsStr>(
|
||||
"djls.agent",
|
||||
None,
|
||||
opts.health_check_interval(),
|
||||
)?;
|
||||
|
||||
println!("LSP server started, beginning to serve...");
|
||||
|
||||
djls_server::serve(python).await?
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue