#![allow(async_fn_in_trait)] use api_macros::{ ApiContract, BinaryBody, HttpMethod, NoBody, Operation, ParameterLocation, TransportMetadata, WebSocketOperation, WireKind, api, }; pub struct CreateWidget; pub struct Widget; pub struct Lookup; pub struct PublicError; pub struct SearchResult(std::marker::PhantomData); pub struct ClientFrame; pub struct ServerFrame; #[api] pub trait MixedTransportApi { #[get("/status")] async fn status(&self) -> Widget; #[websocket( "/widgets/{widget_id}/events", operation_id = "widgets.events", method = GET, client_to_server = ClientFrame, server_to_client = ServerFrame, path_parameters = [widget_id: u64] )] type WidgetEvents; } #[api] pub trait WidgetApi { #[post( "/widgets", operation_id = "widgets.create", status = 201, error_status = 422 )] async fn create( &self, #[body] request: CreateWidget, #[header("x-request-id")] request_id: String, ) -> Result; #[get("/widgets/{widget_id}", operation_id = "widgets.get")] async fn lookup(&self, widget_id: u64, #[query] query: Lookup) -> Result; #[delete("/widgets/{widget_id}", operation_id = "widgets.delete")] async fn delete(&self, #[path] widget_id: u64) -> (); } #[api] pub trait WrapperApi { #[get("/search", operation_id = "search")] async fn search(&self) -> SearchResult; } #[api] pub trait BinaryApi { #[put("/payload", operation_id = "payload.upload")] async fn upload(&self, #[binary] payload: BinaryBody) -> Widget; } #[api] pub trait ConditionalApi { #[get( "/conditional", operation_id = "conditional.get", responses = [ (status = 200, body = Widget, headers = [("etag", String), ("cache-control", String)]), (status = 304, headers = [("etag", String), ("cache-control", String)]) ] )] async fn fetch(&self) -> conditional_api_responses::Fetch; } #[api] pub trait RawIdentifierApi { #[get("/type", operation_id = "raw.type")] async fn r#type(&self) -> Widget; } #[api] pub trait UnusualIdentifierApi { #[get("/underscores", operation_id = "unusual.underscores")] async fn __(&self) -> Widget; #[get("/underscore-digit", operation_id = "unusual.underscore_digit")] async fn _0(&self) -> Widget; } fn assert_operation_types() where O: Operation< Parameters = (CreateWidget, String), RequestBody = CreateWidget, ResponseBody = Widget, ErrorBody = PublicError, >, { } #[test] fn expansion_exposes_deterministic_metadata_and_type_connections() { assert_operation_types::(); fn assert_named_wrapper>>() {} assert_named_wrapper::(); let raw = ::METADATA; assert_eq!(raw.operation_id, "raw.type"); let underscores = ::METADATA; assert_eq!(underscores.operation_id, "unusual.underscores"); let underscore_digit = ::METADATA; assert_eq!(underscore_digit.operation_id, "unusual.underscore_digit"); let operations = ::OPERATIONS; assert_eq!(operations.len(), 3); assert_eq!(operations[0].operation_id, "widgets.create"); assert_eq!(operations[1].operation_id, "widgets.delete"); assert_eq!(operations[2].operation_id, "widgets.get"); let create = ::METADATA; assert_eq!(operations[0], create); assert_eq!(create.method, HttpMethod::Post); assert_eq!(create.path, "/widgets"); assert_eq!(create.transport, TransportMetadata::Http); assert_eq!(create.request_body.wire_kind, WireKind::Json); assert_eq!(create.success_responses[0].status, 201); assert_eq!(create.success_responses[0].body.wire_kind, WireKind::Json); assert_eq!(create.error_responses[0].status, 422); assert_eq!(create.parameters[0].location, ParameterLocation::Body); assert_eq!(create.parameters[0].rust_type, "CreateWidget"); assert_eq!(create.parameters[1].location, ParameterLocation::Header); assert_eq!(create.parameters[1].wire_name, "x-request-id"); let lookup = ::METADATA; assert_eq!(lookup.parameters[0].location, ParameterLocation::Path); assert_eq!(lookup.parameters[1].location, ParameterLocation::Query); let upload = ::METADATA; assert_eq!(upload.request_body.wire_kind, WireKind::Binary); assert_eq!(upload.parameters[0].location, ParameterLocation::Body); fn assert_binary_request>() {} assert_binary_request::(); let conditional = ::METADATA; assert_eq!(conditional.success_responses.len(), 2); assert_eq!(conditional.success_responses[0].status, 200); assert_eq!( conditional.success_responses[0].body.wire_kind, WireKind::Json ); assert_eq!(conditional.success_responses[0].headers.len(), 2); assert_eq!( conditional.success_responses[0].headers[0].wire_name, "etag" ); assert_eq!( conditional.success_responses[0].headers[0].rust_type, "String" ); assert_eq!(conditional.success_responses[1].status, 304); assert_eq!( conditional.success_responses[1].body.wire_kind, WireKind::Empty ); fn assert_conditional_response< O: Operation, >() { } assert_conditional_response::(); let delete = ::METADATA; assert_eq!(delete.success_responses[0].status, 204); assert_eq!(delete.success_responses[0].body.wire_kind, WireKind::Empty); assert!(delete.error_responses.is_empty()); fn assert_empty_request>() {} fn assert_empty_response>() {} assert_empty_request::(); assert_empty_response::(); fn assert_websocket_types< O: WebSocketOperation< PathParameters = (u64,), ClientToServerFrame = ClientFrame, ServerToClientFrame = ServerFrame, >, >() { } assert_websocket_types::(); let websocket = ::METADATA; assert_eq!(websocket.method, HttpMethod::Get); assert_eq!(websocket.path, "/widgets/{widget_id}/events"); assert_eq!(websocket.parameters[0].rust_name, "widget_id"); assert_eq!(websocket.parameters[0].rust_type, "u64"); assert!(websocket.success_responses.is_empty()); assert!(websocket.error_responses.is_empty()); assert_eq!( websocket.transport, TransportMetadata::WebSocket { client_to_server_frame: "ClientFrame", server_to_client_frame: "ServerFrame", } ); }