feat: Generate binary documentation
Some checks failed
Checks / Prefligit / prefligit (push) Failing after 1s
Release Docker Image / define-variables (push) Failing after 1s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 1s
Checks / Rust / Clippy (push) Failing after 27s
Checks / Rust / Cargo Test (push) Failing after 26s

Also refactors main.rs/mod.rs to silence clippy
This commit is contained in:
Jade Ellis 2025-07-06 21:59:20 +01:00
parent d98ce2c7b9
commit 28a29c3a7b
No known key found for this signature in database
GPG key ID: 8705A2A3EBF77BD2
12 changed files with 281 additions and 209 deletions

2
Cargo.lock generated
View file

@ -6129,7 +6129,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "xtask-admin-command" name = "xtask-generate-commands"
version = "0.5.0-rc.6" version = "0.5.0-rc.6"
dependencies = [ dependencies = [
"clap-markdown", "clap-markdown",

View file

@ -768,7 +768,7 @@ inherits = "dev"
# '-Clink-arg=-Wl,-z,nodlopen', # '-Clink-arg=-Wl,-z,nodlopen',
# '-Clink-arg=-Wl,-z,nodelete', # '-Clink-arg=-Wl,-z,nodelete',
#] #]
[profile.dev.package.xtask-admin-command] [profile.dev.package.xtask-generate-commands]
inherits = "dev" inherits = "dev"
[profile.dev.package.conduwuit] [profile.dev.package.conduwuit]
inherits = "dev" inherits = "dev"

21
docs/server_reference.md Normal file
View file

@ -0,0 +1,21 @@
# Command-Line Help for `continuwuity`
This document contains the help content for the `continuwuity` command-line program.
**Command Overview:**
* [`continuwuity`↴](#continuwuity)
## `continuwuity`
a very cool Matrix chat homeserver written in Rust
**Usage:** `continuwuity [OPTIONS]`
###### **Options:**
* `-c`, `--config <CONFIG>` — Path to the config TOML file (optional)
* `-O`, `--option <OPTION>` — Override a configuration variable using TOML 'key=value' syntax
* `--read-only` — Run in a stricter read-only --maintenance mode
* `--maintenance` — Run in maintenance mode while refusing connections
* `--execute <EXECUTE>` — Execute console command automatically after startup

View file

@ -18,35 +18,35 @@ use conduwuit_core::{
name = conduwuit_core::name(), name = conduwuit_core::name(),
version = conduwuit_core::version(), version = conduwuit_core::version(),
)] )]
pub(crate) struct Args { pub struct Args {
#[arg(short, long)] #[arg(short, long)]
/// Path to the config TOML file (optional) /// Path to the config TOML file (optional)
pub(crate) config: Option<Vec<PathBuf>>, pub config: Option<Vec<PathBuf>>,
/// Override a configuration variable using TOML 'key=value' syntax /// Override a configuration variable using TOML 'key=value' syntax
#[arg(long, short('O'))] #[arg(long, short('O'))]
pub(crate) option: Vec<String>, pub option: Vec<String>,
/// Run in a stricter read-only --maintenance mode. /// Run in a stricter read-only --maintenance mode.
#[arg(long)] #[arg(long)]
pub(crate) read_only: bool, pub read_only: bool,
/// Run in maintenance mode while refusing connections. /// Run in maintenance mode while refusing connections.
#[arg(long)] #[arg(long)]
pub(crate) maintenance: bool, pub maintenance: bool,
#[cfg(feature = "console")] #[cfg(feature = "console")]
/// Activate admin command console automatically after startup. /// Activate admin command console automatically after startup.
#[arg(long, num_args(0))] #[arg(long, num_args(0))]
pub(crate) console: bool, pub console: bool,
/// Execute console command automatically after startup. /// Execute console command automatically after startup.
#[arg(long)] #[arg(long)]
pub(crate) execute: Vec<String>, pub execute: Vec<String>,
/// Set functional testing modes if available. Ex '--test=smoke' /// Set functional testing modes if available. Ex '--test=smoke'
#[arg(long, hide(true))] #[arg(long, hide(true))]
pub(crate) test: Vec<String>, pub test: Vec<String>,
/// Override the tokio worker_thread count. /// Override the tokio worker_thread count.
#[arg( #[arg(
@ -55,19 +55,19 @@ pub(crate) struct Args {
env = "TOKIO_WORKER_THREADS", env = "TOKIO_WORKER_THREADS",
default_value = available_parallelism().to_string(), default_value = available_parallelism().to_string(),
)] )]
pub(crate) worker_threads: usize, pub worker_threads: usize,
/// Override the tokio global_queue_interval. /// Override the tokio global_queue_interval.
#[arg(long, hide(true), env = "TOKIO_GLOBAL_QUEUE_INTERVAL", default_value = "192")] #[arg(long, hide(true), env = "TOKIO_GLOBAL_QUEUE_INTERVAL", default_value = "192")]
pub(crate) global_event_interval: u32, pub global_event_interval: u32,
/// Override the tokio event_interval. /// Override the tokio event_interval.
#[arg(long, hide(true), env = "TOKIO_EVENT_INTERVAL", default_value = "512")] #[arg(long, hide(true), env = "TOKIO_EVENT_INTERVAL", default_value = "512")]
pub(crate) kernel_event_interval: u32, pub kernel_event_interval: u32,
/// Override the tokio max_io_events_per_tick. /// Override the tokio max_io_events_per_tick.
#[arg(long, hide(true), env = "TOKIO_MAX_IO_EVENTS_PER_TICK", default_value = "512")] #[arg(long, hide(true), env = "TOKIO_MAX_IO_EVENTS_PER_TICK", default_value = "512")]
pub(crate) kernel_events_per_tick: usize, pub kernel_events_per_tick: usize,
/// Set the histogram bucket size, in microseconds (tokio_unstable). Default /// Set the histogram bucket size, in microseconds (tokio_unstable). Default
/// is 25 microseconds. If the values of the histogram don't approach zero /// is 25 microseconds. If the values of the histogram don't approach zero
@ -81,7 +81,7 @@ pub(crate) struct Args {
env = "CONDUWUIT_RUNTIME_HISTOGRAM_INTERVAL", env = "CONDUWUIT_RUNTIME_HISTOGRAM_INTERVAL",
default_value = "25" default_value = "25"
)] )]
pub(crate) worker_histogram_interval: u64, pub worker_histogram_interval: u64,
/// Set the histogram bucket count (tokio_unstable). Default is 20. /// Set the histogram bucket count (tokio_unstable). Default is 20.
#[arg( #[arg(
@ -91,7 +91,7 @@ pub(crate) struct Args {
env = "CONDUWUIT_RUNTIME_HISTOGRAM_BUCKETS", env = "CONDUWUIT_RUNTIME_HISTOGRAM_BUCKETS",
default_value = "20" default_value = "20"
)] )]
pub(crate) worker_histogram_buckets: usize, pub worker_histogram_buckets: usize,
/// Toggles worker affinity feature. /// Toggles worker affinity feature.
#[arg( #[arg(
@ -105,7 +105,7 @@ pub(crate) struct Args {
default_value = "true", default_value = "true",
default_missing_value = "true", default_missing_value = "true",
)] )]
pub(crate) worker_affinity: bool, pub worker_affinity: bool,
/// Toggles feature to promote memory reclamation by the operating system /// Toggles feature to promote memory reclamation by the operating system
/// when tokio worker runs out of work. /// when tokio worker runs out of work.
@ -118,7 +118,7 @@ pub(crate) struct Args {
num_args = 0..=1, num_args = 0..=1,
require_equals(false), require_equals(false),
)] )]
pub(crate) gc_on_park: Option<bool>, pub gc_on_park: Option<bool>,
/// Toggles muzzy decay for jemalloc arenas associated with a tokio /// Toggles muzzy decay for jemalloc arenas associated with a tokio
/// worker (when worker-affinity is enabled). Setting to false releases /// worker (when worker-affinity is enabled). Setting to false releases
@ -134,12 +134,12 @@ pub(crate) struct Args {
num_args = 0..=1, num_args = 0..=1,
require_equals(false), require_equals(false),
)] )]
pub(crate) gc_muzzy: Option<bool>, pub gc_muzzy: Option<bool>,
} }
/// Parse commandline arguments into structured data /// Parse commandline arguments into structured data
#[must_use] #[must_use]
pub(super) fn parse() -> Args { Args::parse() } pub(crate) fn parse() -> Args { Args::parse() }
/// Synthesize any command line options with configuration file options. /// Synthesize any command line options with configuration file options.
pub(crate) fn update(mut config: Figment, args: &Args) -> Result<Figment> { pub(crate) fn update(mut config: Figment, args: &Args) -> Result<Figment> {

View file

@ -1,120 +1,3 @@
#![type_length_limit = "49152"] //TODO: reduce me use conduwuit::Result;
pub(crate) mod clap; fn main() -> Result<()> { conduwuit::run() }
mod logging;
mod mods;
mod restart;
mod runtime;
mod sentry;
mod server;
mod signal;
use std::sync::{Arc, atomic::Ordering};
use conduwuit_core::{Error, Result, debug_info, error, rustc_flags_capture};
use server::Server;
rustc_flags_capture! {}
fn main() -> Result {
let args = clap::parse();
let runtime = runtime::new(&args)?;
let server = Server::new(&args, Some(runtime.handle()))?;
runtime.spawn(signal::signal(server.clone()));
runtime.block_on(async_main(&server))?;
runtime::shutdown(&server, runtime);
#[cfg(unix)]
if server.server.restarting.load(Ordering::Acquire) {
restart::restart();
}
debug_info!("Exit");
Ok(())
}
/// Operate the server normally in release-mode static builds. This will start,
/// run and stop the server within the asynchronous runtime.
#[cfg(any(not(conduwuit_mods), not(feature = "conduwuit_mods")))]
#[tracing::instrument(
name = "main",
parent = None,
skip_all
)]
async fn async_main(server: &Arc<Server>) -> Result<(), Error> {
extern crate conduwuit_router as router;
match router::start(&server.server).await {
| Ok(services) => server.services.lock().await.insert(services),
| Err(error) => {
error!("Critical error starting server: {error}");
return Err(error);
},
};
if let Err(error) = router::run(
server
.services
.lock()
.await
.as_ref()
.expect("services initialized"),
)
.await
{
error!("Critical error running server: {error}");
return Err(error);
}
if let Err(error) = router::stop(
server
.services
.lock()
.await
.take()
.expect("services initialized"),
)
.await
{
error!("Critical error stopping server: {error}");
return Err(error);
}
debug_info!("Exit runtime");
Ok(())
}
/// Operate the server in developer-mode dynamic builds. This will start, run,
/// and hot-reload portions of the server as-needed before returning for an
/// actual shutdown. This is not available in release-mode or static builds.
#[cfg(all(conduwuit_mods, feature = "conduwuit_mods"))]
async fn async_main(server: &Arc<Server>) -> Result<(), Error> {
let mut starts = true;
let mut reloads = true;
while reloads {
if let Err(error) = mods::open(server).await {
error!("Loading router: {error}");
return Err(error);
}
let result = mods::run(server, starts).await;
if let Ok(result) = result {
(starts, reloads) = result;
}
let force = !reloads || result.is_err();
if let Err(error) = mods::close(server, force).await {
error!("Unloading router: {error}");
return Err(error);
}
if let Err(error) = result {
error!("{error}");
return Err(error);
}
}
debug_info!("Exit runtime");
Ok(())
}

