use crate::scheduler::ScheduledJob;
use crate::state::AppState;
use async_trait::async_trait;
use axum::Router;
use std::collections::BTreeMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Lang {
Ko,
En,
}
impl Lang {
pub fn as_str(self) -> &'static str {
match self {
Lang::Ko => "ko",
Lang::En => "en",
}
}
pub fn parse(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"ko" => Some(Lang::Ko),
"en" => Some(Lang::En),
_ => None,
}
}
}
pub struct Migration {
pub version: i64,
pub name: &'static str,
pub sql: &'static str,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct LobbyCardItem {
pub title: String,
pub url: String,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct LobbyCard {
pub id: String,
pub items: Vec<LobbyCardItem>,
}
#[derive(Debug, Clone)]
pub struct PageSpec {
pub path: String,
pub doc_id: String,
}
pub trait CliHandler: Send + Sync {
fn run(&self, args: BTreeMap<String, String>, client: &crate::client::Client)
-> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send + '_>>;
}
#[derive(Clone)]
pub struct CliCommand {
pub name: &'static str,
pub about: &'static str,
pub subcommands: Vec<CliSubcommand>,
}
#[derive(Clone)]
pub struct CliSubcommand {
pub name: &'static str,
pub about: &'static str,
pub args: Vec<CliArg>,
pub handler: Option<Arc<dyn CliHandler>>,
}
#[derive(Debug, Clone)]
pub struct CliArg {
pub long: &'static str,
pub short: Option<char>,
pub help: &'static str,
pub required: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CliCommandManifest {
pub extensions: Vec<CliCommandSpec>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CliCommandSpec {
pub extension_id: String,
pub name: String,
pub about: String,
pub subcommands: Vec<CliSubcommandSpec>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CliSubcommandSpec {
pub name: String,
pub about: String,
pub args: Vec<CliArgSpec>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CliArgSpec {
pub long: String,
pub short: Option<char>,
pub help: String,
pub required: bool,
}
#[async_trait]
pub trait Extension: Send + Sync {
fn id(&self) -> &str;
fn display_name(&self, lang: Lang) -> String;
fn migrations(&self) -> Vec<Migration>;
fn routes(&self) -> Router<AppState>;
async fn lobby_summary(&self, ctx: &AppState) -> Option<LobbyCard>;
async fn on_startup(&self, _ctx: &AppState) -> anyhow::Result<()> {
Ok(())
}
async fn on_disable(&self, ctx: &AppState) -> anyhow::Result<()> {
crate::search::delete_extension(&ctx.db, self.id()).await?;
Ok(())
}
fn background_jobs(&self) -> Vec<Arc<dyn ScheduledJob>> {
Vec::new()
}
fn public_pages(&self) -> Vec<PageSpec> {
Vec::new()
}
fn table_names(&self) -> Vec<&'static str> {
Vec::new()
}
fn route_dispatcher(&self) -> Option<&dyn RouteDispatcher> {
None
}
fn cli_commands(&self) -> Vec<CliCommand> {
Vec::new()
}
}
#[derive(Debug, Clone)]
pub struct RouteSpec {
pub method: String,
pub path: String,
}
#[derive(Debug)]
pub struct RouteResponse {
pub status: u16,
pub body: Vec<u8>,
}
#[async_trait]
pub trait RouteDispatcher: Send + Sync {
fn route_specs(&self) -> &[RouteSpec];
async fn dispatch(
&self,
method: &str,
path: &str,
body: Vec<u8>,
ctx: &AppState,
) -> RouteResponse;
}
pub trait WasmLoader: Send + Sync {
fn load(&self, path: &std::path::Path) -> anyhow::Result<Arc<dyn Extension>>;
}
#[derive(Debug, serde::Serialize)]
pub struct DataEnvelope<T: serde::Serialize> {
pub data: T,
}
#[derive(Debug, serde::Serialize)]
pub struct ListEnvelope<T: serde::Serialize> {
pub data: Vec<T>,
pub meta: ListMeta,
}
#[derive(Debug, serde::Serialize, Default)]
pub struct ListMeta {
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<String>,
}