4 use crate::api_schema
::*;
5 use crate::api_schema
::router
::*;
8 pub enum ParameterDisplayStyle
{
15 /// CLI usage information format
16 #[derive(Copy, Clone, PartialEq)]
17 pub enum DocumentationFormat
{
18 /// text, command line only (one line)
20 /// text, list all options
22 /// text, include description
24 /// like full, but in reStructuredText format
28 /// line wrapping to form simple list of paragraphs
29 pub fn wrap_text(initial_indent
: &str, subsequent_indent
: &str, text
: &str, columns
: usize) -> String
{
31 let wrapper1
= textwrap
::Wrapper
::new(columns
)
32 .initial_indent(initial_indent
)
33 .subsequent_indent(subsequent_indent
);
35 let wrapper2
= textwrap
::Wrapper
::new(columns
)
36 .initial_indent(subsequent_indent
)
37 .subsequent_indent(subsequent_indent
);
41 .filter(|p
| { p.len() != 0 }
)
42 .fold(String
::new(), |mut acc
, p
| {
44 acc
.push_str(&wrapper1
.wrap(p
).concat());
46 acc
.push_str(&wrapper2
.wrap(p
).concat());
53 pub fn get_schema_type_text(schema
: &Schema
, _style
: ParameterDisplayStyle
) -> String
{
55 Schema
::Null
=> String
::from("<null>"), // should not happen
56 Schema
::String(_
) => String
::from("<string>"),
57 Schema
::Boolean(_
) => String
::from("<boolean>"),
58 Schema
::Integer(integer_schema
) => {
59 match (integer_schema
.minimum
, integer_schema
.maximum
) {
60 (Some(min
), Some(max
)) => format
!("<integer> ({} - {})", min
, max
),
61 (Some(min
), None
) => format
!("<integer> ({} - N)", min
),
62 (None
, Some(max
)) => format
!("<integer> (-N - {})", max
),
63 _
=> String
::from("<integer>"),
66 Schema
::Object(_
) => String
::from("<object>"),
67 Schema
::Array(_
) => String
::from("<array>"),
71 pub fn get_property_description(
74 style
: ParameterDisplayStyle
,
75 format
: DocumentationFormat
,
78 let type_text
= get_schema_type_text(schema
, style
);
80 let (descr
, default) = match schema
{
81 Schema
::Null
=> ("null", None
),
82 Schema
::String(ref schema
) => (schema
.description
, schema
.default.map(|v
| v
.to_owned())),
83 Schema
::Boolean(ref schema
) => (schema
.description
, schema
.default.map(|v
| v
.to_string())),
84 Schema
::Integer(ref schema
) => (schema
.description
, schema
.default.map(|v
| v
.to_string())),
85 Schema
::Object(ref schema
) => (schema
.description
, None
),
86 Schema
::Array(ref schema
) => (schema
.description
, None
),
89 let default_text
= match default {
90 Some(text
) => format
!(" (default={})", text
),
91 None
=> String
::new(),
94 if format
== DocumentationFormat
::ReST
{
96 let mut text
= match style
{
97 ParameterDisplayStyle
::Config
=> {
98 format
!(":``{} {}{}``: ", name
, type_text
, default_text
)
100 ParameterDisplayStyle
::Arg
=> {
101 format
!(":``--{} {}{}``: ", name
, type_text
, default_text
)
103 ParameterDisplayStyle
::Fixed
=> {
104 format
!(":``<{}> {}{}``: ", name
, type_text
, default_text
)
108 text
.push_str(&wrap_text("", "", descr
, 80));
116 let display_name
= match style
{
117 ParameterDisplayStyle
::Config
=> {
120 ParameterDisplayStyle
::Arg
=> {
121 format
!("--{}", name
)
123 ParameterDisplayStyle
::Fixed
=> {
124 format
!("<{}>", name
)
128 let mut text
= format
!(" {:-10} {}{}", display_name
, type_text
, default_text
);
131 text
.push_str(&wrap_text(indent
, indent
, descr
, 80));
139 fn dump_api_parameters(param
: &ObjectSchema
) -> String
{
141 let mut res
= wrap_text("", "", param
.description
, 80);
143 let properties
= ¶m
.properties
;
145 let mut prop_names
: Vec
<&str> = properties
.keys().map(|v
| *v
).collect();
148 let mut required_list
: Vec
<String
> = vec
![];
149 let mut optional_list
: Vec
<String
> = vec
![];
151 for prop
in prop_names
{
152 let (optional
, schema
) = properties
.get(prop
).unwrap();
154 let param_descr
= get_property_description(
155 prop
, &schema
, ParameterDisplayStyle
::Config
, DocumentationFormat
::ReST
);
158 optional_list
.push(param_descr
);
160 required_list
.push(param_descr
);
164 if required_list
.len() > 0 {
166 res
.push_str("\n*Required properties:*\n\n");
168 for text
in required_list
{
175 if optional_list
.len() > 0 {
177 res
.push_str("\n*Optional properties:*\n\n");
179 for text
in optional_list
{
188 fn dump_api_return_schema(schema
: &Schema
) -> String
{
190 let mut res
= String
::from("*Returns*: ");
192 let type_text
= get_schema_type_text(schema
, ParameterDisplayStyle
::Config
);
193 res
.push_str(&format
!("**{}**\n\n", type_text
));
199 Schema
::Boolean(schema
) => {
200 let description
= wrap_text("", "", schema
.description
, 80);
201 res
.push_str(&description
);
203 Schema
::Integer(schema
) => {
204 let description
= wrap_text("", "", schema
.description
, 80);
205 res
.push_str(&description
);
207 Schema
::String(schema
) => {
208 let description
= wrap_text("", "", schema
.description
, 80);
209 res
.push_str(&description
);
211 Schema
::Array(schema
) => {
212 let description
= wrap_text("", "", schema
.description
, 80);
213 res
.push_str(&description
);
215 Schema
::Object(obj_schema
) => {
216 res
.push_str(&dump_api_parameters(obj_schema
));
226 fn dump_method_definition(method
: &str, path
: &str, def
: &MethodDefinition
) -> Option
<String
> {
229 MethodDefinition
::None
=> None
,
230 MethodDefinition
::Simple(simple_method
) => {
231 let param_descr
= dump_api_parameters(&simple_method
.parameters
);
233 let return_descr
= dump_api_return_schema(&simple_method
.returns
);
235 let res
= format
!("**{} {}**\n\n{}\n\n{}", method
, path
, param_descr
, return_descr
);
238 MethodDefinition
::Async(async_method
) => {
239 let method
= if method
== "POST" { "UPLOAD" }
else { method }
;
240 let method
= if method
== "GET" { "DOWNLOAD" }
else { method }
;
242 let param_descr
= dump_api_parameters(&async_method
.parameters
);
244 let return_descr
= dump_api_return_schema(&async_method
.returns
);
246 let res
= format
!("**{} {}**\n\n{}\n\n{}", method
, path
, param_descr
, return_descr
);
252 pub fn dump_api(output
: &mut dyn Write
, router
: &Router
, path
: &str, mut pos
: usize) -> Result
<(), Error
> {
254 let mut cond_print
= |x
| -> Result
<_
, Error
> {
255 if let Some(text
) = x
{
257 writeln
!(output
, "-----\n")?
;
259 writeln
!(output
, "{}", text
)?
;
265 cond_print(dump_method_definition("GET", path
, &router
.get
))?
;
266 cond_print(dump_method_definition("POST", path
, &router
.post
))?
;
267 cond_print(dump_method_definition("PUT", path
, &router
.put
))?
;
268 cond_print(dump_method_definition("DELETE", path
, &router
.delete
))?
;
270 match &router
.subroute
{
271 SubRoute
::None
=> return Ok(()),
272 SubRoute
::MatchAll { router, param_name }
=> {
273 let sub_path
= if path
== "." {
274 format
!("<{}>", param_name
)
276 format
!("{}/<{}>", path
, param_name
)
278 dump_api(output
, router
, &sub_path
, pos
)?
;
280 SubRoute
::Hash(map
) => {
281 let mut keys
: Vec
<&String
> = map
.keys().collect();
282 keys
.sort_unstable_by(|a
, b
| a
.cmp(b
));
284 let sub_router
= &map
[key
];
285 let sub_path
= if path
== "." { key.to_owned() }
else { format!("{}
/{}
", path, key) };
286 dump_api(output, sub_router, &sub_path, pos)?;