View file

@ -1,8 +1,10 @@
#![type_length_limit = "49152"] //TODO: reduce me #![type_length_limit = "49152"] //TODO: reduce me
use conduwuit_core::rustc_flags_capture; use std::sync::{Arc, atomic::Ordering};
pub(crate) mod clap; use conduwuit_core::{debug_info, error, rustc_flags_capture};
mod clap;
mod logging; mod logging;
mod mods; mod mods;
mod restart; mod restart;
@ -11,4 +13,117 @@ mod sentry;
mod server; mod server;
mod signal; mod signal;
use server::Server;
rustc_flags_capture! {} rustc_flags_capture! {}
pub use conduwuit_core::{Error, Result};
pub use crate::clap::Args;
pub fn run() -> Result<()> {
let args = clap::parse();
run_with_args(&args)
}
pub fn run_with_args(args: &Args) -> Result<()> {
let runtime = runtime::new(args)?;
let server = Server::new(args, Some(runtime.handle()))?;
runtime.spawn(signal::signal(server.clone()));
runtime.block_on(async_main(&server))?;
runtime::shutdown(&server, runtime);
#[cfg(unix)]
if server.server.restarting.load(Ordering::Acquire) {
restart::restart();
}
debug_info!("Exit");
Ok(())
}
/// Operate the server normally in release-mode static builds. This will start,
/// run and stop the server within the asynchronous runtime.
#[cfg(any(not(conduwuit_mods), not(feature = "conduwuit_mods")))]
#[tracing::instrument(
name = "main",
parent = None,
skip_all
)]
async fn async_main(server: &Arc<Server>) -> Result<(), Error> {
extern crate conduwuit_router as router;
match router::start(&server.server).await {
| Ok(services) => server.services.lock().await.insert(services),
| Err(error) => {
error!("Critical error starting server: {error}");
return Err(error);
},
};
if let Err(error) = router::run(
server
.services
.lock()
.await
.as_ref()
.expect("services initialized"),
)
.await
{
error!("Critical error running server: {error}");
return Err(error);
}
if let Err(error) = router::stop(
server
.services
.lock()
.await
.take()
.expect("services initialized"),
)
.await
{
error!("Critical error stopping server: {error}");
return Err(error);
}
debug_info!("Exit runtime");
Ok(())
}
/// Operate the server in developer-mode dynamic builds. This will start, run,
/// and hot-reload portions of the server as-needed before returning for an
/// actual shutdown. This is not available in release-mode or static builds.
#[cfg(all(conduwuit_mods, feature = "conduwuit_mods"))]
async fn async_main(server: &Arc<Server>) -> Result<(), Error> {
let mut starts = true;
let mut reloads = true;
while reloads {
if let Err(error) = mods::open(server).await {
error!("Loading router: {error}");
return Err(error);
}
let result = mods::run(server, starts).await;
if let Ok(result) = result {
(starts, reloads) = result;
}
let force = !reloads || result.is_err();
if let Err(error) = mods::close(server, force).await {
error!("Unloading router: {error}");
return Err(error);
}
if let Err(error) = result {
error!("{error}");
return Err(error);
}
}
debug_info!("Exit runtime");
Ok(())
}

