Skip to content
Extraits de code Groupes Projets
Vérifiée Valider 69fdd2f4 rédigé par Kubat's avatar Kubat
Parcourir les fichiers

LEKTORD: Begin to add options to lektord (view/edit config, launch daemon...)

parent 7e3f2910
Aucune branche associée trouvée
Aucune étiquette associée trouvée
1 requête de fusion!197Draft: Refactor the whole code.
......@@ -1992,6 +1992,7 @@ dependencies = [
"anyhow",
"async-trait",
"axum",
"clap",
"futures",
"hyper",
"lektor_mpris",
......
......@@ -35,6 +35,8 @@ pub struct LektorPlayerConfig {
pub font_size: u64,
pub font_name: String,
pub msg_duration: u64,
#[cfg(unix)]
pub force_x11: bool,
}
......
......@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
pub fn get_or_write_default_config<Config: Default + Serialize + for<'de> Deserialize<'de>>(
appli: &'static str,
) -> Result<Config> {
let path = crate::user_config_directory("lektor").join(format!("{appli}.toml"));
let path = crate::user_config_directory_sync("lektor").join(format!("{appli}.toml"));
match std::fs::read_to_string(&path) {
Ok(config) => toml::from_str::<Config>(&config)
.with_context(|| format!("invalid config file `{}`", path.to_string_lossy())),
......@@ -47,3 +47,19 @@ pub async fn write_config_async<Config: Default + Serialize + for<'de> Deseriali
)
})
}
/// Write the config to its file
pub fn write_config_sync<Config: Default + Serialize + for<'de> Deserialize<'de>>(
appli: &'static str,
config: Config,
) -> Result<()> {
let path = crate::user_config_directory_sync("lektor").join(format!("{appli}.toml"));
let pretty_config =
toml::to_string_pretty(&config).expect("failed to prettify the default config...");
std::fs::write(&path, pretty_config).with_context(|| {
format!(
"failed to write default config to file `{}`",
path.to_string_lossy()
)
})
}
......@@ -40,7 +40,7 @@ pub fn user_home_directory() -> std::path::PathBuf {
/// USERPROFILE are defined this function will panic. The config directory is
/// named `.config` and is placed in the home folder. This is the most common
/// behaviour on unix systems.
pub fn user_config_directory(app: impl AsRef<str>) -> std::path::PathBuf {
pub fn user_config_directory_sync(app: impl AsRef<str>) -> std::path::PathBuf {
let folder = user_home_directory().join(".config").join(app.as_ref());
std::fs::create_dir_all(&folder).unwrap_or_else(|err| {
panic!(
......@@ -51,7 +51,7 @@ pub fn user_config_directory(app: impl AsRef<str>) -> std::path::PathBuf {
folder
}
/// Same as [user_config_directory] but with async using tokio.
/// Same as [user_config_directory_sync] but with async using tokio.
pub async fn user_config_directory_async(app: impl AsRef<str>) -> std::path::PathBuf {
let folder = user_home_directory().join(".config").join(app.as_ref());
tokio::fs::create_dir_all(&folder)
......
......@@ -20,6 +20,8 @@ tokio.workspace = true
hyper.workspace = true
async-trait.workspace = true
clap.workspace = true
lektor_nkdb = { path = "../lektor_nkdb" }
lektor_repo = { path = "../lektor_repo" }
lektor_utils = { path = "../lektor_utils" }
......
......@@ -162,7 +162,6 @@ pub(crate) type LektorStatePtr = Arc<LektorState>;
impl LektorState {
/// Create a new server state from the configuration file.
pub async fn new(config: LektorConfig, shutdown: Sender<()>) -> Result<LektorStatePtr> {
lektor_utils::logger::level(config.log);
let LektorConfig {
database: LektorDatabaseConfig { folder, .. },
player,
......
use clap::{Parser, Subcommand};
use lektor_utils::*;
#[derive(Parser, Debug, Default)]
#[command( author
, version = version()
, about
, long_about = None
, disable_help_subcommand = false
, args_conflicts_with_subcommands = true
)]
pub struct Args {
#[command(subcommand)]
pub action: Option<SubCommand>,
/// Make lkt more verbose, repeat to make it even more verbose.
#[arg( long
, short = 'v'
, action = clap::ArgAction::Count
)]
pub verbose: u8,
}
#[derive(Subcommand, Debug, Default)]
#[command(long_about = None, about = None)]
pub enum SubCommand {
/// Launch the daemon, this is the default command, if nothing is passed then the daemon will
/// be unleashed ;)
#[command(short_flag = 'S')]
#[default]
Start,
/// Config manipulation commands, to not edit it by hand!
#[command(short_flag = 'C', arg_required_else_help = true)]
Config {
/// Show the current config.
#[arg( action = clap::ArgAction::SetTrue
, exclusive = true
, short = 's'
, long = "show"
)]
show: bool,
/// Edit the config.
#[arg( action = clap::ArgAction::Set
, exclusive = true
, num_args = 2
, short = 'e'
, long = "edit"
, value_name = "<OPT.NAME> <VALUE>"
)]
edit: Option<Vec<String>>,
},
}
use anyhow::{bail, Context, Result};
use lektor_utils::{config::*, log::Level as LogLevel};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::{net::SocketAddr, path::PathBuf};
/// Lektord configuration.
#[derive(Debug, Serialize, Deserialize, Clone)]
......@@ -41,3 +42,111 @@ impl Default for LektorConfig {
}
}
}
impl LektorConfig {
pub fn edit(mut self, name: impl AsRef<str>, value: impl AsRef<str>) -> Result<()> {
match &name.as_ref().split('.').collect::<Vec<_>>()[..] {
// Flat
["log"] => todo!(),
["workers"] => {
self.workers = value
.as_ref()
.parse::<usize>()
.with_context(|| "invalid value for 'workers'")?
}
["mpris"] => {
self.mpris = value
.as_ref()
.parse::<bool>()
.with_context(|| "invalid value for 'mpris'")?
}
// Database
["database", "folder"] => self.database.folder = PathBuf::from(value.as_ref()),
["database", "autoclear"] => {
self.database.autoclear = value
.as_ref()
.parse::<bool>()
.with_context(|| "invalid value for 'database.autoclear'")?
}
["database", "save_history"] => {
self.database.save_history = value
.as_ref()
.parse::<bool>()
.with_context(|| "invalid value for 'database.save_history'")?
}
// Player
["player", "font_name"] => self.player.font_name = value.as_ref().to_string(),
["player", "font_size"] => {
self.player.font_size = value
.as_ref()
.parse::<u64>()
.with_context(|| "invalid value for 'player.font_size'")?
}
["player", "msg_duration"] => {
self.player.msg_duration = value
.as_ref()
.parse::<u64>()
.with_context(|| "invalid value for 'player.msg_duration'")?
}
#[cfg(unix)]
["player", "force_x11"] => {
self.player.force_x11 = value
.as_ref()
.parse::<bool>()
.with_context(|| "invalid value for 'player.force_x11'")?
}
//
_ => bail!("invalid option name {}", name.as_ref()),
}
write_config_sync("lektord", self)
}
}
impl std::fmt::Display for LektorConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "log: {}", self.log)?;
writeln!(f, "mpris: {}", self.mpris)?;
writeln!(f, "workers: {}", self.workers)?;
writeln!(f, "\n[users]")?;
for UserConfig { user, token, admin } in &self.users {
let title = match admin {
true => "admin:",
false => "user: ",
};
writeln!(f, "{title} {user} -> {token}")?;
}
writeln!(f, "\n[listen]")?;
for (idx, addr) in self.listen.iter().enumerate() {
writeln!(f, "{idx}: {addr}")?;
}
writeln!(f, "\n[database]")?;
let folder = self.database.folder.to_string_lossy();
writeln!(f, "folder: {}", folder)?;
writeln!(f, "autoclear: {}", self.database.autoclear)?;
writeln!(f, "save_history: {}", self.database.save_history)?;
writeln!(f, "\n[player]")?;
writeln!(f, "font_name: {}", self.player.font_name)?;
writeln!(f, "font_size: {}", self.player.font_size)?;
writeln!(f, "msg_duration: {}", self.player.msg_duration)?;
#[cfg(unix)]
writeln!(f, "force_x11: {}", self.player.force_x11)?;
for repo in &self.repo {
writeln!(f, "\n[repo.{}]", repo.name)?;
writeln!(f, "api: {:?}", repo.api)?;
writeln!(f, "token: {}", repo.token)?;
for (idx, url) in repo.urls.iter().enumerate() {
writeln!(f, "url.{idx}: {url}")?;
}
}
Ok(())
}
}
mod app;
pub mod c_wrapper;
mod cmd;
mod config;
mod error;
mod listen;
......@@ -12,6 +13,7 @@ pub use error::*;
pub use listen::*;
use anyhow::{Context, Result};
use cmd::SubCommand;
use lektor_utils::*;
use std::sync::atomic::{AtomicU64, Ordering};
use tokio::{signal, sync::oneshot::Receiver};
......@@ -19,15 +21,35 @@ use tokio::{signal, sync::oneshot::Receiver};
fn main() -> Result<()> {
logger::init(Some(log::Level::Debug)).expect("failed to install logger");
let config = lektor_utils::config::get_or_write_default_config::<LektorConfig>("lektord")?;
let args = <cmd::Args as clap::Parser>::parse();
lektor_utils::logger::level(config.log);
if args.verbose != 0 {
lektor_utils::logger::level_int(args.verbose);
}
match args.action.unwrap_or_default() {
SubCommand::Config { show: true, .. } => {
println!("{config}");
Ok(())
}
SubCommand::Config {
edit: Some(args), ..
} => match &args[..] {
[name, value] => config.edit(name, value),
args => unreachable!("{args:#?}"),
},
SubCommand::Start => {
log::info!("starting the lektord daemon");
tokio::runtime::Builder::new_multi_thread()
.worker_threads(config.workers) // Thread count from the config file.
.thread_name_fn(|| {
static COUNT: AtomicU64 = AtomicU64::new(0);
format!("lektord-{}", COUNT.fetch_add(1, Ordering::SeqCst))
}) // Custom names, to see utilization... and because it's fancy!
.enable_all() // Don't care, enable everything.
})
.enable_all()
.thread_stack_size(3 * 1024 * 1024) // 3Mio for each thread, should be enaugh
.max_blocking_threads(1024)
.build()?
.block_on(async move {
lektor_utils::config::write_config_async("lektord", config.clone()).await?; // Write to apply changes...
......@@ -43,6 +65,10 @@ fn main() -> Result<()> {
})
}
args => unreachable!("{args:?}"),
}
}
/// Gracefull ctrl+c handling.
async fn shutdown_signal(shutdown: Receiver<()>) {
let shutdown = async {
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Veuillez vous inscrire ou vous pour commenter