///|
/// The default port a moonzero service listens on.
///
/// 12000 is the family's number, not go-zero's 8888: every example, every
/// document and every local run in this ecosystem points at the same place, and
/// on a development machine 8888 is somebody else's already.
pub let port : Int32 = 12000
///|
/// The default port the zRPC server listens on.
///
/// gRPC shares 12000 with HTTP wherever one listener carries both, but a
/// moonzero process binds `RestEngine` and `RpcServer` separately — two
/// listeners cannot hold one port — so the second takes the next number.
pub let rpc_port : Int32 = 12001
///|
/// Log verbosity (← go-zero's `LogConf.Level`), ordered from most to least
/// verbose. `Compare` follows that order so thresholds can be tested directly.
pub(all) enum LogLevel {
Debug
Info
Error
Severe
} derive(Eq, Compare, Debug)
///|
/// The canonical lowercase name go-zero uses on the wire.
pub fn LogLevel::to_string(self : LogLevel) -> String {
match self {
Debug => "debug"
Info => "info"
Error => "error"
Severe => "severe"
}
}
///|
/// The `moonlog` level this one records as. go-zero's ladder has four rungs and
/// `moonlog`'s has six, so the map is one-way: `Severe` records as `Fatal`, and
/// [`level_name`] writes it back out as `severe`.
pub fn LogLevel::as_level(self : LogLevel) -> @moonlog.Level {
match self {
Debug => Debug
Info => Info
Error => Error
Severe => Fatal
}
}
///|
/// Parse a level name, falling back to `Info` for anything unrecognised — the
/// same lenient default go-zero applies to a missing/empty level.
pub fn LogLevel::parse(s : String) -> LogLevel {
match s {
"debug" => Debug
"error" => Error
"severe" => Severe
_ => Info
}
}
///|
/// Service configuration (← go-zero's `ServiceConf`): the service name, its bind
/// address, a request timeout, and the log level. `timeout_ms` is the per-request
/// budget in milliseconds; `0` disables the deadline.
pub(all) struct ServiceConf {
name : String
host : String
port : Int32
timeout_ms : Int32
log_level : LogLevel
} derive(FromJson, Eq)
///|
/// Build a config with sensible defaults (`0.0.0.0:12000`, 3s timeout, `info`).
pub fn ServiceConf::new(
name~ : String = "app",
host~ : String = "0.0.0.0",
port~ : Int32 = port,
timeout_ms~ : Int32 = 3000,
log_level~ : LogLevel = Info,
) -> ServiceConf {
{ name, host, port, timeout_ms, log_level, }
}
///|
/// An AsgiApp transformer — one layer of the middleware onion.
pub type Middleware = (@moonasgi.AsgiApp) -> @moonasgi.AsgiApp
///|
/// A moonzero service: its config plus the assembled application (a moonapi App
/// with any middleware already wrapped around it).
pub struct Server {
conf : ServiceConf
handler : @moonasgi.AsgiApp
}
///|
/// Assemble a service from config and a moonapi application.
pub fn Server::new(conf : ServiceConf, app : @moonapi.App) -> Server {
{ conf, handler: app.to_asgi(), }
}
///|
/// Wrap the current application in another middleware layer (outermost last).
pub fn Server::use_(self : Server, mw : Middleware) -> Server {
{ conf: self.conf, handler: mw(self.handler), }
}
///|
/// The assembled `AsgiApp`, ready for a server (mooncat) to run.
pub fn Server::to_asgi(self : Server) -> @moonasgi.AsgiApp {
self.handler
}
///|
/// A human-readable description of what this service binds to.
pub fn Server::describe(self : Server) -> String {
self.conf.name +
" listening on " +
self.conf.host +
":" +
self.conf.port.to_string()
}
///|
/// A request-logging middleware: writes `METHOD path` through the process `logx`
/// at `info` for each HTTP request, then delegates to the wrapped application.
/// It logs on the way in and knows nothing of the response; `structured_logging`
/// is the layer that times the request and records its status.
pub fn logging(inner : @moonasgi.AsgiApp) -> @moonasgi.AsgiApp {
(scope, receive, send) => {
match scope {
Http(hs) => logx.info(hs.http_method + " " + hs.path)
_ => ()
}
inner(scope, receive, send)
}
}
///|
pub extend LogLevel with Compare::{op_lt, op_le, op_ge, compare, op_gt}
///|
pub extend LogLevel with Debug::{to_repr}
///|
pub extend LogLevel with Eq::{not_equal, equal}
///|
pub extend LogLevel with FromJson::{from_json}
///|
pub extend ServiceConf with Eq::{not_equal, equal}