View file

@ -9,7 +9,10 @@ use conduwuit_core::{
}; };
use tokio::{runtime, sync::Mutex}; use tokio::{runtime, sync::Mutex};
use crate::{clap::Args, logging::TracingFlameGuard}; use crate::{
clap::{Args, update},
logging::TracingFlameGuard,
};
/// Server runtime state; complete /// Server runtime state; complete
pub(crate) struct Server { pub(crate) struct Server {
@ -43,7 +46,7 @@ impl Server {
.map(PathBuf::as_path); .map(PathBuf::as_path);
let config = Config::load(config_paths) let config = Config::load(config_paths)
.and_then(|raw| crate::clap::update(raw, args)) .and_then(|raw| update(raw, args))
.and_then(|raw| Config::new(&raw))?; .and_then(|raw| Config::new(&raw))?;
let (tracing_reload_handle, tracing_flame_guard, capture) = let (tracing_reload_handle, tracing_flame_guard, capture) =

View file

@ -1,63 +0,0 @@
use std::{
fs::{self, File},
io::{self, Write},
path::Path,
};
use clap_builder::{Command, CommandFactory};
use conduwuit_admin::AdminCommand;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1);
let task = args.next();
match task {
| None => todo!(),
| Some(t) => match t.as_str() {
| "man" => {
let dir = Path::new("./admin-man");
gen_manpages(dir)?;
},
| "md" => {
let command = AdminCommand::command().name("admin");
let res = clap_markdown::help_markdown_command_custom(
&command,
&clap_markdown::MarkdownOptions::default().show_footer(false),
)
.replace("\n\r", "\n")
.replace("\r\n", "\n")
.replace(" \n", "\n");
let mut file = File::create(Path::new("./docs/admin_reference.md"))?;
Write::write_all(&mut file, res.trim_end().as_bytes())?;
file.write(b"\n")?;
},
| invalid => return Err(format!("Invalid task name: {invalid}").into()),
},
}
Ok(())
}
fn gen_manpages(dir: &Path) -> Result<(), io::Error> {
fn r#gen(dir: &Path, c: &Command, prefix: Option<&str>) -> Result<(), io::Error> {
fs::create_dir_all(dir)?;
let sub_name = c.get_display_name().unwrap_or_else(|| c.get_name());
let name = if let Some(prefix) = prefix {
format!("{prefix}-{sub_name}")
} else {
sub_name.to_owned()
};
let mut out = File::create(dir.join(format!("{name}.1")))?;
let clap_mangen = clap_mangen::Man::new(c.to_owned().disable_help_flag(true));
clap_mangen.render(&mut out)?;
for sub in c.get_subcommands() {
r#gen(&dir.join(sub_name), sub, Some(&name))?;
}
Ok(())
}
r#gen(dir, &AdminCommand::command().name("admin"), None)
}

