web.openapi
运行期 /openapi.json 端点:自路由表同源导出最小 OpenAPI 3 (Plan 2b Task 2;设计 docs/superpowers/specs/2026-09-27-web-framework-design.md §4.1;纪律 §14-1)
形态:openapi_json(r, title, ver) -> Resp——装配点一行挂载
r.get("/openapi.json", |rq| openapi_json(app, title, ver))(app 为终态 Router;
spec §4.1 .openapi(标题, 版本) 链式糖即此二行的封装,v1 不另入 Router 面)。
spec §13-⑥ 惰性缓存不在此件——端点闭包自持缓存归装配点,本件恒纯函数。
导出面(最小 OpenAPI 3.0.3):openapi/info.title/info.version/paths;
路由模式段还原 —— lit 原样、:par → {par}、*splat → {splat}(OpenAPI
无尾通配概念,模板化登记);同模板多方法归并进同一 path 对象(声明序);
每方法 responses 恒 200 → description "ok"(v1 无逐路由响应模型,登记志向)。
JSON 构建:§14-1 收集+join 一遍成形(无中途拼接);字符串经 oapi_esc 转义 (core.json_esc 私有不可复用,core 增导出会牵动 2a 冻结面——本件自铸,名面隔开)。 method 小写化逐字节 +32(utf8_enc 单字符),A-Z 域外原样。
pub struct
struct Pair
| Field | Type |
|---|---|
k |
Str |
v |
Str |
struct Resp
| Field | Type |
|---|---|
status |
I32 |
headers |
List[Pair] |
body |
Str |
struct JObj
| Field | Type |
|---|---|
parts |
List[Str] |
struct Req[S]
| Field | Type |
|---|---|
method |
Str |
path |
Str |
query |
Str |
body |
Str |
headers |
List[Pair] |
cookies |
List[Pair] |
params |
List[Pair] |
session |
List[Pair] |
state |
S |
struct RouteSeg
| Field | Type |
|---|---|
kind |
Str |
text |
Str |
struct Route[S]
| Field | Type |
|---|---|
m |
Str |
segs |
List[RouteSeg] |
chain |
List[fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp] |
h |
fn(&Req[S]) -> Resp |
struct Router[S]
| Field | Type |
|---|---|
state |
S |
routes |
List[Route[S]] |
mws |
List[fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp] |
not_found_h |
List[fn(&Req[S]) -> Resp] |
not_allowed_h |
List[fn(&Req[S]) -> Resp] |
struct Match[S]
| Field | Type |
|---|---|
kind |
I32 |
route |
Route[S] |
params |
List[Pair] |
splat |
Str |
allow |
Str |
pub fn
| Signature | Returns | Description |
|---|---|---|
openapi_json[S](r: Router[S], title: Str, ver: Str) |
Resp |
— |
html(body: Str) |
Resp |
— |
json(body: Str) |
Resp |
— |
text(body: Str) |
Resp |
— |
redirect(loc: Str) |
Resp |
— |
status(n: I32, body: Str) |
Resp |
— |
xml(body: Str) |
Resp |
— |
yaml(body: Str) |
Resp |
— |
err_json(n: I32, msg: Str) |
Resp |
— |
attachment(r: Resp, filename: Str) |
Resp |
— |
with(r: Resp, k: Str, v: Str) |
Resp |
— |
with_status(r: Resp, n: I32) |
Resp |
— |
resp_header(r: Resp, k: Str) |
Str |
— |
resp_body(r: Resp) |
Str |
— |
resp_status(r: Resp) |
I32 |
— |
json_obj() |
JObj |
— |
str(b: JObj, k: Str, v: Str) |
JObj |
— |
i64(b: JObj, k: Str, v: I64) |
JObj |
— |
bool(b: JObj, k: Str, v: Bool) |
JObj |
— |
strs(b: JObj, k: Str, xs: List[Str]) |
JObj |
— |
end(b: JObj) |
Str |
— |
end_status(b: JObj, n: I32) |
Resp |
— |
csv_rows(headers: List[Str], rows: List[List[Str]]) |
Str |
— |
req_of[S](method: Str, path: Str, state: S) |
Req[S] |
— |
req_full[S](method: Str, path: Str, body: Str, hdrs: List[Pair], state: S) |
Req[S] |
— |
req_set_params[S](r: &Req[S], params: List[Pair]) |
Req[S] |
— |
req_set_session[S](r: &Req[S], sess: List[Pair]) |
Req[S] |
— |
pdec(s: Str) |
Str |
— |
param[S](r: &Req[S], name: Str) |
Str |
— |
pair_get(ps: List[Pair], k: Str) |
Str |
— |
param_i64[S](r: &Req[S], name: Str) |
Option[I64] |
— |
param_f64[S](r: &Req[S], name: Str) |
Option[F64] |
— |
query[S](r: &Req[S], name: Str) |
Str |
— |
query_all[S](r: &Req[S], name: Str) |
List[Str] |
— |
form[S](r: &Req[S], name: Str) |
Str |
— |
form_all[S](r: &Req[S], name: Str) |
List[Str] |
— |
header[S](r: &Req[S], name: Str) |
Str |
— |
cookie[S](r: &Req[S], name: Str) |
Str |
— |
session[S](r: &Req[S], name: Str) |
Str |
— |
router[S](state: S) |
Router[S] |
— |
get[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) |
Router[S] |
— |
post[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) |
Router[S] |
— |
put[S](r: Router[S], p: Str, h: fn(&Req[S]) -> Resp) |
Router[S] |
— |
middleware[S](r: Router[S], mw: fn(&Req[S], fn(&Req[S]) -> Resp) -> Resp) |
Router[S] |
— |
mount_at[S](r: Router[S], prefix: Str, sub: Router[S]) |
Router[S] |
— |
has_conflict[S](r: Router[S]) |
Bool |
— |
install[S](r: Router[S], p: fn(Router[S]) -> Router[S]) |
Router[S] |
— |
not_found[S](r: Router[S], h: fn(&Req[S]) -> Resp) |
Router[S] |
— |
method_not_allowed[S](r: Router[S], h: fn(&Req[S]) -> Resp) |
Router[S] |
— |
test_call[S](r: Router[S], method: Str, path: Str, body: Str) |
Resp |
— |
route_match[S](r: Router[S], m: Str, path: Str) |
Match[S] |
— |
dispatch_h[S](r: Router[S], method: Str, path: Str, body: Str, hdrs: List[Pair]) |
Resp |
— |
dispatch[S](r: Router[S], method: Str, path: Str, body: Str) |
Resp |
— |