Refactor and reorganize primary djls crate (#136)

This commit is contained in:
Josh Thomas 2025-05-03 22:09:19 -05:00 committed by GitHub
parent ec141b7d09
commit 42c19e751d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 98 additions and 98 deletions

12
crates/djls/src/args.rs Normal file
View file

@ -0,0 +1,12 @@
use clap::Parser;
#[derive(Parser, Debug, Clone)]
pub struct Args {
/// Do not print any output.
#[arg(global = true, long, short, conflicts_with = "verbose")]
pub quiet: bool,
/// Use verbose output.
#[arg(global = true, action = clap::ArgAction::Count, long, short, conflicts_with = "quiet")]
pub verbose: u8,
}

28
crates/djls/src/cli.rs Normal file
View file

@ -0,0 +1,28 @@
use crate::args::Args;
use crate::commands::{Command, DjlsCommand};
use anyhow::Result;
use clap::Parser;
use std::process::ExitCode;
/// Main CLI structure that defines the command-line interface
#[derive(Parser)]
#[command(name = "djls")]
#[command(version, about)]
pub struct Cli {
#[command(subcommand)]
pub command: DjlsCommand,
#[command(flatten)]
pub args: Args,
}
/// Parse CLI arguments and execute the chosen command
pub async fn run(args: Vec<String>) -> Result<ExitCode> {
let cli = Cli::try_parse_from(args).unwrap_or_else(|e| {
e.exit();
});
match &cli.command {
DjlsCommand::Serve(cmd) => cmd.execute(&cli.args).await,
}
}

View file

@ -1,13 +1,16 @@
use clap::{Parser, ValueEnum}; pub mod serve;
#[derive(Debug, Parser)] use crate::args::Args;
pub struct Serve { use anyhow::Result;
#[arg(short, long, default_value_t = ConnectionType::Stdio, value_enum)] use clap::Subcommand;
connection_type: ConnectionType, use std::process::ExitCode;
pub trait Command {
async fn execute(&self, args: &Args) -> Result<ExitCode>;
} }
#[derive(Clone, Debug, ValueEnum)] #[derive(Debug, Subcommand)]
enum ConnectionType { pub enum DjlsCommand {
Stdio, /// Start the LSP server
Tcp, Serve(self::serve::Serve),
} }

View file

@ -0,0 +1,24 @@
use crate::args::Args;
use crate::commands::Command;
use anyhow::Result;
use clap::{Parser, ValueEnum};
use std::process::ExitCode;
#[derive(Debug, Parser)]
pub struct Serve {
#[arg(short, long, default_value_t = ConnectionType::Stdio, value_enum)]
connection_type: ConnectionType,
}
#[derive(Clone, Debug, ValueEnum)]
enum ConnectionType {
Stdio,
Tcp,
}
impl Command for Serve {
async fn execute(&self, _args: &Args) -> Result<ExitCode> {
djls_server::serve().await?;
Ok(ExitCode::SUCCESS)
}
}

View file

@ -1,45 +1,12 @@
mod args;
mod cli;
mod commands; mod commands;
use crate::commands::Serve;
use anyhow::Result;
use clap::{Parser, Subcommand};
use pyo3::prelude::*; use pyo3::prelude::*;
use std::env; use std::env;
use std::process::ExitCode; use std::process::ExitCode;
#[derive(Parser)] pub use cli::Cli;
#[command(name = "djls")]
#[command(version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
command: Command,
#[command(flatten)]
args: Args,
}
#[derive(Debug, Subcommand)]
enum Command {
/// Start the LSP server
Serve(Serve),
}
#[derive(Parser)]
pub struct Args {
#[command(flatten)]
global: GlobalArgs,
}
#[derive(Parser, Debug, Clone)]
struct GlobalArgs {
/// Do not print any output.
#[arg(global = true, long, short, conflicts_with = "verbose")]
pub quiet: bool,
/// Use verbose output.
#[arg(global = true, action = clap::ArgAction::Count, long, short, conflicts_with = "quiet")]
pub verbose: u8,
}
#[pyfunction] #[pyfunction]
fn entrypoint(_py: Python) -> PyResult<()> { fn entrypoint(_py: Python) -> PyResult<()> {
@ -48,18 +15,19 @@ fn entrypoint(_py: Python) -> PyResult<()> {
.chain(env::args().skip(2)) .chain(env::args().skip(2))
.collect(); .collect();
let runtime = tokio::runtime::Runtime::new().unwrap(); let runtime = tokio::runtime::Builder::new_multi_thread()
let local = tokio::task::LocalSet::new(); .enable_all()
local.block_on(&runtime, async move { .build()
tokio::select! { .unwrap();
// The main CLI program
result = main(args) => { let result = runtime.block_on(cli::run(args));
match result { match result {
Ok(code) => { Ok(code) => {
if code != ExitCode::SUCCESS { if code != ExitCode::SUCCESS {
std::process::exit(1); std::process::exit(1);
} }
Ok::<(), PyErr>(()) Ok(())
} }
Err(e) => { Err(e) => {
eprintln!("Error: {}", e); eprintln!("Error: {}", e);
@ -70,41 +38,6 @@ fn entrypoint(_py: Python) -> PyResult<()> {
} }
} }
} }
// Ctrl+C handling
_ = tokio::signal::ctrl_c() => {
println!("\nReceived Ctrl+C, shutting down...");
// Cleanup code here if needed
std::process::exit(130); // Standard Ctrl+C exit code
}
// SIGTERM handling (Unix only)
_ = async {
#[cfg(unix)]
{
use tokio::signal::unix::{signal, SignalKind};
let mut term = signal(SignalKind::terminate()).unwrap();
term.recv().await;
}
} => {
println!("\nReceived termination signal, shutting down...");
std::process::exit(143); // Standard SIGTERM exit code
}
}
})?;
Ok(())
}
async fn main(args: Vec<String>) -> Result<ExitCode> {
let cli = Cli::try_parse_from(args).unwrap_or_else(|e| {
e.exit();
});
match cli.command {
Command::Serve(_serve) => djls_server::serve().await?,
}
Ok(ExitCode::SUCCESS)
}
#[pymodule] #[pymodule]
fn djls(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { fn djls(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {