7 //use super::api_handler::*;
10 #[derive(Copy, Clone)]
11 pub enum ParameterDisplayStyle
{
18 /// CLI usage information format
19 #[derive(Copy, Clone, PartialEq)]
20 pub enum DocumentationFormat
{
21 /// text, command line only (one line)
23 /// text, list all options
25 /// text, include description
27 /// like full, but in reStructuredText format
31 /// line wrapping to form simple list of paragraphs
32 pub fn wrap_text(initial_indent
: &str, subsequent_indent
: &str, text
: &str, columns
: usize) -> String
{
34 let wrapper1
= textwrap
::Wrapper
::new(columns
)
35 .initial_indent(initial_indent
)
36 .subsequent_indent(subsequent_indent
);
38 let wrapper2
= textwrap
::Wrapper
::new(columns
)
39 .initial_indent(subsequent_indent
)
40 .subsequent_indent(subsequent_indent
);
44 .filter(|p
| !p
.is_empty())
45 .fold(String
::new(), |mut acc
, p
| {
47 acc
.push_str(&wrapper1
.wrap(p
).concat());
49 acc
.push_str(&wrapper2
.wrap(p
).concat());
56 pub fn get_schema_type_text(schema
: &Schema
, _style
: ParameterDisplayStyle
) -> String
{
58 Schema
::Null
=> String
::from("<null>"), // should not happen
59 Schema
::String(_
) => String
::from("<string>"),
60 Schema
::Boolean(_
) => String
::from("<boolean>"),
61 Schema
::Integer(integer_schema
) => {
62 match (integer_schema
.minimum
, integer_schema
.maximum
) {
63 (Some(min
), Some(max
)) => format
!("<integer> ({} - {})", min
, max
),
64 (Some(min
), None
) => format
!("<integer> ({} - N)", min
),
65 (None
, Some(max
)) => format
!("<integer> (-N - {})", max
),
66 _
=> String
::from("<integer>"),
69 Schema
::Object(_
) => String
::from("<object>"),
70 Schema
::Array(_
) => String
::from("<array>"),
74 pub fn get_property_description(
77 style
: ParameterDisplayStyle
,
78 format
: DocumentationFormat
,
81 let type_text
= get_schema_type_text(schema
, style
);
83 let (descr
, default) = match schema
{
84 Schema
::Null
=> ("null", None
),
85 Schema
::String(ref schema
) => (schema
.description
, schema
.default.map(|v
| v
.to_owned())),
86 Schema
::Boolean(ref schema
) => (schema
.description
, schema
.default.map(|v
| v
.to_string())),
87 Schema
::Integer(ref schema
) => (schema
.description
, schema
.default.map(|v
| v
.to_string())),
88 Schema
::Object(ref schema
) => (schema
.description
, None
),
89 Schema
::Array(ref schema
) => (schema
.description
, None
),
92 let default_text
= match default {
93 Some(text
) => format
!(" (default={})", text
),
94 None
=> String
::new(),
97 if format
== DocumentationFormat
::ReST
{
99 let mut text
= match style
{
100 ParameterDisplayStyle
::Config
=> {
101 format
!(":``{} {}{}``: ", name
, type_text
, default_text
)
103 ParameterDisplayStyle
::Arg
=> {
104 format
!(":``--{} {}{}``: ", name
, type_text
, default_text
)
106 ParameterDisplayStyle
::Fixed
=> {
107 format
!(":``<{}> {}{}``: ", name
, type_text
, default_text
)
111 text
.push_str(&wrap_text("", "", descr
, 80));
119 let display_name
= match style
{
120 ParameterDisplayStyle
::Config
=> {
123 ParameterDisplayStyle
::Arg
=> {
124 format
!("--{}", name
)
126 ParameterDisplayStyle
::Fixed
=> {
127 format
!("<{}>", name
)
131 let mut text
= format
!(" {:-10} {}{}", display_name
, type_text
, default_text
);
134 text
.push_str(&wrap_text(indent
, indent
, descr
, 80));
142 fn dump_api_parameters(param
: &ObjectSchema
) -> String
{
144 let mut res
= wrap_text("", "", param
.description
, 80);
146 let mut required_list
: Vec
<String
> = Vec
::new();
147 let mut optional_list
: Vec
<String
> = Vec
::new();
149 for (prop
, optional
, schema
) in param
.properties
{
150 let param_descr
= get_property_description(
151 prop
, &schema
, ParameterDisplayStyle
::Config
, DocumentationFormat
::ReST
);
154 optional_list
.push(param_descr
);
156 required_list
.push(param_descr
);
160 if !required_list
.is_empty() {
162 res
.push_str("\n*Required properties:*\n\n");
164 for text
in required_list
{
171 if !optional_list
.is_empty() {
173 res
.push_str("\n*Optional properties:*\n\n");
175 for text
in optional_list
{
184 fn dump_api_return_schema(schema
: &Schema
) -> String
{
186 let mut res
= String
::from("*Returns*: ");
188 let type_text
= get_schema_type_text(schema
, ParameterDisplayStyle
::Config
);
189 res
.push_str(&format
!("**{}**\n\n", type_text
));
195 Schema
::Boolean(schema
) => {
196 let description
= wrap_text("", "", schema
.description
, 80);
197 res
.push_str(&description
);
199 Schema
::Integer(schema
) => {
200 let description
= wrap_text("", "", schema
.description
, 80);
201 res
.push_str(&description
);
203 Schema
::String(schema
) => {
204 let description
= wrap_text("", "", schema
.description
, 80);
205 res
.push_str(&description
);
207 Schema
::Array(schema
) => {
208 let description
= wrap_text("", "", schema
.description
, 80);
209 res
.push_str(&description
);
211 Schema
::Object(obj_schema
) => {
212 res
.push_str(&dump_api_parameters(obj_schema
));
222 fn dump_method_definition(method
: &str, path
: &str, def
: Option
<&ApiMethod
>) -> Option
<String
> {
226 Some(api_method
) => {
227 let param_descr
= dump_api_parameters(api_method
.parameters
);
229 let return_descr
= dump_api_return_schema(api_method
.returns
);
231 let mut method
= method
;
233 if let ApiHandler
::Async(_
) = api_method
.handler
{
234 method
= if method
== "POST" { "UPLOAD" }
else { method }
;
235 method
= if method
== "GET" { "DOWNLOAD" }
else { method }
;
238 let res
= format
!("**{} {}**\n\n{}\n\n{}", method
, path
, param_descr
, return_descr
);
244 pub fn dump_api(output
: &mut dyn Write
, router
: &Router
, path
: &str, mut pos
: usize) -> Result
<(), Error
> {
246 let mut cond_print
= |x
| -> Result
<_
, Error
> {
247 if let Some(text
) = x
{
249 writeln
!(output
, "-----\n")?
;
251 writeln
!(output
, "{}", text
)?
;
257 cond_print(dump_method_definition("GET", path
, router
.get
))?
;
258 cond_print(dump_method_definition("POST", path
, router
.post
))?
;
259 cond_print(dump_method_definition("PUT", path
, router
.put
))?
;
260 cond_print(dump_method_definition("DELETE", path
, router
.delete
))?
;
262 match &router
.subroute
{
263 None
=> return Ok(()),
264 Some(SubRoute
::MatchAll { router, param_name }
) => {
265 let sub_path
= if path
== "." {
266 format
!("<{}>", param_name
)
268 format
!("{}/<{}>", path
, param_name
)
270 dump_api(output
, router
, &sub_path
, pos
)?
;
272 Some(SubRoute
::Map(dirmap
)) => {
273 //let mut keys: Vec<&String> = map.keys().collect();
274 //keys.sort_unstable_by(|a, b| a.cmp(b));
275 for (key
, sub_router
) in dirmap
.iter() {
276 let sub_path
= if path
== "." { key.to_string() }
else { format!("{}
/{}
", path, key) };
277 dump_api(output, sub_router, &sub_path, pos)?;