diff --git a/help.html b/help.html index 84a65ca4..bab2a5b7 100644 --- a/help.html +++ b/help.html @@ -1,2 +1,2 @@ -Help +Help

Rustdoc help

Back
\ No newline at end of file diff --git a/search-index.js b/search-index.js index 0c351c5e..4b825f77 100644 --- a/search-index.js +++ b/search-index.js @@ -1,5 +1,5 @@ var searchIndex = new Map(JSON.parse('[\ -["tide_disco",{"doc":"Tide Disco is a web server framework with built-in …","t":"EEIIPPPPPFGEPGPIPPPEEEEEESSFPEPPPPFGOPCOPCHOOPNNNNNNNOPNNNNNNNNNNNNNHNNNNNNNNNNHHHNNNPNNNNNNCNNNNNNNNNNNNNNNNNNNNHNNNNNNOOCHNNENNNNHNNNNNNNNNNNNNNNHNCCNNHNNNNNNNNNNNNNCOONNNNNNNNNNNNCNCCNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNHPFGFPFPPPPPPPPPPONNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNONNNNNNNOOOOOONNNNNNONOOOONNONNNNONNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOPFGFFKRFPKMONNMNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNMNNNNNNNOONNNNNNNOMNNNNNNNNNNNNNNNNNNNNNNNNNNNOKFNNNNMNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNMNONNNNNNPKGPPPPPNNNNNNNNNNNNNNNMNNNNNNPGPFKPRKNNNNNNNNNNNNNNNNNNNNNNNNNNNMNNNNNNNNNNNMRKMPPPPPPPPPPPPPPPGFGGFPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOPPPFPPPGPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPFPPPPPPPPPPPPPGPPPPPPPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNFNNNNNNNNHHHNNNN","n":["Api","App","AppServerState","AppState","Array","Available","Boolean","Boolean","Datetime","DiscoArgs","DiscoKey","Error","Float","HealthStatus","Hexadecimal","Html","Integer","Integer","Literal","Method","RequestError","RequestParam","RequestParamType","RequestParamValue","RequestParams","SERVER_STARTUP_RETRIES","SERVER_STARTUP_SLEEP_MS","ServerState","Starting","StatusCode","Stopping","String","Table","TaggedBase64","Url","UrlSegment","ansi_color","ansi_color","api","api_toml","api_toml","app","app_api_path","app_state","app_state","app_toml","as_ref","as_ref","as_ref","as_str","augment_args","augment_args_for_update","authority","base_url","base_url","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cannot_be_a_base","check_api","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","cmp","compare","compose_config_path","compose_settings","configure_router","deserialize","deserialize","deserialize_internal","disco_toml","domain","eq","equivalent","equivalent","equivalent","equivalent","error","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fragment","from","from","from","from","from","from","from_arg_matches","from_arg_matches_mut","from_directory_path","from_file_path","from_str","from_str","get_api_path","group_id","has_authority","has_host","hash","header","header","health_status","health_status","healthcheck","healthcheck","host","host_str","http","index","index","index","index","init_logging","into","into","into","into","into","into","into_client_request","into_client_request","into_client_request","into_client_request","into_resettable","into_string","is_bound","is_special","join","load_api","make_relative","method","metrics","new","options","org_data_path","origin","parse","parse_with_params","partial_cmp","password","path","path_segments","path_segments_mut","port","port_or_known_default","query","query_pairs","query_pairs_mut","request","router","router","scheme","serialize","serialize_internal","set_fragment","set_host","set_ip_host","set_password","set_path","set_port","set_query","set_scheme","set_username","socket","socket_addrs","status","testing","to_file_path","to_listener","to_owned","to_owned","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","update_from_arg_matches","update_from_arg_matches_mut","username","vzip","vzip","vzip","vzip","vzip","vzip","wait_for_server","AmbiguousRoutes","Api","ApiError","ApiMetadata","ApiMustBeTable","ApiVersion","CannotReadToml","HandlerAlreadyRegistered","IncorrectMethod","InvalidFormatVersion","InvalidMetaTable","MissingFormatVersion","MissingRoutesTable","Route","RoutesMustBeTable","UndefinedRoute","api_version","as_error_source","at","backtrace","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","clone","clone","clone","clone_into","clone_into","clone_into","default","delete","description","description","deserialize","deserialize","eq","eq","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","fmt","fmt","fmt","fmt","fmt","format_version","from","from","from","from","from_file","get","header","heading_description","heading_entry","heading_parameters","heading_routes","html_bottom","html_top","in_current_span","into","into","into","into","metrics","name","new","parameter_none","parameter_row","parameter_table_close","parameter_table_open","post","put","route_path","serialize","serialize","socket","source","spec_version","stream","to_owned","to_owned","to_owned","to_string","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","vzip","vzip","vzip","vzip","with_health_check","with_public","with_version","actual","expected","reason","route1","route2","source","source","Api","App","AppError","AppHealth","AppVersion","Listener","Listener","Module","ModuleAlreadyExists","ToListener","accept","app_version","as_error_source","backtrace","bind","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","clone","clone","clone","clone_into","clone_into","clone_into","deref","deref_mut","description","deserialize","deserialize","disco_version","drop","eq","eq","eq","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","header","health","in_current_span","info","into","into","into","into","into","module","module_health","modules","modules","register","register_module","serialize","serialize","serve","source","status","status","to_listener","to_owned","to_owned","to_owned","to_string","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","version","vzip","vzip","vzip","vzip","vzip","with_state","with_version","source","Error","ServerError","as_error_source","backtrace","borrow","borrow_mut","catch_all","catch_all","cause","clone","clone_into","description","deserialize","eq","equivalent","equivalent","equivalent","equivalent","fmt","fmt","from","from","from","from","from","from_config_error","from_io_error","from_request_error","from_route_error","from_server_error","from_socket_error","header","in_current_span","into","into_tide_error","message","serialize","source","status","status","status","to_owned","to_string","try_from","try_into","type_id","vzip","Available","HealthCheck","HealthStatus","Initializing","ShuttingDown","TemporarilyUnavailable","Unavailabale","Unhealthy","borrow","borrow_mut","clone","clone_into","default","deserialize","eq","equivalent","equivalent","equivalent","equivalent","fmt","from","into","serialize","status","status","to_owned","try_from","try_into","type_id","vzip","Http","Method","Metrics","ParseMethodError","ReadState","Socket","State","WriteState","borrow","borrow","borrow_mut","borrow_mut","clone","clone_into","delete","eq","equivalent","equivalent","equivalent","equivalent","fmt","fmt","from","from","from","from_str","get","header","into","into","is_http","is_mutable","metrics","post","put","read","socket","to_owned","to_string","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","write","Error","Metrics","export","Binary","Boolean","Boolean","Hexadecimal","Hexadecimal","Http","IncorrectParamType","Integer","Integer","IntegerOverflow","InvalidParam","Json","Literal","Literal","MissingParam","RequestError","RequestParam","RequestParamType","RequestParamValue","RequestParams","TagMismatch","TaggedBase64","TaggedBase64","TaggedBase64","UnsupportedContentType","accept","as_blob","as_boolean","as_error_source","as_integer","as_string","as_tagged_base64","backtrace","blob_param","body_auto","body_bytes","body_json","boolean_param","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","description","deserialize","deserialize","eq","eq","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from_str","header","header","headers","in_current_span","integer_param","into","into","into","into","into","method","name","new","opt_blob_param","opt_boolean_param","opt_integer_param","opt_param","opt_string_param","opt_tagged_base64_param","param","param_type","param_type","parse","remote","serialize","serialize","source","string_param","tagged_base64_param","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","vzip","vzip","vzip","vzip","vzip","actual","actual","expected","expected","expected","name","param_type","reason","reason","reason","value","AppSpecific","Binary","Closed","Connection","IncorrectMethod","Json","Request","SocketError","UnsupportedMessageType","WebSockets","borrow","borrow","borrow_mut","borrow_mut","code","drop","fmt","fmt","from","from","from","from","from","from","header","into","into","into_stream","map_app_specific","poll_close","poll_flush","poll_next","poll_ready","start_send","status","to_string","try_from","try_from","try_into","try_into","try_poll_next","type_id","type_id","vzip","vzip","actual","expected","Accepted","BadGateway","BadRequest","Conflict","Continue","Created","EarlyHints","ExpectationFailed","FailedDependency","Forbidden","Found","GatewayTimeout","Gone","HttpVersionNotSupported","ImATeapot","ImUsed","InsufficientStorage","InternalServerError","LengthRequired","Locked","LoopDetected","MethodNotAllowed","MisdirectedRequest","MovedPermanently","MultiStatus","MultipleChoice","NetworkAuthenticationRequired","NoContent","NonAuthoritativeInformation","NotAcceptable","NotExtended","NotFound","NotImplemented","NotModified","Ok","OutOfRangeError","PartialContent","PayloadTooLarge","PaymentRequired","PermanentRedirect","PreconditionFailed","PreconditionRequired","ProxyAuthenticationRequired","RequestHeaderFieldsTooLarge","RequestTimeout","RequestedRangeNotSatisfiable","ResetContent","SeeOther","ServiceUnavailable","StatusCode","SwitchingProtocols","TemporaryRedirect","TooEarly","TooManyRequests","Unauthorized","UnavailableForLegalReasons","UnprocessableEntity","UnsupportedMediaType","UpgradeRequired","UriTooLong","VariantAlsoNegotiates","as_error_source","backtrace","borrow","borrow","borrow_mut","borrow_mut","canonical_reason","cause","clone","clone","clone_into","clone_into","description","deserialize","eq","eq","eq","equivalent","equivalent","equivalent","equivalent","fmt","fmt","fmt","fmt","from","from","from","from","from_i64","from_u64","hash","header","header","in_current_span","into","into","is_client_error","is_informational","is_redirection","is_server_error","is_success","serialize","source","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","Client","borrow","borrow_mut","from","get","into","new","post","request","setup_test","test_ws_client","test_ws_client_with_headers","try_from","try_into","type_id","vzip"],"q":[[0,"tide_disco"],[224,"tide_disco::api"],[338,"tide_disco::api::ApiError"],[345,"tide_disco::app"],[458,"tide_disco::app::AppError"],[459,"tide_disco::error"],[506,"tide_disco::healthcheck"],[536,"tide_disco::method"],[584,"tide_disco::metrics"],[587,"tide_disco::request"],[729,"tide_disco::request::RequestError"],[740,"tide_disco::socket"],[785,"tide_disco::socket::SocketError"],[787,"tide_disco::status"],[905,"tide_disco::testing"],[921,"std::path"],[922,"clap_builder::builder::command"],[923,"toml::value"],[924,"core::clone"],[925,"core::cmp"],[926,"config::config"],[927,"config::error"],[928,"core::result"],[929,"routefinder::router"],[930,"alloc::sync"],[931,"serde::de"],[932,"core::option"],[933,"core::fmt"],[934,"core::fmt"],[935,"clap_builder"],[936,"std::path"],[937,"url::parser"],[938,"clap_builder::util::id"],[939,"core::hash"],[940,"color_eyre::section"],[941,"core::fmt"],[942,"core::marker"],[943,"tide::response"],[944,"http_types::error"],[945,"url::host"],[946,"core::ops::range"],[947,"url::slicing"],[948,"core::ops::range"],[949,"tungstenite::error"],[950,"http::request"],[951,"clap_builder::builder::resettable"],[952,"url"],[953,"url::origin"],[954,"core::iter::traits::collect"],[955,"core::str::iter"],[956,"url::path_segments"],[957,"form_urlencoded"],[958,"url"],[959,"core::net::ip_addr"],[960,"core::net::socket_addr"],[961,"alloc::vec"],[962,"std::io::error"],[963,"core::ops::function"],[964,"core::any"],[965,"core::error"],[966,"vbs::version"],[967,"serde::ser"],[968,"std::backtrace"],[969,"alloc::borrow"],[970,"core::convert"],[971,"semver"],[972,"core::future::future"],[973,"alloc::boxed"],[974,"core::pin"],[975,"tide::server"],[976,"core::convert"],[977,"std::io::error"],[978,"core::ops::function"],[979,"tagged_base64"],[980,"core::convert"],[981,"tungstenite::protocol::frame::coding"],[982,"serde_json::error"],[983,"anyhow"],[984,"core::task::wake"],[985,"core::task::poll"],[986,"http::status"],[987,"http_types::status_code"],[988,"reqwest::async_impl::request"],[989,"async_tungstenite::async_std"],[990,"async_tungstenite"],[991,"http::header::name"]],"d":["","","","","Represents a TOML array","","Represents a TOML boolean","","Represents a TOML datetime","","Configuration keys for Tide Disco settings","","Represents a TOML float","","","","Represents a TOML integer","","","","","","","","","Number of times to poll before failing","Number of milliseconds to sleep between attempts","","","","","Represents a TOML string","Represents a TOML table","","A parsed URL record.","","If true, log in color. Otherwise, no color.","","","HTTP routes","","","","","","","","","","Return the serialization of this URL.","","","Return the authority of this URL as an ASCII string.","Server address","","","","","","","","","","","","","","Return whether this URL is a cannot-be-a-base URL, meaning …","Check api.toml for schema compliance errors","","","","","","","","","","","Compose the path to the application’s configuration file","Get the application configuration","Add routes from api.toml to the routefinder instance in …","","","Serialize with Serde using the internal representation of …","","If this URL has a host and it is a domain name (not an IP …","","","","","","","","","","","","","","Return this URL’s fragment identifier, if any.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Convert a directory name as std::path::Path into an URL in …","Convert a file name as std::path::Path into an URL in the …","","","Get the path to api.toml","","Return whether the URL has an ‘authority’, which can …","Equivalent to url.host().is_some().","","","","","","","Return a JSON expression with status 200 indicating the …","Return the parsed representation of the host for this URL. …","Return the string representation of the host (domain or IP …","","","","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","Return the serialization of this URL.","","Return whether the URL is special (has a special scheme)","Parse a string as an URL, with this URL as the base URL.","Load the web API or panic","Creates a relative URL if possible, with this URL as the …","Interfaces for methods of accessing to state.","Support for routes using the Prometheus metrics format.","","Return a default ParseOptions that can fully configure the …","","Return the origin of this URL (…","Parse an absolute URL from a string.","Parse an absolute URL from a string and add params to its …","","Return the password for this URL, if any, as a …","Return the path for this URL, as a percent-encoded ASCII …","Unless this URL is cannot-be-a-base, return an iterator of …","Return an object with methods to manipulate this URL’s …","Return the port number for this URL, if any.","Return the port number for this URL, or the default port …","Return this URL’s query string, if any, as a …","Parse the URL’s query string, if any, as …","Manipulate this URL’s query string, viewed as a sequence …","","","","Return the scheme of this URL, lower-cased, as an ASCII …","","Serialize with Serde using the internal representation of …","Change this URL’s fragment identifier.","Change this URL’s host.","Change this URL’s host to the given IP address.","Change this URL’s password.","Change this URL’s path.","Change this URL’s port number.","Change this URL’s query string.","Change this URL’s scheme.","Change this URL’s username.","An interface for asynchronous communication with clients, …","Resolve a URL’s host and port number to SocketAddr.","","","Assuming the URL is in the file scheme or similar, convert …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Return the username for this URL (typically the empty …","","","","","","","Wait for the server to respond to a connection request","","A description of an API.","An error encountered when parsing or constructing an Api.","Metadata used for describing and documenting an API.","","Version information about an API.","","","","","","","","","","","The version of this API.","","Register a handler for a route.","","","","","","","","","","","","","","","","","","Register a handler for a DELETE route.","","A description of this API.","","","","","","","","","","","","","","","","","","The version of the Tide Disco API specification format.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Create an Api by reading a TOML specification from a file.","Register a handler for a GET route.","","The heading preceding documentation of a route description.","The heading for documentation of a route.","The heading preceding documentation of route parameters.","The heading preceding documentation of all routes in this …","HTML to be appended to automatically generated …","HTML to be prepended to automatically generated …","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Register a handler for a METRICS route.","The name of this API.","Parse an API from a TOML specification.","Documentation to insert in the parameters section of a …","HTML formatting an entry in a table documenting the …","HTML closing a table documenting the parameters of a route.","HTML preceding the contents of a table documenting the …","Register a handler for a POST route.","Register a handler for a PUT route.","HTML formatting the path of a route.","","","Register a handler for a SOCKET route.","","The format version of the TOML specification used to load …","Register a uni-directional handler for a SOCKET route.","","","","","","","","","","","","","","","","","","","","","Set the health check handler for this API.","Serve the contents of dir at the URL /public/{{NAME}}.","Set the API version.","","","","","","","","","A tide-disco server application.","An error encountered while building an App.","The health status of an application.","Version information about an application.","The Listener trait represents an implementation of http …","What listener are we converting into?","RAII guard to ensure a module is registered after it is …","","ToListener represents any type that can be converted into a","Start accepting incoming connections. This method must be …","The version of this application.","","","Bind the listener. This starts the listening process by …","","","","","","","","","","","","","","","","","","","","","","","The version of the Tide Disco server framework.","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Check the health of each registered module in response to …","","Expose information about the connection. This should …","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Create and register an API module.","Check the health of the named module.","The status of each registered module, indexed by version.","The supported versions of each module registered with this …","Register this module with the linked app.","Register an API module.","","","Serve the App asynchronously.","","","The status of the overall application.","Transform self into a Listener. Unless self is already …","","","","","","","","","","","","","","","","","","","","Get the version of this application.","","","","","","Create a new App with a given state.","Set the application version.","","Errors which can be serialized in a response body.","The simplest possible implementation of Error.","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","","","","","","","","","","","Calls U::from(self).","","","","","","","","","","","","","","","A response to a healthcheck endpoint.","Common health statuses of an application.","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Calls U::from(self).","","The status of this health check.","","","","","","","","","","","A state which allows read access.","","The type of state which this type allows a caller to read.","A state which allows exclusive, write access.","","","","","","","The HTTP DELETE method.","","","","","","","","Returns the argument unchanged.","","Returns the argument unchanged.","","The HTTP GET method.","","Calls U::from(self).","Calls U::from(self).","Check if a method is a standard HTTP method.","Check if a request method implies mutable access to the …","The Tide Disco METRICS method.","The HTTP POST method.","The HTTP PUT method.","Do an operation with immutable access to the state.","The Tide Disco SOCKET method.","","","","","","","","","","","Do an operation with mutable access to the state.","","","","","","","","","","","","","","","","","","","","","","","Parameters passed to a route handler.","","","","","","The Accept header of this request.","","","","","","","","Get the value of a named parameter and convert it to a …","Deserialize the body of a request.","","","Get the value of a named parameter and convert it to a bool…","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","The headers of the incoming request.","","Get the value of a named parameter and convert it to an …","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","The Method used to dispatch the request.","","Parse a parameter from a Request.","Get the value of a named optional parameter and convert it …","Get the value of a named optional parameter and convert it …","Get the value of a named optional parameter and convert it …","Get the value of a named optional parameter.","Get the value of a named optional parameter and convert it …","Get the value of a named optional parameter and convert it …","Get the value of a named parameter.","","","","Get the remote address for this request.","","","","Get the value of a named parameter and convert it to a …","Get the value of a named parameter and convert it to …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","A connection facilitating bi-directional, asynchronous …","","","","An error returned by a socket handler.","","","","","","","","","","","Returns the argument unchanged.","","","","Returns the argument unchanged.","","","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","202 Accepted","502 Bad Gateway","400 Bad Request","409 Conflict","100 Continue","201 Created","103 Early Hints","417 Expectation Failed","424 Failed Dependency","403 Forbidden","302 Found","504 Gateway Timeout","410 Gone","505 HTTP Version Not Supported","418 I’m a teapot","226 Im Used","507 Insufficient Storage","500 Internal Server Error","411 Length Required","423 Locked","508 Loop Detected","405 Method Not Allowed","421 Misdirected Request","301 Moved Permanently","207 Multi-Status","300 Multiple Choice","511 Network Authentication Required","204 No Content","203 Non Authoritative Information","406 Not Acceptable","510 Not Extended","404 Not Found","501 Not Implemented","304 Not Modified","200 Ok","","206 Partial Content","413 Payload Too Large","402 Payment Required","308 Permanent Redirect","412 Precondition Failed","428 Precondition Required","407 Proxy Authentication Required","431 Request Header Fields Too Large","408 Request Timeout","416 Requested Range Not Satisfiable","205 Reset Content","303 See Other","503 Service Unavailable","Serializable HTTP status code.","101 Switching Protocols","307 Temporary Redirect","425 Too Early","429 Too Many Requests","401 Unauthorized","451 Unavailable For Legal Reasons","422 Unprocessable Entity","415 Unsupported Media Type","426 Upgrade Required","414 URI Too Long","506 Variant Also Negotiates","","","","","","","The canonical reason for a given status code","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","","","","","Calls U::from(self).","Calls U::from(self).","Returns true if the status code is the 4xx range.","Returns true if the status code is 1xx range.","Returns true if the status code is the 3xx range.","Returns true if the status code is the 5xx range.","Returns true if the status code is the 2xx range.","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","Calls U::from(self).","","","","","","","","","",""],"i":[0,0,0,0,145,5,145,11,145,0,0,0,145,0,11,0,145,11,11,0,0,0,0,0,0,0,0,0,5,0,5,145,145,11,0,0,26,4,0,26,4,0,0,39,9,4,3,4,5,3,26,26,3,26,4,3,26,4,5,9,11,3,26,4,5,9,11,3,0,3,5,9,11,3,5,9,11,3,3,0,0,0,3,5,3,4,3,3,3,3,3,3,0,3,3,26,4,5,5,11,3,3,26,4,5,9,11,26,26,3,3,3,11,0,26,3,3,3,3,5,39,9,0,0,3,3,0,3,3,3,3,0,3,26,4,5,9,11,3,3,3,3,3,3,11,3,3,0,3,0,0,11,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,0,39,9,3,3,3,3,3,3,3,3,3,3,3,3,0,3,0,0,3,3,3,5,9,11,3,5,3,3,26,4,5,9,11,11,3,26,4,5,9,11,3,26,4,5,9,11,26,26,3,3,26,4,5,9,11,0,76,0,0,0,76,0,76,76,76,76,76,76,76,76,76,76,82,76,75,76,76,82,83,75,76,82,83,75,76,76,82,83,76,82,83,83,75,76,83,82,83,76,82,76,76,76,76,82,82,82,82,76,76,82,83,75,83,76,82,83,75,75,75,76,83,83,83,83,83,83,76,76,82,83,75,75,83,75,83,83,83,83,75,75,83,82,83,75,76,82,75,76,82,83,76,76,82,83,75,76,82,83,75,76,82,83,75,76,82,83,75,75,75,75,146,146,147,148,148,149,150,99,0,0,0,0,0,110,0,99,0,95,102,99,99,95,103,106,99,101,102,103,106,99,101,102,99,99,101,102,99,101,102,103,103,99,101,102,102,103,99,101,102,99,99,99,99,101,101,101,101,102,102,102,102,106,99,99,101,102,103,106,99,101,102,99,106,99,95,103,106,99,101,102,106,106,101,102,103,106,101,102,106,99,101,101,110,99,101,102,99,103,106,99,101,102,103,106,99,101,102,103,106,99,101,102,106,103,106,99,101,102,106,106,151,0,0,113,113,113,113,104,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,104,104,104,104,104,104,113,113,113,104,113,113,113,104,113,113,113,113,113,113,113,113,116,0,0,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,93,116,116,116,116,116,116,117,0,117,0,0,117,85,0,152,117,152,117,117,117,117,117,117,117,117,117,117,117,152,117,117,117,117,117,152,117,117,117,117,117,117,85,117,117,117,152,117,152,117,152,117,152,117,84,86,0,86,115,123,128,123,128,115,115,123,128,115,115,115,123,128,115,0,0,0,0,0,115,115,123,128,115,79,123,123,115,123,123,123,115,79,79,79,79,79,115,79,123,128,129,115,79,123,128,129,115,115,79,123,128,129,115,79,123,128,129,115,115,128,123,128,123,123,123,123,128,128,128,128,115,115,79,123,128,128,129,115,79,123,128,129,128,115,128,79,115,79,115,79,123,128,129,79,129,123,79,79,79,79,79,79,79,123,129,123,79,115,128,115,79,79,115,79,123,128,129,115,128,115,79,123,128,128,129,115,79,123,128,129,115,79,123,128,129,115,79,123,128,129,153,154,153,155,154,156,157,158,159,157,155,114,114,114,0,114,114,114,0,114,114,91,114,91,114,114,91,114,114,91,114,114,114,114,114,114,91,114,91,114,91,91,91,91,91,114,114,91,114,91,114,91,91,114,91,114,160,160,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,0,111,111,111,111,111,111,111,111,111,111,111,111,111,0,111,111,111,111,111,111,111,111,111,111,111,136,136,111,136,111,136,111,136,111,136,111,136,136,111,111,111,111,111,111,111,111,111,111,136,136,111,111,111,136,111,111,111,111,136,136,111,136,111,111,111,111,111,111,136,111,136,111,136,111,111,136,111,136,111,136,111,136,0,140,140,140,140,140,140,140,140,0,0,0,140,140,140,140],"f":"``````````````````````````````````````````{{bb}d}```{fb}{hb}{jb}2{ll}03``{ce{}{}}00000000000{fn}{A`n}{ff}{jj}{{{Ab{c}}}{{Ab{c}}}Ad}{AfAf}{{ce}Ah{}{}}000{{ff}Aj}{{ce}Aj{}{}}>{{bb{An{{Al{bb}}}}}{{Bd{B`Bb}}}}{A`{{Bj{{Bh{Bf}}}}}}{c{{Bd{f}}}Bl}{c{{Bd{j}}}Bl}1`{f{{Bn{b}}}}{{ff}n}{{ce}n{}{}}000`{{fC`}{{Bd{AhCb}}}}0{{CdC`}Cf}{{hC`}Cf}{{jC`}Cf}{{jC`}{{Bd{AhCb}}}}{{AfC`}Cf}8{cc{}}00000{Ch{{Bd{CdCj}}}}0{c{{Bd{fAh}}}{{Cn{Cl}}}}0{b{{Bd{fD`}}}}{b{{Bd{Afc}}}{}}{bd}{{}{{Bn{Db}}}}{fn}0{{fc}AhDd}{{ce}{{Df{eg}}}{}{DhDjDl}{}}0```{{{E`{Dn}}}{{Bd{EbEd}}}}{f{{Bn{{Ef{b}}}}}}{f{{Bn{b}}}}`{{fEh}b}{{f{El{Ej}}}b}{{f{En{Ej}}}b}{{f{F`{Ej}}}b}{nAh}{ce{}{}}00000{f{{Bd{{Fb{Ah}}Fd}}}}0{f{{Bd{{Ff{Ah}}Fh}}}}0{c{{Fl{Fj}}}{}}{fFj}{Afn}{fn}{{fb}{{Bd{fD`}}}}{ClA`}{{ff}{{Bn{Fj}}}}``{{bAf}Af}{{}Fn}{bd}{fG`}{b{{Bd{fD`}}}}{{bc}{{Bd{fD`}}}Gb}{{ff}{{Bn{Aj}}}}{f{{Bn{b}}}}{fb}{f{{Bn{{Gf{Gd}}}}}}{f{{Bd{GhAh}}}}{f{{Bn{Gj}}}}04{fGl}{f{{H`{Gn}}}}```5{{fc}BdHb}0{{f{Bn{b}}}Ah}{{f{Bn{b}}}{{Bd{AhD`}}}}{{fHd}{{Bd{AhAh}}}}{{f{Bn{b}}}{{Bd{AhAh}}}}{{fb}Ah}{{f{Bn{Gj}}}{{Bd{AhAh}}}}5{{fb}{{Bd{AhAh}}}}0`{{fc}{{Bd{{Hh{Hf}}Hj}}}{{Hn{}{{Hl{{Bn{Gj}}}}}}}}``{f{{Bd{dAh}}}}{f{{Bd{Hj}}}}{ce{}{}}000{cFj{}}0{c{{Bd{e}}}{}{}}{b{{Bd{f}}}}11111{b{{Bd{Afc}}}{}}222222{cI`{}}00000{{CdCh}{{Bd{AhCj}}}}0{fb}777777{{fIbIb}Ah}`````````````````{cId{}}{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDl}{}{DjDlIj}Il{DjDl{Hn{Inc}{{Hl{{J`{{Bd{ie}}}}}}}}}}{Ih{{Bn{Jb}}}};;;;;;;;{Ih{{Bn{Id}}}}{IhIh}{JdJd}{JfJf}{{ce}Ah{}{}}00{{}Jf}{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDlJh}{}{DjDlIj}Il{DjDl{Hn{In}{{Hl{{J`{{Bd{ie}}}}}}}}}}{Ihb}`{c{{Bd{Jd}}}Bl}{c{{Bd{Jf}}}Bl}{{IhIh}n}{{JdJd}n}{{ce}n{}{}}0000000{{IhC`}Cf}0{{JdC`}Cf}{{JfC`}Cf}{{{If{ceg}}C`}Cf{}{}{}}`{cc{}}000{c{{Bd{{If{egi}}Ih}}}{{Cn{Cl}}}{}{}Ij}{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDlJj}{}{DjDlIj}Il{DjDl{Hn{In}{{Hl{{J`{{Bd{ie}}}}}}}}}}{{ce}{{Df{eg}}}{}{DhDjDl}{}}``````{c{}{}}{ce{}{}}000{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDlJj}{}{DjDlIj}{AdJl}{DjDl{Hn{In}{{Hl{{J`{{Bd{{Jn{i}}e}}}}}}}}}}`{c{{Bd{{If{egi}}Ih}}}{{K`{A`}}}{}{}Ij}````{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDlJh}{}{DjDlIj}Il{DjDl{Hn{In}{{Hl{{J`{{Bd{ie}}}}}}}}}}0`{{Jdc}BdHb}{{Jfc}BdHb}{{{If{ceg}}bm}{{Bd{{If{ceg}}Ih}}}{DjDl}{DjDh}Ij{IlKb}Kd{DjDl{Hn{In{Kf{ikeg}}c}{{Hl{{J`{{Bd{Ahe}}}}}}}}}}{Ih{{Bn{Id}}}}`{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDl}{DjDh}{DjDlIj}{IlDjDl}{DjDl{Hn{Inc}{{Hl{{Kh{{Bd{ie}}}}}}}}}}888{cFj{}}{c{{Bd{e}}}{}{}}0000000{cI`{}}000;;;;{{{If{ceg}}k}{{If{ceg}}}{DjDl}{}{DjDlIj}Kj{DjDl{Hn{c}{{Hl{{J`{i}}}}}}}}{{{If{ceg}}d}{{If{ceg}}}{}{}Ij}{{{If{ceg}}Kl}{{If{ceg}}}{}{}Ij}`````````````````{Kn{{Ld{{Lb{L`}}}}}}`{cId{}}{Lf{{Bn{Jb}}}}{{Kn{Lh{c}}}{{Ld{{Lb{L`}}}}}{DjDl}}{ce{}{}}000000000{Lf{{Bn{Id}}}}{LfLf}{LjLj}{LlLl}{{ce}Ah{}{}}00{{{Ln{cgei}}}k{DjDl}{DjDl}{M`{Mb{e}}}Ij{}}0{Lfb}{c{{Bd{Lj}}}Bl}{c{{Bd{Ll}}}Bl}`{{{Ln{cgei}}}Ah{DjDl}{DjDl}{M`{Mb{e}}}Ij}{{LfLf}n}{{LjLj}n}{{LlLl}n}{{ce}n{}{}}00000000000{{{Md{ce}}C`}CfMfMf}{{LfC`}Cf}0{{LjC`}Cf}{{LlC`}Cf}{cc{}}0000{{ce}{{Df{eg}}}{}{DhDjDl}{}}{{{Md{ce}}Inc}Lj{DjDl}{}}{c{}{}}{Kn{{Hh{Mh}}}}{ce{}{}}0000{{{Md{cg}}bi}{{Bd{{Ln{cgek}}Lf}}}{DjDl}{DjDl}{M`{Mb{e}}}{{K`{A`}}}Ij}{{{Md{ce}}Incb{Bn{Ib}}}{{Bn{Eb}}}{DjDl}{}}``{{{Ln{cgei}}}{{Bd{AhLf}}}{DjDl}{DjDl}{M`{Mb{e}}}Ij}{{{Md{cg}}b{If{cei}}}{{Bd{{Md{cg}}Lf}}}{DjDl}{DjDl}{M`{Mb{e}}}Ij}{{Ljc}BdHb}{{Llc}BdHb}{{{Md{ce}}gi}{{Mj{Ah}}}{DjDl}M`{{Ml{{Bj{{Md{ce}}}}}}}Ij}{Lf{{Bn{Id}}}}{LjMn}`{{{Ml{}{{N`{c}}}}}{{Bd{cHj}}}{{Kn{e}}}{AdDjDl}}:::{cFj{}}{c{{Bd{e}}}{}{}}000000000{cI`{}}0000{{{Md{ce}}}Ll{DjDl}{}}>>>>>{c{{Md{ce}}}{DjDl}{}}{{{Md{ce}}Kl}{{Md{ce}}}{DjDl}{}}```{cId{}}{Nb{{Bn{Jb}}}}{ce{}{}}0{{MnFj}M`}{{MnFj}Nb}{Nb{{Bn{Id}}}}{NbNb}{{ce}Ah{}{}}{Nbb}{c{{Bd{Nb}}}Bl}{{NbNb}n}{{ce}n{}{}}000{{NbC`}Cf}0{HjNb}{cc{}}{{{Nd{c}}}NbDh}{NfNb}{BbNb}{BbM`}{HjM`}{NfM`}{{{`{c}}}M`Dh}{EdM`}{{{Nd{c}}}M`Dh}{{ce}{{Df{eg}}}{}{DhDjDl}{}}{c{}{}}{ce{}{}}{M`Ed}`{{Nbc}BdHb}{Nb{{Bn{Id}}}}{M`Mn}{NbMn}`5{cFj{}}{c{{Bd{e}}}{}{}}0{cI`{}}8````````88{NhNh}{{ce}Ah{}{}}{{}Nh}{c{{Bd{Nh}}}Bl}{{NhNh}n}{{ce}n{}{}}000{{NhC`}Cf}{cc{}}{ce{}{}}{{Nhc}BdHb}{KjMn}{NhMn}3==<3````````3333{NjNj};{{}Nj}{{NjNj}n}9999{{NjC`}Cf}08{NlNj}9{b{{Bd{Njc}}}{}}4{{ce}{{Df{eg}}}{}{DhDjDl}{}}::{Njn}0666{{{Jj{}{{Nn{c}}}}g}{{Ld{{Lb{L`}}}}}{}{}{Dj{O`{c}{{Hl{{J`{e}}}}}}}}7<{cFj{}}{c{{Bd{e}}}{}{}}000{cI`{}}0??{{Jhg}{{Ld{{Lb{L`}}}}}{}{}{Dj{O`{c}{{Hl{{J`{e}}}}}}}}``{{{Jl{}{{Ob{c}}}}}{{Bd{Fjc}}}{MfId}}`````````````````````````{In{{Bd{OdNf}}}}{Of{{Bd{cNf}}}{{Oj{Oh}}}}{Of{{Bd{nNf}}}}{cId{}}{Of{{Bd{cNf}}}{{Oj{Ol}}}}{Of{{Bd{bNf}}}}{Of{{Bd{OhNf}}}}{Nf{{Bn{Jb}}}}{{Inc}{{Bd{eNf}}}{KbDh}{{Oj{Oh}}}}{{Inc}{{Bd{eNf}}}IjKd}{In{{Hh{On}}}}{In{{Bd{cNf}}}Kd}{{Inc}{{Bd{nNf}}}{KbDh}}{ce{}{}}000000000{Nf{{Bn{Id}}}}{NfNf}{InIn}{OfOf}{A`A`}{AbAb}{{ce}Ah{}{}}0000{Nfb}{c{{Bd{Nf}}}Bl}{c{{Bd{A`}}}Bl}{{OfOf}n}{{A`A`}n}{{ce}n{}{}}0000000{{NfC`}Cf}0{{InC`}Cf}{{OfC`}Cf}{{A`C`}{{Bd{AhCb}}}}{{A`C`}Cf}{{AbC`}Cf}{cc{}}0000{b{{Bd{A`c}}}{}}{{ce}{{Df{eg}}}{}{DhDjDl}{}}0{InAd}{c{}{}}{{Inc}{{Bd{eNf}}}{KbDh}{{Oj{Ol}}}}{ce{}{}}0000{InNj}`{{{E`{c}}Ab}{{Bd{{Bn{Of}}Nf}}}{}}{{Inc}{{Bd{{Bn{e}}Nf}}}{KbDh}{{Oj{Oh}}}}{{Inc}{{Bd{{Bn{n}}Nf}}}{KbDh}}{{Inc}{{Bd{{Bn{e}}Nf}}}{KbDh}{{Oj{Ol}}}}{{Inc}{{Bn{Of}}}{KbDh}}{{Inc}{{Bd{{Bn{b}}Nf}}}{KbDh}}{{Inc}{{Bd{{Bn{Oh}}Nf}}}{KbDh}}{{Inc}{{Bd{OfNf}}}{KbDh}}{OfA`}`{{bAb}{{Bd{OfNf}}}}{In{{Bn{b}}}}{{Nfc}BdHb}{{A`c}BdHb}{Nf{{Bn{Id}}}}{{Inc}{{Bd{bNf}}}{KbDh}}{{Inc}{{Bd{OhNf}}}{KbDh}}{ce{}{}}0000{cFj{}}0{c{{Bd{e}}}{}{}}000{b{{Bd{A`c}}}{}}111111{cI`{}}000044444`````````````````````4444{{{Nd{c}}}Af{}}{{{Kf{cegi}}}AhKb{}{}Ij}{{{Nd{c}}C`}CfMf}{{{Nd{c}}C`}CfDh}{cc{}}{Fh{{Nd{c}}}{}}{Ah{{Nd{c}}}{}}{Nf{{Nd{c}}}{}}3{Aj{{Nd{c}}}{}}{{ce}{{Df{eg}}}{}{DhDjDl}{}}>>>{{{Nd{c}}g}{{Nd{e}}}{}{}{{Hn{c}{{Hl{e}}}}}}{{{Ld{{Kf{cegi}}}}Al}{{An{{Bd{Ahk}}}}}{IlKb}{}{}Ij{}}0{{{Ld{{Kf{cegi}}}}Al}{{An{{Bn{k}}}}}KbKd{}Ij{}}1{{{Ld{{Kf{cegi}}}}c}{{Bd{Ahk}}}{IlKb}{}{}Ij{}}{{{Nd{c}}}Mn{}}{cFj{}}{c{{Bd{e}}}{}{}}000{{{Ld{c}}Al}{{An{{Bn{Bd}}}}}{}}{cI`{}}0{ce{}{}}0```````````````````````````````````````````````````````````````{cId{}}{AA`{{Bn{Jb}}}}2222{Mnb}{AA`{{Bn{Id}}}}{MnMn}{AA`AA`}{{ce}Ah{}{}}0{AA`b}{c{{Bd{Mn}}}Bl}{{MnMn}n}{{MnAAb}n}{{MnAAd}n}{{ce}n{}{}}000{{MnC`}Cf}0{{AA`C`}Cf}0{AAbMn}{AAdMn}{cc{}}0{AAf{{Bn{Mn}}}}{Ib{{Bn{Mn}}}}{{Mnc}AhDd}{{ce}{{Df{eg}}}{}{DhDjDl}{}}0{c{}{}}{ce{}{}}0{Mnn}0000{{Mnc}BdHb}{AA`{{Bn{Id}}}}33{cFj{}}0{Gj{{Bd{Mnc}}}{}}{c{{Bd{e}}}{}{}}000{cI`{}}077`77={{AAhb}AAj}8{fAAh}1{{AAhNlb}AAj}{{}Ah}{f{{AAn{AAl}}}}{{f{An{{Al{AB`b}}}}}{{AAn{AAl}}}}776=","c":[143],"p":[[1,"str"],[5,"PathBuf",921],[5,"Url",0],[6,"DiscoKey",0],[6,"HealthStatus",0],[5,"Command",922],[1,"bool"],[6,"Value",923],[5,"ServerState",0],[10,"Clone",924],[6,"UrlSegment",0],[1,"unit"],[6,"Ordering",925],[1,"tuple"],[1,"slice"],[5,"Config",926],[6,"ConfigError",927],[6,"Result",928],[1,"usize"],[5,"Router",929],[5,"Arc",930],[10,"Deserializer",931],[6,"Option",932],[5,"Formatter",933],[5,"Error",933],[5,"DiscoArgs",0],[8,"Result",933],[5,"ArgMatches",934],[8,"Error",935],[5,"Path",921],[10,"AsRef",936],[6,"ParseError",937],[5,"Id",938],[10,"Hasher",939],[5,"IndentedSection",940],[10,"Display",933],[10,"Send",941],[10,"Sync",941],[8,"AppServerState",0],[5,"Request",942],[5,"Response",943],[5,"Error",944],[6,"Host",945],[5,"RangeFull",946],[6,"Position",947],[5,"RangeFrom",946],[5,"RangeTo",946],[5,"Range",946],[5,"Request",948],[6,"Error",949],[5,"Request",948],[6,"Error",949],[5,"String",950],[6,"Resettable",951],[5,"ParseOptions",952],[6,"Origin",953],[10,"IntoIterator",954],[1,"char"],[5,"Split",955],[5,"PathSegmentsMut",956],[1,"u16"],[5,"Parse",957],[5,"UrlQuery",952],[5,"Serializer",957],[10,"Serializer",958],[6,"IpAddr",959],[6,"SocketAddr",960],[5,"Vec",961],[5,"Error",962],[17,"Output"],[10,"Fn",963],[5,"TypeId",964],[1,"u64"],[10,"Error",965],[5,"Api",224],[6,"ApiError",224],[10,"StaticVersionType",966],[10,"Serialize",958],[5,"RequestParams",587],[8,"BoxFuture",967],[5,"Backtrace",968],[5,"ApiVersion",224],[5,"ApiMetadata",224],[10,"WriteState",536],[10,"ReadState",536],[10,"Metrics",584],[6,"Cow",969],[10,"Into",936],[10,"Sized",941],[10,"DeserializeOwned",931],[5,"Connection",740],[8,"BoxStream",970],[10,"HealthCheck",506],[5,"Version",971],[10,"Listener",345],[10,"Future",972],[5,"Box",973],[5,"Pin",974],[6,"AppError",345],[5,"Server",975],[5,"AppHealth",345],[5,"AppVersion",345],[5,"Module",345],[10,"Error",459],[10,"From",936],[5,"App",345],[10,"Debug",933],[5,"ListenInfo",976],[8,"Result",962],[10,"ToListener",345],[6,"StatusCode",787],[17,"Listener"],[5,"ServerError",459],[6,"SocketError",740],[6,"RequestError",587],[6,"HealthStatus",506],[6,"Method",536],[6,"Method",977],[17,"State"],[10,"FnOnce",963],[17,"Error"],[5,"Accept",978],[6,"RequestParamValue",587],[5,"TaggedBase64",979],[10,"TryFrom",936],[1,"u128"],[1,"u8"],[6,"RequestParamType",587],[5,"RequestParam",587],[5,"Headers",980],[6,"CloseCode",981],[5,"Error",982],[5,"Error",983],[5,"Context",984],[6,"Poll",985],[5,"OutOfRangeError",787],[5,"StatusCode",986],[6,"StatusCode",987],[1,"i64"],[5,"Client",905],[5,"RequestBuilder",988],[8,"ConnectStream",989],[5,"WebSocketStream",990],[5,"HeaderName",991],[8,"AppState",0],[15,"IncorrectMethod",338],[15,"CannotReadToml",338],[15,"AmbiguousRoutes",338],[15,"Route",338],[15,"InvalidMetaTable",338],[15,"Api",458],[5,"ParseMethodError",536],[15,"IncorrectParamType",729],[15,"TagMismatch",729],[15,"IntegerOverflow",729],[15,"MissingParam",729],[15,"InvalidParam",729],[15,"TaggedBase64",729],[15,"Http",729],[15,"IncorrectMethod",785]],"b":[[93,"impl-Display-for-Url"],[94,"impl-Debug-for-Url"],[97,"impl-Debug-for-HealthStatus"],[98,"impl-Display-for-HealthStatus"],[127,"impl-Index%3CRangeFull%3E-for-Url"],[128,"impl-Index%3CRangeFrom%3CPosition%3E%3E-for-Url"],[129,"impl-Index%3CRangeTo%3CPosition%3E%3E-for-Url"],[130,"impl-Index%3CRange%3CPosition%3E%3E-for-Url"],[138,"impl-IntoClientRequest-for-%26Url"],[139,"impl-IntoClientRequest-for-Url"],[140,"impl-IntoClientRequest-for-Url"],[141,"impl-IntoClientRequest-for-%26Url"],[275,"impl-Debug-for-ApiError"],[276,"impl-Display-for-ApiError"],[400,"impl-Display-for-AppError"],[401,"impl-Debug-for-AppError"],[477,"impl-Display-for-ServerError"],[478,"impl-Debug-for-ServerError"],[479,"impl-From%3CError%3E-for-ServerError"],[481,"impl-From%3CSocketError%3CE%3E%3E-for-ServerError"],[482,"impl-From%3CRequestError%3E-for-ServerError"],[483,"impl-From%3CConfigError%3E-for-ServerError"],[556,"impl-Debug-for-Method"],[557,"impl-Display-for-Method"],[659,"impl-Display-for-RequestError"],[660,"impl-Debug-for-RequestError"],[663,"impl-Display-for-RequestParamType"],[664,"impl-Debug-for-RequestParamType"],[756,"impl-Debug-for-SocketError%3CE%3E"],[757,"impl-Display-for-SocketError%3CE%3E"],[759,"impl-From%3CError%3E-for-SocketError%3CE%3E"],[760,"impl-From%3CError%3E-for-SocketError%3CE%3E"],[761,"impl-From%3CRequestError%3E-for-SocketError%3CE%3E"],[763,"impl-From%3CError%3E-for-SocketError%3CE%3E"],[862,"impl-PartialEq-for-StatusCode"],[863,"impl-PartialEq%3CStatusCode%3E-for-StatusCode"],[864,"impl-PartialEq%3CStatusCode%3E-for-StatusCode"],[869,"impl-Display-for-StatusCode"],[870,"impl-Debug-for-StatusCode"],[871,"impl-Debug-for-OutOfRangeError"],[872,"impl-Display-for-OutOfRangeError"],[873,"impl-From%3CStatusCode%3E-for-StatusCode"],[874,"impl-From%3CStatusCode%3E-for-StatusCode"]]}]\ +["tide_disco",{"doc":"Tide Disco is a web server framework with built-in …","t":"EEIIPPPPPFGEPGPIPPPEEEEEESSFPEPPPPFGOPCOPCHOOPNNNNNNNOPNNNNNNNNNNNNNHNNNNNNNNNNHHHNNNPNNNNNNCNNNNNNNNNNNNNNNNNNNNHNNNNNNOOCHNNENNNNHNNNNNNNNNNNNNNNQHNCCNNHNNNNNNNNNNNNNCOONNNNNNNNNNNNCNCCNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNHPFGFPFPPPPPPPPPPONNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNONNNNNNNOOOOOONNNNNNONOOOONNONNNNONNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOPFGFFPKRFKMONNMNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNMNNNNNNNOONNNNNNNOMNNNNNNNNNNNNNNNNNNNNNNNNNNNOOKFNNNNMNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNMNONNNNNNPKGPPPPPNNNNNNNNNNNNNNNMNNNNNNPGPFKPRKNNNNNNNNNNNNNNNNNNNNNNNNNNNMNNNNNNNNNNNMRKMPPPPPPPPPPPPPPPGFGGFPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOPPPFPPPGPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPFPPPPPPPPPPPPPGPPPPPPPPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNFNNNNNNNNHHHNNNN","n":["Api","App","AppServerState","AppState","Array","Available","Boolean","Boolean","Datetime","DiscoArgs","DiscoKey","Error","Float","HealthStatus","Hexadecimal","Html","Integer","Integer","Literal","Method","RequestError","RequestParam","RequestParamType","RequestParamValue","RequestParams","SERVER_STARTUP_RETRIES","SERVER_STARTUP_SLEEP_MS","ServerState","Starting","StatusCode","Stopping","String","Table","TaggedBase64","Url","UrlSegment","ansi_color","ansi_color","api","api_toml","api_toml","app","app_api_path","app_state","app_state","app_toml","as_ref","as_ref","as_ref","as_str","augment_args","augment_args_for_update","authority","base_url","base_url","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cannot_be_a_base","check_api","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","cmp","compare","compose_config_path","compose_settings","configure_router","deserialize","deserialize","deserialize_internal","disco_toml","domain","eq","equivalent","equivalent","equivalent","equivalent","error","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fragment","from","from","from","from","from","from","from_arg_matches","from_arg_matches_mut","from_directory_path","from_file_path","from_str","from_str","get_api_path","group_id","has_authority","has_host","hash","header","header","health_status","health_status","healthcheck","healthcheck","host","host_str","http","index","index","index","index","init_logging","into","into","into","into","into","into","into_client_request","into_client_request","into_client_request","into_client_request","into_resettable","into_string","is_bound","is_special","join","join","load_api","make_relative","method","metrics","new","options","org_data_path","origin","parse","parse_with_params","partial_cmp","password","path","path_segments","path_segments_mut","port","port_or_known_default","query","query_pairs","query_pairs_mut","request","router","router","scheme","serialize","serialize_internal","set_fragment","set_host","set_ip_host","set_password","set_path","set_port","set_query","set_scheme","set_username","socket","socket_addrs","status","testing","to_file_path","to_listener","to_owned","to_owned","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","update_from_arg_matches","update_from_arg_matches_mut","username","vzip","vzip","vzip","vzip","vzip","vzip","wait_for_server","AmbiguousRoutes","Api","ApiError","ApiMetadata","ApiMustBeTable","ApiVersion","CannotReadToml","HandlerAlreadyRegistered","IncorrectMethod","InvalidFormatVersion","InvalidMetaTable","MissingFormatVersion","MissingRoutesTable","Route","RoutesMustBeTable","UndefinedRoute","api_version","as_error_source","at","backtrace","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","clone","clone","clone","clone_into","clone_into","clone_into","default","delete","description","description","deserialize","deserialize","eq","eq","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","fmt","fmt","fmt","fmt","fmt","format_version","from","from","from","from","from_file","get","header","heading_description","heading_entry","heading_parameters","heading_routes","html_bottom","html_top","in_current_span","into","into","into","into","metrics","name","new","parameter_none","parameter_row","parameter_table_close","parameter_table_open","post","put","route_path","serialize","serialize","socket","source","spec_version","stream","to_owned","to_owned","to_owned","to_string","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","vzip","vzip","vzip","vzip","with_health_check","with_public","with_version","actual","expected","reason","route1","route2","source","source","Api","App","AppError","AppHealth","AppVersion","Dispatch","Listener","Listener","Module","ToListener","accept","app_version","as_error_source","backtrace","bind","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","clone","clone","clone","clone_into","clone_into","clone_into","deref","deref_mut","description","deserialize","deserialize","disco_version","drop","eq","eq","eq","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","header","health","in_current_span","info","into","into","into","into","into","module","module_health","modules","modules","register","register_module","serialize","serialize","serve","source","status","status","to_listener","to_owned","to_owned","to_owned","to_string","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","version","vzip","vzip","vzip","vzip","vzip","with_state","with_version","source","source","Error","ServerError","as_error_source","backtrace","borrow","borrow_mut","catch_all","catch_all","cause","clone","clone_into","description","deserialize","eq","equivalent","equivalent","equivalent","equivalent","fmt","fmt","from","from","from","from","from","from_config_error","from_io_error","from_request_error","from_route_error","from_server_error","from_socket_error","header","in_current_span","into","into_tide_error","message","serialize","source","status","status","status","to_owned","to_string","try_from","try_into","type_id","vzip","Available","HealthCheck","HealthStatus","Initializing","ShuttingDown","TemporarilyUnavailable","Unavailabale","Unhealthy","borrow","borrow_mut","clone","clone_into","default","deserialize","eq","equivalent","equivalent","equivalent","equivalent","fmt","from","into","serialize","status","status","to_owned","try_from","try_into","type_id","vzip","Http","Method","Metrics","ParseMethodError","ReadState","Socket","State","WriteState","borrow","borrow","borrow_mut","borrow_mut","clone","clone_into","delete","eq","equivalent","equivalent","equivalent","equivalent","fmt","fmt","from","from","from","from_str","get","header","into","into","is_http","is_mutable","metrics","post","put","read","socket","to_owned","to_string","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","write","Error","Metrics","export","Binary","Boolean","Boolean","Hexadecimal","Hexadecimal","Http","IncorrectParamType","Integer","Integer","IntegerOverflow","InvalidParam","Json","Literal","Literal","MissingParam","RequestError","RequestParam","RequestParamType","RequestParamValue","RequestParams","TagMismatch","TaggedBase64","TaggedBase64","TaggedBase64","UnsupportedContentType","accept","as_blob","as_boolean","as_error_source","as_integer","as_string","as_tagged_base64","backtrace","blob_param","body_auto","body_bytes","body_json","boolean_param","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","description","deserialize","deserialize","eq","eq","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","equivalent","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from_str","header","header","headers","in_current_span","integer_param","into","into","into","into","into","method","name","new","opt_blob_param","opt_boolean_param","opt_integer_param","opt_param","opt_string_param","opt_tagged_base64_param","param","param_type","param_type","parse","remote","serialize","serialize","source","string_param","tagged_base64_param","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","vzip","vzip","vzip","vzip","vzip","actual","actual","expected","expected","expected","name","param_type","reason","reason","reason","value","AppSpecific","Binary","Closed","Connection","IncorrectMethod","Json","Request","SocketError","UnsupportedMessageType","WebSockets","borrow","borrow","borrow_mut","borrow_mut","code","drop","fmt","fmt","from","from","from","from","from","from","header","into","into","into_stream","map_app_specific","poll_close","poll_flush","poll_next","poll_ready","start_send","status","to_string","try_from","try_from","try_into","try_into","try_poll_next","type_id","type_id","vzip","vzip","actual","expected","Accepted","BadGateway","BadRequest","Conflict","Continue","Created","EarlyHints","ExpectationFailed","FailedDependency","Forbidden","Found","GatewayTimeout","Gone","HttpVersionNotSupported","ImATeapot","ImUsed","InsufficientStorage","InternalServerError","LengthRequired","Locked","LoopDetected","MethodNotAllowed","MisdirectedRequest","MovedPermanently","MultiStatus","MultipleChoice","NetworkAuthenticationRequired","NoContent","NonAuthoritativeInformation","NotAcceptable","NotExtended","NotFound","NotImplemented","NotModified","Ok","OutOfRangeError","PartialContent","PayloadTooLarge","PaymentRequired","PermanentRedirect","PreconditionFailed","PreconditionRequired","ProxyAuthenticationRequired","RequestHeaderFieldsTooLarge","RequestTimeout","RequestedRangeNotSatisfiable","ResetContent","SeeOther","ServiceUnavailable","StatusCode","SwitchingProtocols","TemporaryRedirect","TooEarly","TooManyRequests","Unauthorized","UnavailableForLegalReasons","UnprocessableEntity","UnsupportedMediaType","UpgradeRequired","UriTooLong","VariantAlsoNegotiates","as_error_source","backtrace","borrow","borrow","borrow_mut","borrow_mut","canonical_reason","cause","clone","clone","clone_into","clone_into","description","deserialize","eq","eq","eq","equivalent","equivalent","equivalent","equivalent","fmt","fmt","fmt","fmt","from","from","from","from","from_i64","from_u64","hash","header","header","in_current_span","into","into","is_client_error","is_informational","is_redirection","is_server_error","is_success","serialize","source","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_into","try_into","type_id","type_id","vzip","vzip","Client","borrow","borrow_mut","from","get","into","new","post","request","setup_test","test_ws_client","test_ws_client_with_headers","try_from","try_into","type_id","vzip"],"q":[[0,"tide_disco"],[225,"tide_disco::api"],[339,"tide_disco::api::ApiError"],[346,"tide_disco::app"],[461,"tide_disco::app::AppError"],[463,"tide_disco::error"],[510,"tide_disco::healthcheck"],[540,"tide_disco::method"],[588,"tide_disco::metrics"],[591,"tide_disco::request"],[733,"tide_disco::request::RequestError"],[744,"tide_disco::socket"],[789,"tide_disco::socket::SocketError"],[791,"tide_disco::status"],[909,"tide_disco::testing"],[925,"std::path"],[926,"clap_builder::builder::command"],[927,"toml::value"],[928,"core::clone"],[929,"core::cmp"],[930,"config::config"],[931,"config::error"],[932,"core::result"],[933,"routefinder::router"],[934,"alloc::sync"],[935,"serde::de"],[936,"core::option"],[937,"core::fmt"],[938,"core::fmt"],[939,"clap_builder"],[940,"std::path"],[941,"url::parser"],[942,"clap_builder::util::id"],[943,"core::hash"],[944,"color_eyre::section"],[945,"core::fmt"],[946,"core::marker"],[947,"tide::response"],[948,"http_types::error"],[949,"url::host"],[950,"url::slicing"],[951,"core::ops::range"],[952,"core::ops::range"],[953,"tungstenite::error"],[954,"http::request"],[955,"clap_builder::builder::resettable"],[956,"url"],[957,"url::origin"],[958,"core::iter::traits::collect"],[959,"core::str::iter"],[960,"url::path_segments"],[961,"form_urlencoded"],[962,"url"],[963,"core::net::ip_addr"],[964,"core::net::socket_addr"],[965,"alloc::vec"],[966,"std::io::error"],[967,"core::ops::function"],[968,"core::any"],[969,"core::error"],[970,"vbs::version"],[971,"serde::ser"],[972,"std::backtrace"],[973,"alloc::borrow"],[974,"core::convert"],[975,"semver"],[976,"core::future::future"],[977,"alloc::boxed"],[978,"core::pin"],[979,"tide::server"],[980,"core::convert"],[981,"std::io::error"],[982,"core::ops::function"],[983,"tagged_base64"],[984,"core::convert"],[985,"tungstenite::protocol::frame::coding"],[986,"serde_json::error"],[987,"anyhow"],[988,"core::task::wake"],[989,"core::task::poll"],[990,"http::status"],[991,"http_types::status_code"],[992,"reqwest::async_impl::request"],[993,"async_tungstenite::async_std"],[994,"async_tungstenite"],[995,"http::header::name"]],"d":["","","","","Represents a TOML array","","Represents a TOML boolean","","Represents a TOML datetime","","Configuration keys for Tide Disco settings","","Represents a TOML float","","","","Represents a TOML integer","","","","","","","","","Number of times to poll before failing","Number of milliseconds to sleep between attempts","","","","","Represents a TOML string","Represents a TOML table","","A parsed URL record.","","If true, log in color. Otherwise, no color.","","","HTTP routes","","","","","","","","","","Return the serialization of this URL.","","","Return the authority of this URL as an ASCII string.","Server address","","","","","","","","","","","","","","Return whether this URL is a cannot-be-a-base URL, meaning …","Check api.toml for schema compliance errors","","","","","","","","","","","Compose the path to the application’s configuration file","Get the application configuration","Add routes from api.toml to the routefinder instance in …","","","Serialize with Serde using the internal representation of …","","If this URL has a host and it is a domain name (not an IP …","","","","","","","","","","","","","","Return this URL’s fragment identifier, if any.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Convert a directory name as std::path::Path into an URL in …","Convert a file name as std::path::Path into an URL in the …","","","Get the path to api.toml","","Return whether the URL has an ‘authority’, which can …","Equivalent to url.host().is_some().","","","","","","","Return a JSON expression with status 200 indicating the …","Return the parsed representation of the host for this URL. …","Return the string representation of the host (domain or IP …","","","","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","Return the serialization of this URL.","","Return whether the URL is special (has a special scheme)","Parse a string as an URL, with this URL as the base URL.","","Load the web API or panic","Creates a relative URL if possible, with this URL as the …","Interfaces for methods of accessing to state.","Support for routes using the Prometheus metrics format.","","Return a default ParseOptions that can fully configure the …","","Return the origin of this URL (…","Parse an absolute URL from a string.","Parse an absolute URL from a string and add params to its …","","Return the password for this URL, if any, as a …","Return the path for this URL, as a percent-encoded ASCII …","Unless this URL is cannot-be-a-base, return an iterator of …","Return an object with methods to manipulate this URL’s …","Return the port number for this URL, if any.","Return the port number for this URL, or the default port …","Return this URL’s query string, if any, as a …","Parse the URL’s query string, if any, as …","Manipulate this URL’s query string, viewed as a sequence …","","","","Return the scheme of this URL, lower-cased, as an ASCII …","","Serialize with Serde using the internal representation of …","Change this URL’s fragment identifier.","Change this URL’s host.","Change this URL’s host to the given IP address.","Change this URL’s password.","Change this URL’s path.","Change this URL’s port number.","Change this URL’s query string.","Change this URL’s scheme.","Change this URL’s username.","An interface for asynchronous communication with clients, …","Resolve a URL’s host and port number to SocketAddr.","","","Assuming the URL is in the file scheme or similar, convert …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Return the username for this URL (typically the empty …","","","","","","","Wait for the server to respond to a connection request","","A description of an API.","An error encountered when parsing or constructing an Api.","Metadata used for describing and documenting an API.","","Version information about an API.","","","","","","","","","","","The version of this API.","","Register a handler for a route.","","","","","","","","","","","","","","","","","","Register a handler for a DELETE route.","","A description of this API.","","","","","","","","","","","","","","","","","","The version of the Tide Disco API specification format.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Create an Api by reading a TOML specification from a file.","Register a handler for a GET route.","","The heading preceding documentation of a route description.","The heading for documentation of a route.","The heading preceding documentation of route parameters.","The heading preceding documentation of all routes in this …","HTML to be appended to automatically generated …","HTML to be prepended to automatically generated …","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Register a handler for a METRICS route.","The name of this API.","Parse an API from a TOML specification.","Documentation to insert in the parameters section of a …","HTML formatting an entry in a table documenting the …","HTML closing a table documenting the parameters of a route.","HTML preceding the contents of a table documenting the …","Register a handler for a POST route.","Register a handler for a PUT route.","HTML formatting the path of a route.","","","Register a handler for a SOCKET route.","","The format version of the TOML specification used to load …","Register a uni-directional handler for a SOCKET route.","","","","","","","","","","","","","","","","","","","","","Set the health check handler for this API.","Serve the contents of dir at the URL /public/{{NAME}}.","Set the API version.","","","","","","","","","A tide-disco server application.","An error encountered while building an App.","The health status of an application.","Version information about an application.","","The Listener trait represents an implementation of http …","What listener are we converting into?","RAII guard to ensure a module is registered after it is …","ToListener represents any type that can be converted into a","Start accepting incoming connections. This method must be …","The version of this application.","","","Bind the listener. This starts the listening process by …","","","","","","","","","","","","","","","","","","","","","","","The version of the Tide Disco server framework.","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Check the health of each registered module in response to …","","Expose information about the connection. This should …","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Create and register an API module.","Check the health of the named module.","The status of each registered module, indexed by version.","The supported versions of each module registered with this …","Register this module with the linked app.","Register an API module.","","","Serve the App asynchronously.","","","The status of the overall application.","Transform self into a Listener. Unless self is already …","","","","","","","","","","","","","","","","","","","","Get the version of this application.","","","","","","Create a new App with a given state.","Set the application version.","","","Errors which can be serialized in a response body.","The simplest possible implementation of Error.","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","","","","","","","","","","","","Calls U::from(self).","","","","","","","","","","","","","","","A response to a healthcheck endpoint.","Common health statuses of an application.","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Calls U::from(self).","","The status of this health check.","","","","","","","","","","","A state which allows read access.","","The type of state which this type allows a caller to read.","A state which allows exclusive, write access.","","","","","","","The HTTP DELETE method.","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","","","The HTTP GET method.","","Calls U::from(self).","Calls U::from(self).","Check if a method is a standard HTTP method.","Check if a request method implies mutable access to the …","The Tide Disco METRICS method.","The HTTP POST method.","The HTTP PUT method.","Do an operation with immutable access to the state.","The Tide Disco SOCKET method.","","","","","","","","","","","Do an operation with mutable access to the state.","","","","","","","","","","","","","","","","","","","","","","","Parameters passed to a route handler.","","","","","","The Accept header of this request.","","","","","","","","Get the value of a named parameter and convert it to a …","Deserialize the body of a request.","","","Get the value of a named parameter and convert it to a bool…","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","The headers of the incoming request.","","Get the value of a named parameter and convert it to an …","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","The Method used to dispatch the request.","","Parse a parameter from a Request.","Get the value of a named optional parameter and convert it …","Get the value of a named optional parameter and convert it …","Get the value of a named optional parameter and convert it …","Get the value of a named optional parameter.","Get the value of a named optional parameter and convert it …","Get the value of a named optional parameter and convert it …","Get the value of a named parameter.","","","","Get the remote address for this request.","","","","Get the value of a named parameter and convert it to a …","Get the value of a named parameter and convert it to …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","A connection facilitating bi-directional, asynchronous …","","","","An error returned by a socket handler.","","","","","","","","","","","Returns the argument unchanged.","","","","Returns the argument unchanged.","","","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","202 Accepted","502 Bad Gateway","400 Bad Request","409 Conflict","100 Continue","201 Created","103 Early Hints","417 Expectation Failed","424 Failed Dependency","403 Forbidden","302 Found","504 Gateway Timeout","410 Gone","505 HTTP Version Not Supported","418 I’m a teapot","226 Im Used","507 Insufficient Storage","500 Internal Server Error","411 Length Required","423 Locked","508 Loop Detected","405 Method Not Allowed","421 Misdirected Request","301 Moved Permanently","207 Multi-Status","300 Multiple Choice","511 Network Authentication Required","204 No Content","203 Non Authoritative Information","406 Not Acceptable","510 Not Extended","404 Not Found","501 Not Implemented","304 Not Modified","200 Ok","","206 Partial Content","413 Payload Too Large","402 Payment Required","308 Permanent Redirect","412 Precondition Failed","428 Precondition Required","407 Proxy Authentication Required","431 Request Header Fields Too Large","408 Request Timeout","416 Requested Range Not Satisfiable","205 Reset Content","303 See Other","503 Service Unavailable","Serializable HTTP status code.","101 Switching Protocols","307 Temporary Redirect","425 Too Early","429 Too Many Requests","401 Unauthorized","451 Unavailable For Legal Reasons","422 Unprocessable Entity","415 Unsupported Media Type","426 Upgrade Required","414 URI Too Long","506 Variant Also Negotiates","","","","","","","The canonical reason for a given status code","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","Returns the argument unchanged.","","","","","","","Calls U::from(self).","Calls U::from(self).","Returns true if the status code is the 4xx range.","Returns true if the status code is 1xx range.","Returns true if the status code is the 3xx range.","Returns true if the status code is the 5xx range.","Returns true if the status code is the 2xx range.","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","","Calls U::from(self).","","","","","","","","","",""],"i":[0,0,0,0,145,5,145,11,145,0,0,0,145,0,11,0,145,11,11,0,0,0,0,0,0,0,0,0,5,0,5,145,145,11,0,0,26,4,0,26,4,0,0,39,9,4,3,4,5,3,26,26,3,26,4,3,26,4,5,9,11,3,26,4,5,9,11,3,0,3,5,9,11,3,5,9,11,3,3,0,0,0,3,5,3,4,3,3,3,3,3,3,0,3,3,26,4,5,5,11,3,3,26,4,5,9,11,26,26,3,3,3,11,0,26,3,3,3,3,5,39,9,0,0,3,3,0,3,3,3,3,0,3,26,4,5,9,11,3,3,3,3,3,3,11,3,3,0,0,3,0,0,11,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,0,39,9,3,3,3,3,3,3,3,3,3,3,3,3,0,3,0,0,3,3,3,5,9,11,3,5,3,3,26,4,5,9,11,11,3,26,4,5,9,11,3,26,4,5,9,11,26,26,3,3,26,4,5,9,11,0,76,0,0,0,76,0,76,76,76,76,76,76,76,76,76,76,82,76,75,76,76,82,83,75,76,82,83,75,76,76,82,83,76,82,83,83,75,76,83,82,83,76,82,76,76,76,76,82,82,82,82,76,76,82,83,75,83,76,82,83,75,75,75,76,83,83,83,83,83,83,76,76,82,83,75,75,83,75,83,83,83,83,75,75,83,82,83,75,76,82,75,76,82,83,76,76,82,83,75,76,82,83,75,76,82,83,75,76,82,83,75,75,75,75,146,146,147,148,148,149,150,99,0,0,0,0,99,0,110,0,0,95,102,99,99,95,106,99,101,102,103,106,99,101,102,103,99,99,101,102,99,101,102,103,103,99,101,102,102,103,99,101,102,99,99,99,99,101,101,101,101,102,102,102,102,106,99,99,101,102,103,106,99,99,101,102,103,99,106,99,95,106,99,101,102,103,106,106,101,102,103,106,101,102,106,99,101,101,110,99,101,102,99,106,99,101,102,103,106,99,101,102,103,106,99,101,102,103,106,106,99,101,102,103,106,106,151,152,0,0,113,113,113,113,104,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,104,104,104,104,104,104,113,113,113,104,113,113,113,104,113,113,113,113,113,113,113,113,116,0,0,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,93,116,116,116,116,116,116,117,0,117,0,0,117,85,0,153,117,153,117,117,117,117,117,117,117,117,117,117,117,153,117,117,117,117,117,153,117,117,117,117,117,117,85,117,117,117,153,117,153,117,153,117,153,117,84,86,0,86,115,123,128,123,128,115,115,123,128,115,115,115,123,128,115,0,0,0,0,0,115,115,123,128,115,79,123,123,115,123,123,123,115,79,79,79,79,79,115,79,123,128,129,115,79,123,128,129,115,115,79,123,128,129,115,79,123,128,129,115,115,128,123,128,123,123,123,123,128,128,128,128,115,115,79,123,128,128,129,115,79,123,128,129,128,115,128,79,115,79,115,79,123,128,129,79,129,123,79,79,79,79,79,79,79,123,129,123,79,115,128,115,79,79,115,79,123,128,129,115,128,115,79,123,128,128,129,115,79,123,128,129,115,79,123,128,129,115,79,123,128,129,154,155,154,156,155,157,158,159,160,158,156,114,114,114,0,114,114,114,0,114,114,91,114,91,114,114,91,114,114,91,114,114,114,114,114,114,91,114,91,114,91,91,91,91,91,114,114,91,114,91,114,91,91,114,91,114,161,161,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,0,111,111,111,111,111,111,111,111,111,111,111,111,111,0,111,111,111,111,111,111,111,111,111,111,111,136,136,111,136,111,136,111,136,111,136,111,136,136,111,111,111,111,111,111,111,111,111,111,136,136,111,111,111,136,111,111,111,111,136,136,111,136,111,111,111,111,111,111,136,111,136,111,136,111,111,136,111,136,111,136,111,136,0,140,140,140,140,140,140,140,140,0,0,0,140,140,140,140],"f":"``````````````````````````````````````````{{bb}d}```{fb}{hb}{jb}2{ll}03``{ce{}{}}00000000000{fn}{A`n}{ff}{jj}{{{Ab{c}}}{{Ab{c}}}Ad}{AfAf}{{ce}Ah{}{}}000{{ff}Aj}{{ce}Aj{}{}}>{{bb{An{{Al{bb}}}}}{{Bd{B`Bb}}}}{A`{{Bj{{Bh{Bf}}}}}}{c{{Bd{f}}}Bl}{c{{Bd{j}}}Bl}1`{f{{Bn{b}}}}{{ff}n}{{ce}n{}{}}000`{{fC`}{{Bd{AhCb}}}}0{{CdC`}Cf}{{hC`}Cf}{{jC`}Cf}{{jC`}{{Bd{AhCb}}}}{{AfC`}Cf}8{cc{}}00000{Ch{{Bd{CdCj}}}}0{c{{Bd{fAh}}}{{Cn{Cl}}}}0{b{{Bd{fD`}}}}{b{{Bd{Afc}}}{}}{bd}{{}{{Bn{Db}}}}{fn}0{{fc}AhDd}{{ce}{{Df{eg}}}{}{DhDjDl}{}}0```{{{E`{Dn}}}{{Bd{EbEd}}}}{f{{Bn{{Ef{b}}}}}}{f{{Bn{b}}}}`{{f{Ej{Eh}}}b}{{f{El{Eh}}}b}{{fEn}b}{{f{F`{Eh}}}b}{nAh}{ce{}{}}00000{f{{Bd{{Fb{Ah}}Fd}}}}{f{{Bd{{Ff{Ah}}Fh}}}}01{c{{Fl{Fj}}}{}}{fFj}{Afn}{fn}{{fb}{{Bd{fD`}}}}`{ClA`}{{ff}{{Bn{Fj}}}}``{{bAf}Af}{{}Fn}{bd}{fG`}{b{{Bd{fD`}}}}{{bc}{{Bd{fD`}}}Gb}{{ff}{{Bn{Aj}}}}{f{{Bn{b}}}}{fb}{f{{Bn{{Gf{Gd}}}}}}{f{{Bd{GhAh}}}}{f{{Bn{Gj}}}}04{fGl}{f{{H`{Gn}}}}```5{{fc}BdHb}0{{f{Bn{b}}}Ah}{{f{Bn{b}}}{{Bd{AhD`}}}}{{fHd}{{Bd{AhAh}}}}{{f{Bn{b}}}{{Bd{AhAh}}}}{{fb}Ah}{{f{Bn{Gj}}}{{Bd{AhAh}}}}5{{fb}{{Bd{AhAh}}}}0`{{fc}{{Bd{{Hh{Hf}}Hj}}}{{Hn{}{{Hl{{Bn{Gj}}}}}}}}``{f{{Bd{dAh}}}}{f{{Bd{Hj}}}}{ce{}{}}000{cFj{}}0{b{{Bd{f}}}}{c{{Bd{e}}}{}{}}00000{b{{Bd{Afc}}}{}}111111{cI`{}}00000{{CdCh}{{Bd{AhCj}}}}0{fb}777777{{fIbIb}Ah}`````````````````{cId{}}{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDl}{}{DjDlIj}Il{DjDl{Hn{Inc}{{Hl{{J`{{Bd{ie}}}}}}}}}}{Ih{{Bn{Jb}}}};;;;;;;;{Ih{{Bn{Id}}}}{IhIh}{JdJd}{JfJf}{{ce}Ah{}{}}00{{}Jf}{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDlJh}{}{DjDlIj}Il{DjDl{Hn{In}{{Hl{{J`{{Bd{ie}}}}}}}}}}{Ihb}`{c{{Bd{Jd}}}Bl}{c{{Bd{Jf}}}Bl}{{IhIh}n}{{JdJd}n}{{ce}n{}{}}0000000{{IhC`}Cf}0{{JdC`}Cf}{{JfC`}Cf}{{{If{ceg}}C`}Cf{}{}{}}`{cc{}}000{c{{Bd{{If{egi}}Ih}}}{{Cn{Cl}}}{}{}Ij}{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDlJj}{}{DjDlIj}Il{DjDl{Hn{In}{{Hl{{J`{{Bd{ie}}}}}}}}}}{{ce}{{Df{eg}}}{}{DhDjDl}{}}``````{c{}{}}{ce{}{}}000{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDlJj}{}{DjDlIj}{AdJl}{DjDl{Hn{In}{{Hl{{J`{{Bd{{Jn{i}}e}}}}}}}}}}`{c{{Bd{{If{egi}}Ih}}}{{K`{A`}}}{}{}Ij}````{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDlJh}{}{DjDlIj}Il{DjDl{Hn{In}{{Hl{{J`{{Bd{ie}}}}}}}}}}0`{{Jdc}BdHb}{{Jfc}BdHb}{{{If{ceg}}bm}{{Bd{{If{ceg}}Ih}}}{DjDl}{DjDh}Ij{IlKb}Kd{DjDl{Hn{In{Kf{ikeg}}c}{{Hl{{J`{{Bd{Ahe}}}}}}}}}}{Ih{{Bn{Id}}}}`{{{If{ceg}}bk}{{Bd{{If{ceg}}Ih}}}{DjDl}{DjDh}{DjDlIj}{IlDjDl}{DjDl{Hn{Inc}{{Hl{{Kh{{Bd{ie}}}}}}}}}}888{cFj{}}{c{{Bd{e}}}{}{}}0000000{cI`{}}000;;;;{{{If{ceg}}k}{{If{ceg}}}{DjDl}{}{DjDlIj}Kj{DjDl{Hn{c}{{Hl{{J`{i}}}}}}}}{{{If{ceg}}d}{{If{ceg}}}{}{}Ij}{{{If{ceg}}Kl}{{If{ceg}}}{}{}Ij}`````````````````{Kn{{Ld{{Lb{L`}}}}}}`{cId{}}{Lf{{Bn{Jb}}}}{{Kn{Lh{c}}}{{Ld{{Lb{L`}}}}}{DjDl}}{ce{}{}}000000000{Lf{{Bn{Id}}}}{LfLf}{LjLj}{LlLl}{{ce}Ah{}{}}00{{{Ln{cgei}}}k{DjDl}{DjDl}{M`{Mb{e}}}Ij{}}0{Lfb}{c{{Bd{Lj}}}Bl}{c{{Bd{Ll}}}Bl}`{{{Ln{cgei}}}Ah{DjDl}{DjDl}{M`{Mb{e}}}Ij}{{LfLf}n}{{LjLj}n}{{LlLl}n}{{ce}n{}{}}00000000000{{{Md{ce}}C`}CfMfMf}{{LfC`}Cf}0{{LjC`}Cf}{{LlC`}Cf}{{{Ln{cgei}}C`}Cf{DjDlMf}{DjDlMf}{M`{Mb{e}}Mf}{IjMf}}{cc{}}{IhLf}1111{{ce}{{Df{eg}}}{}{DhDjDl}{}}{{{Md{ce}}Inc}Lj{DjDl}{}}{c{}{}}{Kn{{Hh{Mh}}}}{ce{}{}}0000{{{Md{cg}}bi}{{Bd{{Ln{cgek}}Lf}}}{DjDl}{DjDl}{M`{Mb{e}}}{{K`{A`}}}Ij}{{{Md{ce}}Incb{Bn{Ib}}}{{Bn{Eb}}}{DjDl}{}}``{{{Ln{cgei}}}{{Bd{AhLf}}}{DjDl}{DjDl}{M`{Mb{e}}}Ij}{{{Md{cg}}b{If{cei}}}{{Bd{{Md{cg}}Lf}}}{DjDl}{DjDl}{M`{Mb{e}}}Ij}{{Ljc}BdHb}{{Llc}BdHb}{{{Md{ce}}gi}{{Mj{Ah}}}{DjDl}M`{{Ml{{Bj{{Md{ce}}}}}}}Ij}{Lf{{Bn{Id}}}}{LjMn}`{{{Ml{}{{N`{c}}}}}{{Bd{cHj}}}{{Kn{e}}}{AdDjDl}}:::{cFj{}}{c{{Bd{e}}}{}{}}000000000{cI`{}}0000{{{Md{ce}}}Ll{DjDl}{}}>>>>>{c{{Md{ce}}}{DjDl}{}}{{{Md{ce}}Kl}{{Md{ce}}}{DjDl}{}}````{cId{}}{Nb{{Bn{Jb}}}}{ce{}{}}0{{MnFj}M`}{{MnFj}Nb}{Nb{{Bn{Id}}}}{NbNb}{{ce}Ah{}{}}{Nbb}{c{{Bd{Nb}}}Bl}{{NbNb}n}{{ce}n{}{}}000{{NbC`}Cf}0{cc{}}{BbNb}{{{Nd{c}}}NbDh}{HjNb}{NfNb}{BbM`}{HjM`}{NfM`}{{{`{c}}}M`Dh}{EdM`}{{{Nd{c}}}M`Dh}{{ce}{{Df{eg}}}{}{DhDjDl}{}}{c{}{}}{ce{}{}}{M`Ed}`{{Nbc}BdHb}{Nb{{Bn{Id}}}}{M`Mn}{NbMn}`5{cFj{}}{c{{Bd{e}}}{}{}}0{cI`{}}8````````88{NhNh}{{ce}Ah{}{}}{{}Nh}{c{{Bd{Nh}}}Bl}{{NhNh}n}{{ce}n{}{}}000{{NhC`}Cf}{cc{}}{ce{}{}}{{Nhc}BdHb}{KjMn}{NhMn}3==<3````````3333{NjNj};{{}Nj}{{NjNj}n}9999{{NjC`}Cf}088{NlNj}{b{{Bd{Njc}}}{}}4{{ce}{{Df{eg}}}{}{DhDjDl}{}}::{Njn}0666{{{Jj{}{{Nn{c}}}}g}{{Ld{{Lb{L`}}}}}{}{}{Dj{O`{c}{{Hl{{J`{e}}}}}}}}7<{cFj{}}{c{{Bd{e}}}{}{}}000{cI`{}}0??{{Jhg}{{Ld{{Lb{L`}}}}}{}{}{Dj{O`{c}{{Hl{{J`{e}}}}}}}}``{{{Jl{}{{Ob{c}}}}}{{Bd{Fjc}}}{MfId}}`````````````````````````{In{{Bd{OdNf}}}}{Of{{Bd{cNf}}}{{Oj{Oh}}}}{Of{{Bd{nNf}}}}{cId{}}{Of{{Bd{cNf}}}{{Oj{Ol}}}}{Of{{Bd{bNf}}}}{Of{{Bd{OhNf}}}}{Nf{{Bn{Jb}}}}{{Inc}{{Bd{eNf}}}{KbDh}{{Oj{Oh}}}}{{Inc}{{Bd{eNf}}}IjKd}{In{{Hh{On}}}}{In{{Bd{cNf}}}Kd}{{Inc}{{Bd{nNf}}}{KbDh}}{ce{}{}}000000000{Nf{{Bn{Id}}}}{NfNf}{InIn}{OfOf}{A`A`}{AbAb}{{ce}Ah{}{}}0000{Nfb}{c{{Bd{Nf}}}Bl}{c{{Bd{A`}}}Bl}{{OfOf}n}{{A`A`}n}{{ce}n{}{}}0000000{{NfC`}Cf}0{{InC`}Cf}{{OfC`}Cf}{{A`C`}Cf}{{A`C`}{{Bd{AhCb}}}}{{AbC`}Cf}{cc{}}0000{b{{Bd{A`c}}}{}}{{ce}{{Df{eg}}}{}{DhDjDl}{}}0{InAd}{c{}{}}{{Inc}{{Bd{eNf}}}{KbDh}{{Oj{Ol}}}}{ce{}{}}0000{InNj}`{{{E`{c}}Ab}{{Bd{{Bn{Of}}Nf}}}{}}{{Inc}{{Bd{{Bn{e}}Nf}}}{KbDh}{{Oj{Oh}}}}{{Inc}{{Bd{{Bn{n}}Nf}}}{KbDh}}{{Inc}{{Bd{{Bn{e}}Nf}}}{KbDh}{{Oj{Ol}}}}{{Inc}{{Bn{Of}}}{KbDh}}{{Inc}{{Bd{{Bn{b}}Nf}}}{KbDh}}{{Inc}{{Bd{{Bn{Oh}}Nf}}}{KbDh}}{{Inc}{{Bd{OfNf}}}{KbDh}}{OfA`}`{{bAb}{{Bd{OfNf}}}}{In{{Bn{b}}}}{{Nfc}BdHb}{{A`c}BdHb}{Nf{{Bn{Id}}}}{{Inc}{{Bd{bNf}}}{KbDh}}{{Inc}{{Bd{OhNf}}}{KbDh}}{ce{}{}}0000{cFj{}}0{c{{Bd{e}}}{}{}}000{b{{Bd{A`c}}}{}}111111{cI`{}}000044444`````````````````````4444{{{Nd{c}}}Af{}}{{{Kf{cegi}}}AhKb{}{}Ij}{{{Nd{c}}C`}CfMf}{{{Nd{c}}C`}CfDh}{cc{}}{Nf{{Nd{c}}}{}}{Ah{{Nd{c}}}{}}{Fd{{Nd{c}}}{}}3{Aj{{Nd{c}}}{}}{{ce}{{Df{eg}}}{}{DhDjDl}{}}>>>{{{Nd{c}}g}{{Nd{e}}}{}{}{{Hn{c}{{Hl{e}}}}}}{{{Ld{{Kf{cegi}}}}Al}{{An{{Bd{Ahk}}}}}{IlKb}{}{}Ij{}}0{{{Ld{{Kf{cegi}}}}Al}{{An{{Bn{k}}}}}KbKd{}Ij{}}1{{{Ld{{Kf{cegi}}}}c}{{Bd{Ahk}}}{IlKb}{}{}Ij{}}{{{Nd{c}}}Mn{}}{cFj{}}{c{{Bd{e}}}{}{}}000{{{Ld{c}}Al}{{An{{Bn{Bd}}}}}{}}{cI`{}}0{ce{}{}}0```````````````````````````````````````````````````````````````{cId{}}{AA`{{Bn{Jb}}}}2222{Mnb}{AA`{{Bn{Id}}}}{MnMn}{AA`AA`}{{ce}Ah{}{}}0{AA`b}{c{{Bd{Mn}}}Bl}{{MnMn}n}{{MnAAb}n}{{MnAAd}n}{{ce}n{}{}}000{{MnC`}Cf}0{{AA`C`}Cf}0{AAbMn}{cc{}}{AAdMn}1{AAf{{Bn{Mn}}}}{Ib{{Bn{Mn}}}}{{Mnc}AhDd}{{ce}{{Df{eg}}}{}{DhDjDl}{}}0{c{}{}}{ce{}{}}0{Mnn}0000{{Mnc}BdHb}{AA`{{Bn{Id}}}}33{cFj{}}0{Gj{{Bd{Mnc}}}{}}{c{{Bd{e}}}{}{}}000{cI`{}}077`77>{{AAhb}AAj}8{fAAh}1{{AAhNlb}AAj}{{}Ah}{f{{AAn{AAl}}}}{{f{An{{Al{AB`b}}}}}{{AAn{AAl}}}}776=","c":[143],"p":[[1,"str"],[5,"PathBuf",925],[5,"Url",0],[6,"DiscoKey",0],[6,"HealthStatus",0],[5,"Command",926],[1,"bool"],[6,"Value",927],[5,"ServerState",0],[10,"Clone",928],[6,"UrlSegment",0],[1,"unit"],[6,"Ordering",929],[1,"tuple"],[1,"slice"],[5,"Config",930],[6,"ConfigError",931],[6,"Result",932],[1,"usize"],[5,"Router",933],[5,"Arc",934],[10,"Deserializer",935],[6,"Option",936],[5,"Formatter",937],[5,"Error",937],[5,"DiscoArgs",0],[8,"Result",937],[5,"ArgMatches",938],[8,"Error",939],[5,"Path",925],[10,"AsRef",940],[6,"ParseError",941],[5,"Id",942],[10,"Hasher",943],[5,"IndentedSection",944],[10,"Display",937],[10,"Send",945],[10,"Sync",945],[8,"AppServerState",0],[5,"Request",946],[5,"Response",947],[5,"Error",948],[6,"Host",949],[6,"Position",950],[5,"RangeFrom",951],[5,"Range",951],[5,"RangeFull",951],[5,"RangeTo",951],[5,"Request",952],[6,"Error",953],[5,"Request",952],[6,"Error",953],[5,"String",954],[6,"Resettable",955],[5,"ParseOptions",956],[6,"Origin",957],[10,"IntoIterator",958],[1,"char"],[5,"Split",959],[5,"PathSegmentsMut",960],[1,"u16"],[5,"Parse",961],[5,"UrlQuery",956],[5,"Serializer",961],[10,"Serializer",962],[6,"IpAddr",963],[6,"SocketAddr",964],[5,"Vec",965],[5,"Error",966],[17,"Output"],[10,"Fn",967],[5,"TypeId",968],[1,"u64"],[10,"Error",969],[5,"Api",225],[6,"ApiError",225],[10,"StaticVersionType",970],[10,"Serialize",962],[5,"RequestParams",591],[8,"BoxFuture",971],[5,"Backtrace",972],[5,"ApiVersion",225],[5,"ApiMetadata",225],[10,"WriteState",540],[10,"ReadState",540],[10,"Metrics",588],[6,"Cow",973],[10,"Into",940],[10,"Sized",945],[10,"DeserializeOwned",935],[5,"Connection",744],[8,"BoxStream",974],[10,"HealthCheck",510],[5,"Version",975],[10,"Listener",346],[10,"Future",976],[5,"Box",977],[5,"Pin",978],[6,"AppError",346],[5,"Server",979],[5,"AppHealth",346],[5,"AppVersion",346],[5,"Module",346],[10,"Error",463],[10,"From",940],[5,"App",346],[10,"Debug",937],[5,"ListenInfo",980],[8,"Result",966],[10,"ToListener",346],[6,"StatusCode",791],[17,"Listener"],[5,"ServerError",463],[6,"SocketError",744],[6,"RequestError",591],[6,"HealthStatus",510],[6,"Method",540],[6,"Method",981],[17,"State"],[10,"FnOnce",967],[17,"Error"],[5,"Accept",982],[6,"RequestParamValue",591],[5,"TaggedBase64",983],[10,"TryFrom",940],[1,"u128"],[1,"u8"],[6,"RequestParamType",591],[5,"RequestParam",591],[5,"Headers",984],[6,"CloseCode",985],[5,"Error",986],[5,"Error",987],[5,"Context",988],[6,"Poll",989],[5,"OutOfRangeError",791],[5,"StatusCode",990],[6,"StatusCode",991],[1,"i64"],[5,"Client",909],[5,"RequestBuilder",992],[8,"ConnectStream",993],[5,"WebSocketStream",994],[5,"HeaderName",995],[8,"AppState",0],[15,"IncorrectMethod",339],[15,"CannotReadToml",339],[15,"AmbiguousRoutes",339],[15,"Route",339],[15,"InvalidMetaTable",339],[15,"Api",461],[15,"Dispatch",461],[5,"ParseMethodError",540],[15,"IncorrectParamType",733],[15,"TagMismatch",733],[15,"IntegerOverflow",733],[15,"MissingParam",733],[15,"InvalidParam",733],[15,"TaggedBase64",733],[15,"Http",733],[15,"IncorrectMethod",789]],"b":[[93,"impl-Debug-for-Url"],[94,"impl-Display-for-Url"],[97,"impl-Debug-for-HealthStatus"],[98,"impl-Display-for-HealthStatus"],[127,"impl-Index%3CRangeFrom%3CPosition%3E%3E-for-Url"],[128,"impl-Index%3CRange%3CPosition%3E%3E-for-Url"],[129,"impl-Index%3CRangeFull%3E-for-Url"],[130,"impl-Index%3CRangeTo%3CPosition%3E%3E-for-Url"],[138,"impl-IntoClientRequest-for-Url"],[139,"impl-IntoClientRequest-for-%26Url"],[140,"impl-IntoClientRequest-for-Url"],[141,"impl-IntoClientRequest-for-%26Url"],[276,"impl-Debug-for-ApiError"],[277,"impl-Display-for-ApiError"],[401,"impl-Debug-for-AppError"],[402,"impl-Display-for-AppError"],[481,"impl-Display-for-ServerError"],[482,"impl-Debug-for-ServerError"],[484,"impl-From%3CConfigError%3E-for-ServerError"],[485,"impl-From%3CSocketError%3CE%3E%3E-for-ServerError"],[486,"impl-From%3CError%3E-for-ServerError"],[487,"impl-From%3CRequestError%3E-for-ServerError"],[560,"impl-Debug-for-Method"],[561,"impl-Display-for-Method"],[663,"impl-Display-for-RequestError"],[664,"impl-Debug-for-RequestError"],[667,"impl-Debug-for-RequestParamType"],[668,"impl-Display-for-RequestParamType"],[760,"impl-Debug-for-SocketError%3CE%3E"],[761,"impl-Display-for-SocketError%3CE%3E"],[763,"impl-From%3CRequestError%3E-for-SocketError%3CE%3E"],[764,"impl-From%3CError%3E-for-SocketError%3CE%3E"],[765,"impl-From%3CError%3E-for-SocketError%3CE%3E"],[767,"impl-From%3CError%3E-for-SocketError%3CE%3E"],[866,"impl-PartialEq-for-StatusCode"],[867,"impl-PartialEq%3CStatusCode%3E-for-StatusCode"],[868,"impl-PartialEq%3CStatusCode%3E-for-StatusCode"],[873,"impl-Debug-for-StatusCode"],[874,"impl-Display-for-StatusCode"],[875,"impl-Debug-for-OutOfRangeError"],[876,"impl-Display-for-OutOfRangeError"],[877,"impl-From%3CStatusCode%3E-for-StatusCode"],[879,"impl-From%3CStatusCode%3E-for-StatusCode"]]}]\ ]')); if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; else if (window.initSearch) window.initSearch(searchIndex); diff --git a/settings.html b/settings.html index 6fbfa6d7..b97e5f86 100644 --- a/settings.html +++ b/settings.html @@ -1,2 +1,2 @@ -Settings +Settings

