Add docs server
This commit is contained in:
+55
-18
@@ -23,28 +23,26 @@
|
||||
//! PAYPHONE_LOGGING="info"
|
||||
//! PAYPHONE_DATABASE_ADDR="your_database_addr"
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! # Parsing Arguments
|
||||
//!
|
||||
//!
|
||||
//! The arguments are parsed using the `Args::parse()` method, which returns an instance of the
|
||||
//! [`Args`] struct. This struct contains the parsed values of the command line arguments, and
|
||||
//! can be used to initialize the bot and start the event loop.
|
||||
//!
|
||||
//!
|
||||
//! ```
|
||||
//! // Read the environment variables from the `.env` file, if it exists, and load them as proper
|
||||
//! // env variables.
|
||||
//! dotenvy::dotenv().ok();
|
||||
//!
|
||||
//!
|
||||
//! let args = Args::parse();
|
||||
//! ```
|
||||
|
||||
/// Payphone's argument parser.
|
||||
#[derive(clap::Parser)]
|
||||
#[command(name = "Payphone CLI")]
|
||||
#[command(version, about = "Payphone's command line interface.")]
|
||||
pub struct Args {
|
||||
/// The Discord bot token to run the bot with.
|
||||
#[arg(env = "PAYPHONE_TOKEN")]
|
||||
pub token: String,
|
||||
|
||||
/// The minimum level logs have to be to be outputted into the console, or off for no logs.
|
||||
#[arg(
|
||||
long,
|
||||
@@ -57,28 +55,33 @@ pub struct Args {
|
||||
)]
|
||||
pub logging: LevelFilter,
|
||||
|
||||
/// The socket address of the database to connect to, a Scylla node.
|
||||
#[arg(
|
||||
long,
|
||||
short,
|
||||
env = "PAYPHONE_DATABASE_ADDR",
|
||||
value_name = "ADDR",
|
||||
default_value_t = String::from("127.0.0.1:9042"))
|
||||
]
|
||||
pub database_addr: String,
|
||||
#[command(subcommand)]
|
||||
pub command: Option<Commands>,
|
||||
}
|
||||
|
||||
/// An enum with the different logging levels that can be used in the CLI.
|
||||
///
|
||||
///
|
||||
/// This enum is used to parse the `--logging` or `-l` command line argument and then map the value
|
||||
/// to [`log::LevelFilter`].
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||
pub enum LevelFilter {
|
||||
/// No logs at all.
|
||||
Off,
|
||||
|
||||
/// The most verbose level, used for debugging purposes.
|
||||
Trace,
|
||||
|
||||
/// Events that are useful to be displayed during development, but not in production.
|
||||
Debug,
|
||||
|
||||
/// Used for general information about the bot's state and milestones.
|
||||
Info,
|
||||
|
||||
/// Used for warnings, when something unexpected happens but the bot can still continue
|
||||
/// running.
|
||||
Warn,
|
||||
|
||||
/// Used for errors, when something goes wrong and the bot cannot continue running.
|
||||
Error,
|
||||
}
|
||||
|
||||
@@ -95,3 +98,37 @@ impl Into<log::LevelFilter> for LevelFilter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Subcommand, Clone)]
|
||||
pub enum Commands {
|
||||
/// Starts the bot with the provided token.
|
||||
Start {
|
||||
/// The Discord bot token to run the bot with.
|
||||
#[arg(env = "PAYPHONE_TOKEN")]
|
||||
token: String,
|
||||
|
||||
/// The socket address of the database to connect to, a Scylla node.
|
||||
#[arg(
|
||||
long,
|
||||
short,
|
||||
env = "PAYPHONE_DATABASE_ADDR",
|
||||
value_name = "ADDR",
|
||||
default_value_t = String::from("127.0.0.1:9042"))
|
||||
]
|
||||
database_addr: String,
|
||||
},
|
||||
|
||||
/// Starts a documentation server for the project which serves `cargo doc` and refreshes on
|
||||
/// changes.
|
||||
Docs {
|
||||
/// The port to run the documentation server on.
|
||||
#[arg(
|
||||
long,
|
||||
short,
|
||||
env = "PAYPHONE_DOCS_PORT",
|
||||
value_name = "PORT",
|
||||
default_value_t = 3000
|
||||
)]
|
||||
port: u16,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
//! A framework for building Discord bots, built with Twilight.
|
||||
//!
|
||||
//!
|
||||
|
||||
pub mod bot;
|
||||
pub mod commands;
|
||||
pub mod events;
|
||||
|
||||
+87
-3
@@ -75,11 +75,17 @@
|
||||
//!
|
||||
//! More documentation about each group can be found in the respective submodule's documentation.
|
||||
|
||||
use std::{net::SocketAddr, path::Path, process::Command, sync::mpsc};
|
||||
|
||||
use axum::{Router, response::Redirect, routing};
|
||||
use clap::Parser;
|
||||
use notify::{Event, EventKind, Watcher};
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::services::ServeDir;
|
||||
use twilight_gateway::Intents;
|
||||
|
||||
use crate::{
|
||||
cli::Args,
|
||||
cli::{Args, Commands},
|
||||
framework::bot::Bot,
|
||||
groups::{
|
||||
about::{commands::HelpCommand, components::HelpGoBackButton},
|
||||
@@ -111,13 +117,29 @@ pub async fn main() {
|
||||
|
||||
logging::init(args.logging.into());
|
||||
|
||||
if let Some(command) = args.command {
|
||||
match command {
|
||||
Commands::Start {
|
||||
token,
|
||||
database_addr,
|
||||
} => {
|
||||
start(token, database_addr).await;
|
||||
}
|
||||
Commands::Docs { port } => {
|
||||
docs(port).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn start(token: String, database_addr: String) {
|
||||
log::info!("Welcome to Payphone, Nyeki!");
|
||||
log::info!(
|
||||
"Payphone is running on version v{}",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
);
|
||||
|
||||
let db = database::init(args.database_addr)
|
||||
let db = database::init(database_addr)
|
||||
.await
|
||||
.expect("Could not connect to the database!");
|
||||
|
||||
@@ -140,5 +162,67 @@ pub async fn main() {
|
||||
.on_server_update(ServerUpdateHandler)
|
||||
.on_server_delete(ServerDeleteHandler);
|
||||
|
||||
bot.run(&args.token).await.expect("Could not run bot!");
|
||||
bot.run(&token).await.expect("Could not run bot!");
|
||||
}
|
||||
|
||||
async fn docs(port: u16) {
|
||||
log::info!("Compiling documentation...");
|
||||
|
||||
Command::new("cargo")
|
||||
.args(["doc", "--no-deps"])
|
||||
.output()
|
||||
.expect("Failed to run `cargo doc`");
|
||||
|
||||
tokio::spawn(async {
|
||||
let (tx, rx) = mpsc::channel::<notify::Result<Event>>();
|
||||
|
||||
let mut watcher = notify::recommended_watcher(tx).expect("Failed to create watcher");
|
||||
|
||||
watcher
|
||||
.watch(Path::new("src/"), notify::RecursiveMode::Recursive)
|
||||
.expect("Failed to watch src directory");
|
||||
|
||||
for res in rx {
|
||||
match res {
|
||||
Ok(event) => {
|
||||
match event.kind {
|
||||
EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) => {
|
||||
log::info!("Source files changed, recompiling...");
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
Command::new("cargo")
|
||||
.args(["doc", "--no-deps"])
|
||||
.output()
|
||||
.expect("Failed to run `cargo doc`");
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Watch error: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
log::info!("Starting documentation server on port {port}...");
|
||||
|
||||
let docs: Router<()> = Router::new()
|
||||
.route(
|
||||
"/",
|
||||
routing::get(|| async { Redirect::permanent("/payphone/") }),
|
||||
)
|
||||
.fallback_service(ServeDir::new(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/target/doc/"
|
||||
)));
|
||||
|
||||
let listener = TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], port)))
|
||||
.await
|
||||
.expect("Could not bind to port!");
|
||||
|
||||
log::info!("Documentation server is running on http://localhost:{port}/");
|
||||
|
||||
axum::serve(listener, docs)
|
||||
.await
|
||||
.expect("Could not start documentation server!");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user