@@ -54,11 +54,41 @@ fn path_parameters(path : String, version : OpenApiVersion) -> Array[Json] {
5454///|
5555/// Build the OpenAPI / Swagger document for the app as a `Json` value, walking
5656/// the registered routes once into paths → methods → operations.
57+ /// OpenAPI `contact` info (← FastAPI's `contact`): every field optional, emitted
58+ /// only when non-empty.
59+ pub (all) struct Contact {
60+ name : String
61+ url : String
62+ email : String
63+ }
64+
65+ ///|
66+ /// OpenAPI `license` info (← FastAPI's `license_info`): `name` required, `url`
67+ /// optional.
68+ pub (all) struct License {
69+ name : String
70+ url : String
71+ }
72+
73+ ///|
74+ /// A `servers` entry (← FastAPI's `servers`): a base URL and an optional
75+ /// description.
76+ pub (all) struct Server {
77+ url : String
78+ description : String
79+ }
80+
81+ ///|
5782pub fn App ::openapi(
5883 self : App ,
5984 version? : OpenApiVersion = OpenApi31 ,
6085 title? : String = "moonapi",
6186 api_version? : String = "0.1.0",
87+ description? : String = "",
88+ terms_of_service? : String = "",
89+ contact? : Contact? = None ,
90+ license? : License? = None ,
91+ servers? : Array [Server ] = [] ,
6292) -> Json {
6393 let paths : Map [String , Json ] = Map ([] )
6494 let defs : Map [String , Json ] = Map ([] )
@@ -110,12 +140,64 @@ pub fn App::openapi(
110140 ("title", title.to_json()),
111141 ("version", api_version.to_json()),
112142 ])
143+ if description != "" {
144+ info["description"] = description.to_json()
145+ }
146+ if terms_of_service != "" {
147+ info["termsOfService "] = terms_of_service.to_json()
148+ }
149+ match contact {
150+ Some (c) => {
151+ let cm : Map [String , Json ] = Map ([] )
152+ if c.name != "" {
153+ cm["name"] = c.name.to_json()
154+ }
155+ if c.url != "" {
156+ cm["url"] = c.url.to_json()
157+ }
158+ if c.email != "" {
159+ cm["email"] = c.email.to_json()
160+ }
161+ if !cm.is_empty() {
162+ info["contact"] = cm.to_json()
163+ }
164+ }
165+ None => ()
166+ }
167+ match license {
168+ Some (l) => {
169+ let lm : Map [String , Json ] = Map ([("name", l.name.to_json())])
170+ if l.url != "" {
171+ lm["url"] = l.url.to_json()
172+ }
173+ info["license"] = lm.to_json()
174+ }
175+ None => ()
176+ }
113177 let (root_key, version_str) = version_field(version)
114178 let doc : Map [String , Json ] = Map ([
115179 (root_key, version_str.to_json()),
116180 ("info", info.to_json()),
117181 ("paths", paths.to_json()),
118182 ])
183+ // `servers` is an OpenAPI 3.x construct (2.0 uses host/basePath), so emit it
184+ // only for the 3.x dialects.
185+ if !servers.is_empty() {
186+ match version {
187+ Swagger20 => ()
188+ _ => {
189+ let sarr : Array [Json ] = []
190+ for s in servers {
191+ let sm : Map [String , Json ] = Map ([("url", s.url.to_json())])
192+ if s.description != "" {
193+ sm["description"] = s.description.to_json()
194+ }
195+ sarr.push(sm.to_json())
196+ }
197+ doc["servers"] = sarr.to_json()
198+ }
199+ }
200+ }
119201 // Hoisted named object schemas and the declared security schemes (the app's
120202 // own plus every mounted sub-app's): under `components` (`schemas` /
121203 // `securitySchemes`) in 3.x, or the top-level `definitions` /
0 commit comments