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)]
pub struct Serve {
#[arg(short, long, default_value_t = ConnectionType::Stdio, value_enum)]
connection_type: ConnectionType,
use crate::args::Args;
use anyhow::Result;
use clap::Subcommand;
use std::process::ExitCode;
pub trait Command {
async fn execute(&self, args: &Args) -> Result<ExitCode>;
}
#[derive(Clone, Debug, ValueEnum)]
enum ConnectionType {
Stdio,
Tcp,
#[derive(Debug, Subcommand)]
pub enum DjlsCommand {
/// Start the LSP server
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;
use crate::commands::Serve;
use anyhow::Result;
use clap::{Parser, Subcommand};
use pyo3::prelude::*;
use std::env;
use std::process::ExitCode;
#[derive(Parser)]
#[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,
}
pub use cli::Cli;
#[pyfunction]
fn entrypoint(_py: Python) -> PyResult<()> {
@ -48,62 +15,28 @@ fn entrypoint(_py: Python) -> PyResult<()> {
.chain(env::args().skip(2))
.collect();
let runtime = tokio::runtime::Runtime::new().unwrap();
let local = tokio::task::LocalSet::new();
local.block_on(&runtime, async move {
tokio::select! {
// The main CLI program
result = main(args) => {
match result {
Ok(code) => {
if code != ExitCode::SUCCESS {
std::process::exit(1);
}
Ok::<(), PyErr>(())
}
Err(e) => {
eprintln!("Error: {}", e);
if let Some(source) = e.source() {
eprintln!("Caused by: {}", source);
}
std::process::exit(1);
}
}
}
// 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
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
let result = runtime.block_on(cli::run(args));
match result {
Ok(code) => {
if code != ExitCode::SUCCESS {
std::process::exit(1);
}
Ok(())
}
Err(e) => {
eprintln!("Error: {}", e);
if let Some(source) = e.source() {
eprintln!("Caused by: {}", source);
}
std::process::exit(1);
}
})?;
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]