Rustdoc settings

Back
\ No newline at end of file diff --git a/src-files.js b/src-files.js index 2522cdc4..8df6e8dd 100644 --- a/src-files.js +++ b/src-files.js @@ -1,4 +1,4 @@ var srcIndex = new Map(JSON.parse('[\ -["tide_disco",["",[],["api.rs","app.rs","error.rs","healthcheck.rs","lib.rs","method.rs","metrics.rs","middleware.rs","request.rs","route.rs","socket.rs","status.rs","testing.rs"]]]\ +["tide_disco",["",[],["api.rs","app.rs","dispatch.rs","error.rs","healthcheck.rs","lib.rs","method.rs","metrics.rs","middleware.rs","request.rs","route.rs","socket.rs","status.rs","testing.rs"]]]\ ]')); createSrcSidebar(); diff --git a/src/tide_disco/api.rs.html b/src/tide_disco/api.rs.html index 4a1729ac..900df930 100644 --- a/src/tide_disco/api.rs.html +++ b/src/tide_disco/api.rs.html @@ -1,4 +1,4 @@ -api.rs - source use crate::{ api::{Api, ApiError, ApiInner, ApiVersion}, + dispatch::{self, DispatchError, Trie}, healthcheck::{HealthCheck, HealthStatus}, http, method::Method, @@ -1631,9 +1825,9 @@

Files

Html, StatusCode, }; use async_std::sync::Arc; +use derive_more::From; use futures::future::{BoxFuture, FutureExt}; use include_dir::{include_dir, Dir}; -use itertools::Itertools; use lazy_static::lazy_static; use maud::{html, PreEscaped}; use semver::Version; @@ -1641,10 +1835,7 @@