View file

@ -1,5 +1,5 @@
[package] [package]
name = "xtask-admin-command" name = "xtask-generate-commands"
authors.workspace = true authors.workspace = true
categories.workspace = true categories.workspace = true
description.workspace = true description.workspace = true

View file

@ -0,0 +1,113 @@
use std::{
fs::{self, File},
io::{self, Write},
path::Path,
};
use clap_builder::{Command, CommandFactory};
use conduwuit_admin::AdminCommand;
enum CommandType {
Admin,
Server,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1);
let command_type = args.next();
let task = args.next();
match (command_type, task) {
| (None, _) => {
return Err("Missing command type (admin or server)".into());
},
| (Some(cmd_type), None) => {
return Err(format!("Missing task for {cmd_type} command").into());
},
| (Some(cmd_type), Some(task)) => {
let command_type = match cmd_type.as_str() {
| "admin" => CommandType::Admin,
| "server" => CommandType::Server,
| _ => return Err(format!("Invalid command type: {cmd_type}").into()),
};
match task.as_str() {
| "man" => match command_type {
| CommandType::Admin => {
let dir = Path::new("./admin-man");
gen_admin_manpages(dir)?;
},
| CommandType::Server => {
let dir = Path::new("./server-man");
gen_server_manpages(dir)?;
},
},
| "md" => {
match command_type {
| CommandType::Admin => {
let command = AdminCommand::command().name("admin");
let res = clap_markdown::help_markdown_command_custom(
&command,
&clap_markdown::MarkdownOptions::default().show_footer(false),
)
.replace("\n\r", "\n")
.replace("\r\n", "\n")
.replace(" \n", "\n");
let mut file = File::create(Path::new("./docs/admin_reference.md"))?;
file.write_all(res.trim_end().as_bytes())?;
file.write_all(b"\n")?;
},
| CommandType::Server => {
// Get the server command from the conduwuit crate
let command = conduwuit::Args::command();
let res = clap_markdown::help_markdown_command_custom(
&command,
&clap_markdown::MarkdownOptions::default().show_footer(false),
)
.replace("\n\r", "\n")
.replace("\r\n", "\n")
.replace(" \n", "\n");
let mut file = File::create(Path::new("./docs/server_reference.md"))?;
file.write_all(res.trim_end().as_bytes())?;
file.write_all(b"\n")?;
},
}
},
| invalid => return Err(format!("Invalid task name: {invalid}").into()),
}
},
}
Ok(())
}
fn gen_manpage_common(dir: &Path, c: &Command, prefix: Option<&str>) -> Result<(), io::Error> {
fs::create_dir_all(dir)?;
let sub_name = c.get_display_name().unwrap_or_else(|| c.get_name());
let name = if let Some(prefix) = prefix {
format!("{prefix}-{sub_name}")
} else {
sub_name.to_owned()
};
let mut out = File::create(dir.join(format!("{name}.1")))?;
let clap_mangen = clap_mangen::Man::new(c.to_owned().disable_help_flag(true));
clap_mangen.render(&mut out)?;
for sub in c.get_subcommands() {
gen_manpage_common(&dir.join(sub_name), sub, Some(&name))?;
}
Ok(())
}
fn gen_admin_manpages(dir: &Path) -> Result<(), io::Error> {
gen_manpage_common(dir, &AdminCommand::command().name("admin"), None)
}
fn gen_server_manpages(dir: &Path) -> Result<(), io::Error> {
gen_manpage_common(dir, &conduwuit::Args::command(), None)
}

View file

@ -15,7 +15,7 @@ version.workspace = true
[dependencies] [dependencies]
clap.workspace = true clap.workspace = true
# Required for working with JSON output from cargo metadata # Required for working with JSON output from cargo metadata
serde = { version = "1.0", features = ["derive"] } serde.workspace = true
serde_json = "1.0" serde_json = "1.0"
[lints] [lints]

View file

@ -1,7 +1,7 @@
use std::{env, process::Command}; use std::{env, process::Command};
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut child = Command::new("cargo").args(["run", "--package", "xtask-admin-command", "--"].into_iter().map(ToOwned::to_owned).chain(env::args().skip(2))) let mut child = Command::new("cargo").args(["run", "--package", "xtask-generate-commands", "--"].into_iter().map(ToOwned::to_owned).chain(env::args().skip(2)))
// .stdout(Stdio::piped()) // .stdout(Stdio::piped())
// .stderr(Stdio::piped()) // .stderr(Stdio::piped())
.spawn() .spawn()