Files

use serde_with::{serde_as, DisplayFromStr}; use snafu::{ResultExt, Snafu}; use std::{ - collections::{ - btree_map::{BTreeMap, Entry as BTreeEntry}, - hash_map::{Entry as HashEntry, HashMap}, - }, + collections::btree_map::BTreeMap, convert::Infallible, env, fmt::Display, @@ -1673,24 +1864,23 @@

Files

/// use by any given API module may differ, depending on the supported version of the API. #[derive(Debug)] pub struct App<State, Error> { - // Map from base URL, major version to API. - pub(crate) apis: HashMap<String, BTreeMap<u64, ApiInner<State, Error>>>, + pub(crate) modules: Trie<ApiInner<State, Error>>, pub(crate) state: Arc<State>, app_version: Option<Version>, } /// An error encountered while building an [App]. -#[derive(Clone, Debug, Snafu, PartialEq, Eq)] +#[derive(Clone, Debug, From, Snafu, PartialEq, Eq)] pub enum AppError { Api { source: ApiError }, - ModuleAlreadyExists, + Dispatch { source: DispatchError }, } impl<State: Send + Sync + 'static, Error: 'static> App<State, Error> { /// Create a new [App] with a given state. pub fn with_state(state: State) -> Self { Self { - apis: HashMap::new(), + modules: Default::default(), state: Arc::new(state), app_version: None, } @@ -1773,20 +1963,8 @@

Files

} }; - match self.apis.entry(base_url.to_string()) { - HashEntry::Occupied(mut e) => match e.get_mut().entry(major_version) { - BTreeEntry::Occupied(_) => { - return Err(AppError::ModuleAlreadyExists); - } - BTreeEntry::Vacant(e) => { - e.insert(api); - } - }, - HashEntry::Vacant(e) => { - e.insert([(major_version, api)].into()); - } - } - + self.modules + .insert(dispatch::split(base_url), major_version, api)?; Ok(self) } @@ -1827,12 +2005,17 @@

Files

app_version: self.app_version.clone(), disco_version: env!("CARGO_PKG_VERSION").parse().unwrap(), modules: self - .apis + .modules .iter() - .map(|(name, versions)| { + .map(|module| { ( - name.clone(), - versions.values().rev().map(|api| api.version()).collect(), + module.path(), + module + .versions + .values() + .rev() + .map(|api| api.version()) + .collect(), ) }) .collect(), @@ -1846,19 +2029,22 @@

Files

/// (due to type erasure) but can be queried using [module_health](Self::module_health) or by /// hitting the endpoint `GET /:module/healthcheck`. pub async fn health(&self, req: RequestParams, state: &State) -> AppHealth { - let mut modules = BTreeMap::<String, BTreeMap<_, _>>::new(); + let mut modules_health = BTreeMap::<String, BTreeMap<_, _>>::new(); let mut status = HealthStatus::Available; - for (name, versions) in &self.apis { - let module = modules.entry(name.clone()).or_default(); - for (version, api) in versions { + for module in &self.modules { + let versions_health = modules_health.entry(module.path()).or_default(); + for (version, api) in &module.versions { let health = StatusCode::from(api.health(req.clone(), state).await.status()); if health != StatusCode::Ok { status = HealthStatus::Unhealthy; } - module.insert(*version, health); + versions_health.insert(*version, health); } } - AppHealth { status, modules } + AppHealth { + status, + modules: modules_health, + } } /// Check the health of the named module. @@ -1879,10 +2065,10 @@

Files

module: &str, major_version: Option<u64>, ) -> Option<tide::Response> { - let versions = self.apis.get(module)?; + let module = self.modules.get(dispatch::split(module))?; let api = match major_version { - Some(v) => versions.get(&v)?, - None => versions.last_key_value()?.1, + Some(v) => module.versions.get(&v)?, + None => module.versions.last_key_value()?.1, }; Some(api.health(req, state).await) } @@ -1932,35 +2118,41 @@

Files

.allow_credentials(true), ); - for (name, versions) in &state.apis { - Self::register_api(&mut server, name.clone(), versions)?; + for module in &state.modules { + Self::register_api(&mut server, module.prefix.clone(), &module.versions)?; } - // Register app-level automatic routes: `healthcheck` and `version`. - server - .at("healthcheck") - .get(move |req: tide::Request<Arc<Self>>| async move { - let state = req.state().clone(); - let app_state = &*state.state; - let req = request_params(req, &[]).await?; - let accept = req.accept()?; - let res = state.health(req, app_state).await; - Ok(health_check_response::<_, VER>(&accept, res)) - }); - server - .at("version") - .get(move |req: tide::Request<Arc<Self>>| async move { - let accept = RequestParams::accept_from_headers(&req)?; - respond_with(&accept, req.state().version(), bind_version) - .map_err(|err| Error::from_route_error::<Infallible>(err).into_tide_error()) - }); - - // Serve documentation at the root URL for discoverability - server - .at("/") - .all(move |req: tide::Request<Arc<Self>>| async move { - Ok(tide::Response::from(Self::top_level_docs(req))) - }); + // Register app-level routes summarizing the status and documentation of all the registered + // modules. We skip this step if this is a singleton app with only one module registered at + // the root URL, as these app-level endpoints would conflict with the (probably more + // specific) API-level status endpoints. + if !state.modules.is_singleton() { + // Register app-level automatic routes: `healthcheck` and `version`. + server + .at("healthcheck") + .get(move |req: tide::Request<Arc<Self>>| async move { + let state = req.state().clone(); + let app_state = &*state.state; + let req = request_params(req, &[]).await?; + let accept = req.accept()?; + let res = state.health(req, app_state).await; + Ok(health_check_response::<_, VER>(&accept, res)) + }); + server + .at("version") + .get(move |req: tide::Request<Arc<Self>>| async move { + let accept = RequestParams::accept_from_headers(&req)?; + respond_with(&accept, req.state().version(), bind_version) + .map_err(|err| Error::from_route_error::<Infallible>(err).into_tide_error()) + }); + + // Serve documentation at the root URL for discoverability + server + .at("/") + .all(move |req: tide::Request<Arc<Self>>| async move { + Ok(tide::Response::from(Self::top_level_docs(req))) + }); + } server.listen(listener).await } @@ -1968,22 +2160,22 @@

Files

fn list_apis(&self) -> Html { html! { ul { - @for (name, versions) in &self.apis { + @for module in &self.modules { li { // Link to the alias for the latest version as the primary link. - a href=(format!("/{}", name)) {(name)} +
a href=(format!("/{}", module.path())) {(module.path())} // Add a superscript link (link a footnote) for each specific supported // version, linking to documentation for that specific version. - @for version in versions.keys().rev() { + @for version in module.versions.keys().rev() { sup { - a href=(format!("/v{version}/{name}")) { + a href=(format!("/v{version}/{}", module.path())) { (format!("[v{version}]")) } } } " " // Take the description of the latest supported version. - (PreEscaped(versions.last_key_value().unwrap().1.short_description())) + (PreEscaped(module.versions.last_key_value().unwrap().1.short_description())) } } } @@ -1992,7 +2184,7 @@

Files

fn register_api( server: &mut tide::Server<Arc<Self>>, - prefix: String, + prefix: Vec<String>, versions: &BTreeMap<u64, ApiInner<State, Error>>, ) -> io::Result<()> { for (version, api) in versions { @@ -2003,7 +2195,7 @@

Files

fn register_api_version( server: &mut tide::Server<Arc<Self>>, - prefix: &String, + prefix: &[String], version: u64, api: &ApiInner<State, Error>, ) -> io::Result<()> { @@ -2015,11 +2207,16 @@

Files

server .at("/public") .at(&format!("v{version}")) - .at(prefix) + .at(&prefix.join("/")) .serve_dir(api.public().unwrap_or_else(|| &DEFAULT_PUBLIC_PATH))?; // Register routes for this API. - let mut api_endpoint = server.at(&format!("/v{version}/{prefix}")); + let mut version_endpoint = server.at(&format!("/v{version}")); + let mut api_endpoint = if prefix.is_empty() { + version_endpoint + } else { + version_endpoint.at(&prefix.join("/")) + }; api_endpoint.with(AddErrorBody::new(api.error_handler())); for (path, routes) in api.routes_by_path() { let mut endpoint = api_endpoint.at(path); @@ -2033,7 +2230,7 @@

Files

// If there is a socket route with this pattern, add the socket middleware to // all endpoints registered under this pattern, so that any request with any // method that has the socket upgrade headers will trigger a WebSockets upgrade. - Self::register_socket(prefix.to_owned(), version, &mut endpoint, socket_route); + Self::register_socket(prefix.to_vec(), version, &mut endpoint, socket_route); } if let Some(metrics_route) = routes .iter() @@ -2043,13 +2240,13 @@

Files

// all endpoints registered under this pattern, so that a request to this path // with the right headers will return metrics instead of going through the // normal method-based dispatching. - Self::register_metrics(prefix.to_owned(), version, &mut endpoint, metrics_route); + Self::register_metrics(prefix.to_vec(), version, &mut endpoint, metrics_route); } // Register the HTTP routes. for route in routes { if let Method::Http(method) = route.method() { - Self::register_route(prefix.to_owned(), version, &mut endpoint, route, method); + Self::register_route(prefix.to_vec(), version, &mut endpoint, route, method); } } } @@ -2057,26 +2254,26 @@

Files

// Register automatic routes for this API: documentation, `healthcheck` and `version`. Serve // documentation at the root of the API (with or without a trailing slash). for path in ["", "/"] { - let prefix = prefix.clone(); + let prefix = prefix.to_vec(); api_endpoint .at(path) .all(move |req: tide::Request<Arc<Self>>| { let prefix = prefix.clone(); async move { - let api = &req.state().clone().apis[&prefix][&version]; + let api = &req.state().clone().modules[&prefix].versions[&version]; Ok(api.documentation()) } }); } { - let prefix = prefix.clone(); + let prefix = prefix.to_vec(); api_endpoint .at("*path") .all(move |req: tide::Request<Arc<Self>>| { let prefix = prefix.clone(); async move { // The request did not match any route. Serve documentation for the API. - let api = &req.state().clone().apis[&prefix][&version]; + let api = &req.state().clone().modules[&prefix].versions[&version]; let docs = html! { "No route matches /" (req.param("path")?) br{} @@ -2089,13 +2286,13 @@

Files

}); } { - let prefix = prefix.clone(); + let prefix = prefix.to_vec(); api_endpoint .at("healthcheck") .get(move |req: tide::Request<Arc<Self>>| { let prefix = prefix.clone(); async move { - let api = &req.state().clone().apis[&prefix][&version]; + let api = &req.state().clone().modules[&prefix].versions[&version]; let state = req.state().clone(); Ok(api .health(request_params(req, &[]).await?, &state.state) @@ -2104,13 +2301,13 @@

Files

}); } { - let prefix = prefix.clone(); + let prefix = prefix.to_vec(); api_endpoint .at("version") .get(move |req: tide::Request<Arc<Self>>| { let prefix = prefix.clone(); async move { - let api = &req.state().apis[&prefix][&version]; + let api = &req.state().modules[&prefix].versions[&version]; let accept = RequestParams::accept_from_headers(&req)?; api.version_handler()(&accept, api.version()) .map_err(|err| Error::from_route_error(err).into_tide_error()) @@ -2122,7 +2319,7 @@

Files

} fn register_route( - api: String, + api: Vec<String>, version: u64, endpoint: &mut tide::Route<Arc<Self>>, route: &Route<State, Error>, @@ -2133,7 +2330,7 @@

Files

let name = name.clone(); let api = api.clone(); async move { - let route = &req.state().clone().apis[&api][&version][&name]; + let route = &req.state().clone().modules[&api].versions[&version][&name]; let state = &*req.state().clone().state; let req = request_params(req, route.params()).await?; route @@ -2149,7 +2346,7 @@

Files

} fn register_metrics( - api: String, + api: Vec<String>, version: u64, endpoint: &mut tide::Route<Arc<Self>>, route: &Route<State, Error>, @@ -2175,7 +2372,7 @@

Files

} fn register_socket( - api: String, + api: Vec<String>, version: u64, endpoint: &mut tide::Route<Arc<Self>>, route: &Route<State, Error>, @@ -2191,7 +2388,7 @@

Files

let name = name.clone(); let api = api.clone(); async move { - let route = &req.state().clone().apis[&api][&version][&name]; + let route = &req.state().clone().modules[&api].versions[&version][&name]; let state = &*req.state().clone().state; let req = request_params(req, route.params()).await?; route @@ -2223,7 +2420,7 @@

Files

} fn register_fallback( - api: String, + api: Vec<String>, version: u64, endpoint: &mut tide::Route<Arc<Self>>, route: &Route<State, Error>, @@ -2233,7 +2430,7 @@

Files

let name = name.clone(); let api = api.clone(); async move { - let route = &req.state().clone().apis[&api][&version][&name]; + let route = &req.state().clone().modules[&api].versions[&version][&name]; route .default_handler() .map_err(|err| match err { @@ -2252,12 +2449,13 @@

Files

next: tide::Next<Arc<Self>>, ) -> BoxFuture<tide::Result> { async move { - let Some(mut path) = req.url().path_segments() else { + let Some(path) = req.url().path_segments() else { // If we can't parse the path, we can't run this middleware. Do our best by // continuing the request processing lifecycle. return Ok(next.run(req).await); }; - let Some(seg1) = path.next() else { + let path = path.collect::<Vec<_>>(); + let Some(seg1) = path.first() else { // This is the root URL, with no path segments. Nothing for this middleware to do. return Ok(next.run(req).await); }; @@ -2266,32 +2464,25 @@

Files

return Ok(next.run(req).await); } - // The first segment is either a version identifier or an API identifier (implicitly - // requesting the latest version of the API). We handle these cases differently. + // The first segment is either a version identifier or (part of) an API identifier + // (implicitly requesting the latest version of the API). We handle these cases + // differently. if let Some(version) = seg1.strip_prefix('v').and_then(|n| n.parse().ok()) { // If the version identifier is present, we probably don't need a redirect. However, // we still check if this is a valid version for the request API. If not, we will // serve documentation listing the available versions. - let Some(api) = path.next() else { - // A version identifier with no API is an error, serve documentation. - return Ok(Self::top_level_error( - req, - StatusCode::BadRequest, - "illegal version prefix without API specifier", - )); - }; - let Some(versions) = req.state().apis.get(api) else { - let message = format!("No API matches /{api}"); + let Some(module) = req.state().modules.search(&path[1..]) else { + let message = format!("No API matches /{}", path[1..].join("/")); return Ok(Self::top_level_error(req, StatusCode::NotFound, message)); }; - if versions.get(&version).is_none() { + if module.versions.get(&version).is_none() { // This version is not supported, list suported versions. return Ok(html! { "Unsupported version v" (version) ". Supported versions are:" ul { - @for v in versions.keys().rev() { + @for v in module.versions.keys().rev() { li { - a href=(format!("/v{v}/{api}")) { "v" (v) } + a href=(format!("/v{v}/{}", module.path())) { "v" (v) } } } } @@ -2303,20 +2494,21 @@

Files

// successfully by the route handlers for this API. Ok(next.run(req).await) } else { - // If the first path segment is not a version prefix, it is either the name of an - // API or one of the magic top-level endpoints (version, healthcheck), implicitly - // requesting the latest version. Validate the API and then redirect. - if ["version", "healthcheck"].contains(&seg1) { + // If the first path segment is not a version prefix, then the path is either the + // name of an API (implicitly requesting the latest version) or one of the magic + // top-level endpoints (version, healthcheck). Validate the API and then redirect. + if !req.state().modules.is_singleton() && ["version", "healthcheck"].contains(seg1) + { return Ok(next.run(req).await); } - let Some(versions) = req.state().apis.get(seg1) else { - let message = format!("No API matches /{seg1}"); + let Some(module) = req.state().modules.search(&path) else { + let message = format!("No API matches /{}", path.join("/")); return Ok(Self::top_level_error(req, StatusCode::NotFound, message)); }; - let latest_version = *versions.last_key_value().unwrap().0; + let latest_version = *module.versions.last_key_value().unwrap().0; let path = path.join("/"); - Ok(tide::Redirect::permanent(format!("/v{latest_version}/{seg1}/{path}")).into()) + Ok(tide::Redirect::permanent(format!("/v{latest_version}/{path}")).into()) } } .boxed() @@ -2394,6 +2586,7 @@

Files

/// Note that if anything goes wrong during module registration (for example, there is already an /// incompatible module registered with the same name), the drop implementation may panic. To handle /// errors without panicking, call [`register`](Self::register) explicitly. +#[derive(Debug)] pub struct Module<'a, State, Error, ModuleError, ModuleVersion> where State: Send + Sync + 'static, @@ -2733,7 +2926,14 @@

Files

.module::<ServerError, StaticVer01>("mod", v1_toml) .unwrap(); api.with_version("1.1.1".parse().unwrap()); - assert_eq!(api.register().unwrap_err(), AppError::ModuleAlreadyExists); + assert_eq!( + api.register().unwrap_err(), + DispatchError::ModuleAlreadyExists { + prefix: "mod".into(), + version: 1, + } + .into() + ); } { let mut v3 = app @@ -3011,10 +3211,7 @@

Files

.text() .await .unwrap(); - assert!( - docs.contains("illegal version prefix without API specifier"), - "{docs}" - ); + assert!(docs.contains("No API matches /"), "{docs}"); assert!(docs.contains(&expected_list_item), "{docs}"); } @@ -3070,6 +3267,8 @@

Files

#[async_std::test] async fn test_format_versions() { + setup_test(); + // Register two modules with different binary format versions, each in turn different from // the app-level version. Each module has two endpoints, one which always succeeds and one // which always fails, so we can test error serialization. @@ -3225,5 +3424,192 @@

Files

check_err::<SerializerV02>(&client, "mod02/err").await; check_err::<SerializerV03>(&client, "mod03/err").await; } + + #[async_std::test] + async fn test_api_prefix() { + setup_test(); + + // It is illegal to register two API modules where one is a prefix (in terms of route + // segments) of another. + for (api1, api2) in [ + ("", "api"), + ("api", ""), + ("path", "path/sub"), + ("path/sub", "path"), + ] { + tracing::info!(api1, api2, "test case"); + let (prefix, conflict) = if api1.len() < api2.len() { + (api1.to_string(), api2.to_string()) + } else { + (api2.to_string(), api1.to_string()) + }; + + let mut app = App::<_, ServerError>::with_state(()); + let toml = toml! { + route = {} + }; + app.module::<ServerError, StaticVer01>(api1, toml.clone()) + .unwrap() + .register() + .unwrap(); + assert_eq!( + app.module::<ServerError, StaticVer01>(api2, toml) + .unwrap() + .register() + .unwrap_err(), + DispatchError::ConflictingModules { prefix, conflict }.into() + ); + } + } + + #[async_std::test] + async fn test_singleton_api() { + setup_test(); + + // If there is only one API, it should be possible to register it with an empty prefix. + let toml = toml! { + [route.test] + PATH = ["/test"] + }; + let mut app = App::<_, ServerError>::with_state(()); + let mut api = app.module::<ServerError, StaticVer01>("", toml).unwrap(); + api.with_version("0.1.0".parse().unwrap()) + .get("test", |_, _| async move { Ok("response") }.boxed()) + .unwrap(); + api.register().unwrap(); + + let port = pick_unused_port().unwrap(); + spawn(app.serve(format!("0.0.0.0:{port}"), StaticVer01::instance())); + let client = Client::new(format!("http://localhost:{port}").parse().unwrap()).await; + + // Test an endpoint. + let res = client.get("/test").send().await.unwrap(); + assert_eq!( + res.status(), + StatusCode::Ok, + "{}", + res.text().await.unwrap() + ); + assert_eq!(res.json::<String>().await.unwrap(), "response"); + + // Test healthcheck and version endpoints. Since these would ordinarily conflict with the + // app-level healthcheck and version endpoints for an API with no prefix, we only get the + // API-level endpoints, so that a singleton API behaves like a normal API, while app-level + // stuff is reserved for non-trivial applications with more than one API. + let res = client.get("/healthcheck").send().await.unwrap(); + assert_eq!(res.status(), StatusCode::Ok); + assert_eq!( + res.json::<HealthStatus>().await.unwrap(), + HealthStatus::Available + ); + + let res = client.get("/version").send().await.unwrap(); + assert_eq!(res.status(), StatusCode::Ok); + assert_eq!( + res.json::<ApiVersion>().await.unwrap(), + ApiVersion { + api_version: Some("0.1.0".parse().unwrap()), + spec_version: "0.1.0".parse().unwrap(), + }, + ); + } + + #[async_std::test] + async fn test_multi_segment() { + setup_test(); + + let toml = toml! { + [route.test] + PATH = ["/test"] + }; + let mut app = App::<_, ServerError>::with_state(()); + + for name in ["a", "b"] { + let path = format!("api/{name}"); + let mut api = app + .module::<ServerError, StaticVer01>(&path, toml.clone()) + .unwrap(); + api.with_version("0.1.0".parse().unwrap()) + .get("test", move |_, _| async move { Ok(name) }.boxed()) + .unwrap(); + api.register().unwrap(); + } + + let port = pick_unused_port().unwrap(); + spawn(app.serve(format!("0.0.0.0:{port}"), StaticVer01::instance())); + let client = Client::new(format!("http://localhost:{port}").parse().unwrap()).await; + + for api in ["a", "b"] { + tracing::info!(api, "testing api"); + + // Test an endpoint. + let res = client.get(&format!("api/{api}/test")).send().await.unwrap(); + assert_eq!(res.status(), StatusCode::Ok); + assert_eq!(res.json::<String>().await.unwrap(), api); + + // Test healthcheck. + let res = client + .get(&format!("api/{api}/healthcheck")) + .send() + .await + .unwrap(); + assert_eq!(res.status(), StatusCode::Ok); + assert_eq!( + res.json::<HealthStatus>().await.unwrap(), + HealthStatus::Available + ); + + // Test version. + let res = client + .get(&format!("api/{api}/version")) + .send() + .await + .unwrap(); + assert_eq!(res.status(), StatusCode::Ok); + assert_eq!( + res.json::<ApiVersion>().await.unwrap().api_version.unwrap(), + "0.1.0".parse().unwrap() + ); + } + + // Test app-level healthcheck. + let res = client.get("healthcheck").send().await.unwrap(); + assert_eq!(res.status(), StatusCode::Ok); + assert_eq!( + res.json::<AppHealth>().await.unwrap(), + AppHealth { + status: HealthStatus::Available, + modules: [ + ("api/a".into(), [(0, StatusCode::Ok)].into()), + ("api/b".into(), [(0, StatusCode::Ok)].into()), + ] + .into() + } + ); + + // Test app-level version. + let res = client.get("version").send().await.unwrap(); + assert_eq!(res.status(), StatusCode::Ok); + assert_eq!( + res.json::<AppVersion>().await.unwrap().modules, + [ + ( + "api/a".into(), + vec![ApiVersion { + api_version: Some("0.1.0".parse().unwrap()), + spec_version: "0.1.0".parse().unwrap(), + }] + ), + ( + "api/b".into(), + vec![ApiVersion { + api_version: Some("0.1.0".parse().unwrap()), + spec_version: "0.1.0".parse().unwrap(), + }] + ), + ] + .into() + ); + } }
\ No newline at end of file diff --git a/src/tide_disco/dispatch.rs.html b/src/tide_disco/dispatch.rs.html new file mode 100644 index 00000000..c88972c3 --- /dev/null +++ b/src/tide_disco/dispatch.rs.html @@ -0,0 +1,709 @@ +dispatch.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+
use itertools::Itertools;
+use snafu::Snafu;
+use std::{
+    collections::{btree_map::Entry, BTreeMap},
+    ops::Index,
+};
+
+pub use crate::join;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub(crate) struct Module<Api> {
+    pub(crate) prefix: Vec<String>,
+    pub(crate) versions: BTreeMap<u64, Api>,
+}
+
+impl<Api> Module<Api> {
+    fn new(prefix: Vec<String>) -> Self {
+        Self {
+            prefix,
+            versions: Default::default(),
+        }
+    }
+
+    pub(crate) fn path(&self) -> String {
+        self.prefix.join("/")
+    }
+}
+
+#[derive(Clone, Debug, Snafu, PartialEq, Eq)]
+pub enum DispatchError {
+    #[snafu(display("duplicate module {prefix} v{version}"))]
+    ModuleAlreadyExists { prefix: String, version: u64 },
+    #[snafu(display("module {prefix} cannot be a prefix of module {conflict}"))]
+    ConflictingModules { prefix: String, conflict: String },
+}
+
+/// Mapping from route prefixes to APIs.
+#[derive(Debug)]
+pub(crate) enum Trie<Api> {
+    Branch {
+        /// The route prefix represented by this node.
+        prefix: Vec<String>,
+        /// APIs with this prefix, indexed by the next route segment.
+        children: BTreeMap<String, Box<Self>>,
+    },
+    Leaf {
+        /// APIs available at this prefix, sorted by version.
+        module: Module<Api>,
+    },
+}
+
+impl<Api> Default for Trie<Api> {
+    fn default() -> Self {
+        Self::Branch {
+            prefix: vec![],
+            children: Default::default(),
+        }
+    }
+}
+
+impl<Api> Trie<Api> {
+    /// Whether this is a singleton [`Trie`].
+    ///
+    /// A singleton [`Trie`] is one with only one module, registered under the empty prefix. Note
+    /// that any [`Trie`] with a module with an empty prefix must be singleton, because no other
+    /// modules would be permitted: the empty prefix is a prefix of every other module path.
+    pub(crate) fn is_singleton(&self) -> bool {
+        matches!(self, Self::Leaf { .. })
+    }
+
+    /// Insert a new API with a certain version under the given prefix.
+    pub(crate) fn insert<I>(
+        &mut self,
+        prefix: I,
+        version: u64,
+        api: Api,
+    ) -> Result<(), DispatchError>
+    where
+        I: IntoIterator,
+        I::Item: Into<String>,
+    {
+        let mut prefix = prefix.into_iter().map(|segment| segment.into());
+
+        // Traverse to a leaf matching `prefix`.
+        let mut curr = self;
+        while let Some(segment) = prefix.next() {
+            // If there are more segments in the prefix, we must be at a branch.
+            match curr {
+                Self::Branch { prefix, children } => {
+                    // Move to the child associated with the next path segment, inserting an empty
+                    // child if this is the first module we've seen that has this path as a prefix.
+                    curr = children.entry(segment.clone()).or_insert_with(|| {
+                        let mut prefix = prefix.clone();
+                        prefix.push(segment);
+                        Box::new(Trie::Branch {
+                            prefix,
+                            children: Default::default(),
+                        })
+                    });
+                }
+                Self::Leaf { module } => {
+                    // If there is a leaf here, then there is already a module registered which is a
+                    // prefix of the new module. This is not allowed.
+                    return Err(DispatchError::ConflictingModules {
+                        prefix: module.path(),
+                        conflict: join!(&module.path(), &segment, &prefix.join("/")),
+                    });
+                }
+            }
+        }
+
+        // If we have reached the end of the prefix, we must be at either a leaf or a temporary
+        // empty branch that we can turn into a leaf.
+        if let Self::Branch { prefix, children } = curr {
+            if children.is_empty() {
+                *curr = Self::Leaf {
+                    module: Module::new(prefix.clone()),
+                };
+            } else {
+                // If we have a non-trival branch at the end of the desired prefix, there is already
+                // a module registered for which `prefix` is a strict prefix of the registered path.
+                // This is not allowed. To give a useful error message, follow the existing trie
+                // down to a leaf so we can give an example of a module which conflicts with this
+                // prefix.
+                let prefix = prefix.join("/");
+                let conflict = loop {
+                    match curr {
+                        Self::Branch { children, .. } => {
+                            curr = children
+                                .values_mut()
+                                .next()
+                                .expect("malformed dispatch trie: empty branch");
+                        }
+                        Self::Leaf { module } => {
+                            break module.path();
+                        }
+                    }
+                };
+                return Err(DispatchError::ConflictingModules { prefix, conflict });
+            }
+        }
+        let Self::Leaf { module } = curr else {
+            unreachable!();
+        };
+
+        // Insert the new API, as long as there isn't already an API with the same version in this
+        // module.
+        let Entry::Vacant(e) = module.versions.entry(version) else {
+            return Err(DispatchError::ModuleAlreadyExists {
+                prefix: module.path(),
+                version,
+            });
+        };
+        e.insert(api);
+        Ok(())
+    }
+
+    /// Get the module named by `prefix`.
+    ///
+    /// This function is similar to [`search`](Self::search), except the given `prefix` must exactly
+    /// match the prefix under which a module is registered.
+    pub(crate) fn get<I>(&self, prefix: I) -> Option<&Module<Api>>
+    where
+        I: IntoIterator,
+        I::Item: AsRef<str>,
+    {
+        let mut iter = prefix.into_iter();
+        let module = self.traverse(&mut iter)?;
+        // Check for exact match.
+        if iter.next().is_some() {
+            None
+        } else {
+            Some(module)
+        }
+    }
+
+    /// Get the supported versions of the API identified by the given request path.
+    ///
+    /// If a prefix of `path` uniquely identifies a registered module, the module (with all
+    /// supported versions) is returned.
+    pub(crate) fn search<I>(&self, path: I) -> Option<&Module<Api>>
+    where
+        I: IntoIterator,
+        I::Item: AsRef<str>,
+    {
+        self.traverse(&mut path.into_iter())
+    }
+
+    /// Iterate over registered modules and their supported versions.
+    pub(crate) fn iter(&self) -> Iter<Api> {
+        Iter { stack: vec![self] }
+    }
+
+    /// Internal implementation of `get` and `search`.
+    ///
+    /// Returns the matching module and advances the iterator past all the segments used in the
+    /// match.
+    fn traverse<I>(&self, iter: &mut I) -> Option<&Module<Api>>
+    where
+        I: Iterator,
+        I::Item: AsRef<str>,
+    {
+        let mut curr = self;
+        loop {
+            match curr {
+                Self::Branch { children, .. } => {
+                    // Traverse to the next child based on the next segment in the path.
+                    let segment = iter.next()?;
+                    curr = children.get(segment.as_ref())?;
+                }
+                Self::Leaf { module } => return Some(module),
+            }
+        }
+    }
+}
+
+pub(crate) struct Iter<'a, Api> {
+    stack: Vec<&'a Trie<Api>>,
+}
+
+impl<'a, Api> Iterator for Iter<'a, Api> {
+    type Item = &'a Module<Api>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        loop {
+            match self.stack.pop()? {
+                Trie::Branch { children, .. } => {
+                    // Push children onto the stack and start visiting them. We add them in reverse
+                    // order so that we will visit the lexicographically first children first.
+                    self.stack
+                        .extend(children.values().rev().map(|boxed| &**boxed));
+                }
+                Trie::Leaf { module } => return Some(module),
+            }
+        }
+    }
+}
+
+impl<'a, Api> IntoIterator for &'a Trie<Api> {
+    type IntoIter = Iter<'a, Api>;
+    type Item = &'a Module<Api>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
+    }
+}
+
+impl<I, Api> Index<I> for Trie<Api>
+where
+    I: IntoIterator,
+    I::Item: AsRef<str>,
+{
+    type Output = Module<Api>;
+
+    fn index(&self, index: I) -> &Self::Output {
+        self.get(index).unwrap()
+    }
+}
+
+/// Split a path prefix into its segments.
+///
+/// Leading and trailing slashes are ignored. That is, `/prefix/` yields only the single segment
+/// `prefix`, with no preceding or following empty segments.
+pub(crate) fn split(s: &str) -> impl '_ + Iterator<Item = &str> {
+    s.split('/').filter(|seg| !seg.is_empty())
+}
+
+/// Join two path strings, ensuring there are no leading or trailing slashes.
+pub(crate) fn join(s1: &str, s2: &str) -> String {
+    let s1 = s1.strip_prefix('/').unwrap_or(s1);
+    let s1 = s1.strip_suffix('/').unwrap_or(s1);
+    let s2 = s2.strip_prefix('/').unwrap_or(s2);
+    let s2 = s2.strip_suffix('/').unwrap_or(s2);
+    if s1.is_empty() {
+        s2.to_string()
+    } else if s2.is_empty() {
+        s1.to_string()
+    } else {
+        format!("{s1}/{s2}")
+    }
+}
+
+#[macro_export]
+macro_rules! join {
+    () => { String::new() };
+    ($s:expr) => { $s };
+    ($head:expr$(, $($tail:expr),*)?) => {
+        $crate::dispatch::join($head, &$crate::join!($($($tail),*)?))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_empty_trie() {
+        let t = Trie::<()>::default();
+        assert_eq!(t.iter().next(), None);
+        assert_eq!(t.get(["mod"]), None);
+    }
+
+    #[test]
+    fn test_branch_trie() {
+        let mut t = Trie::default();
+
+        let mod_a = Module {
+            prefix: vec!["mod".into(), "a".into()],
+            versions: [(0, 0)].into(),
+        };
+        let mod_b = Module {
+            prefix: vec!["mod".into(), "b".into()],
+            versions: [(1, 1)].into(),
+        };
+
+        t.insert(["mod", "a"], 0, 0).unwrap();
+        t.insert(["mod", "b"], 1, 1).unwrap();
+
+        assert_eq!(t.iter().collect::<Vec<_>>(), [&mod_a, &mod_b]);
+
+        assert_eq!(t.search(["mod", "a", "route"]), Some(&mod_a));
+        assert_eq!(t.get(["mod", "a"]), Some(&mod_a));
+        assert_eq!(t.get(["mod", "a", "route"]), None);
+
+        assert_eq!(t.search(["mod", "b", "route"]), Some(&mod_b));
+        assert_eq!(t.get(["mod", "b"]), Some(&mod_b));
+        assert_eq!(t.get(["mod", "b", "route"]), None);
+
+        // Cannot register a module which is a prefix or suffix of the already registered modules.
+        t.insert(["mod"], 0, 0).unwrap_err();
+        t.insert(Vec::<String>::new(), 0, 0).unwrap_err();
+        t.insert(["mod", "a", "b"], 0, 0).unwrap_err();
+    }
+
+    #[test]
+    fn test_null_prefix() {
+        let mut t = Trie::default();
+
+        let module = Module {
+            prefix: vec![],
+            versions: [(0, 0)].into(),
+        };
+        t.insert(Vec::<String>::new(), 0, 0).unwrap();
+
+        assert_eq!(t.iter().collect::<Vec<_>>(), [&module]);
+        assert_eq!(t.search(["anything"]), Some(&module));
+        assert_eq!(t.get(Vec::<String>::new()), Some(&module));
+        assert_eq!(t.get(["anything"]), None);
+
+        // Any other module has the null module as a prefix and is thus not allowed.
+        t.insert(["anything"], 1, 1).unwrap_err();
+    }
+}
+
\ No newline at end of file diff --git a/src/tide_disco/error.rs.html b/src/tide_disco/error.rs.html index b67862f4..31d36516 100644 --- a/src/tide_disco/error.rs.html +++ b/src/tide_disco/error.rs.html @@ -1,4 +1,4 @@ -error.rs - source