diff --git a/waydowntown/openapi.yaml b/waydowntown/openapi.yaml new file mode 100644 index 00000000..db50e7f2 --- /dev/null +++ b/waydowntown/openapi.yaml @@ -0,0 +1,223 @@ +openapi: 3.0.0 +info: + title: Games API + version: 1.0.0 +paths: + /games: + post: + summary: Create a new game + parameters: + - in: query + name: concept + schema: + type: string + required: false + description: Optional concept for the game + - in: query + name: incarnation_filter[concept] + schema: + type: string + required: false + description: Filter incarnations by concept + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/GameInput" + responses: + "201": + description: Successfully created game + content: + application/json: + schema: + type: object + properties: + data: + $ref: "#/components/schemas/Game" + included: + type: array + items: + oneOf: + - $ref: "#/components/schemas/Incarnation" + - $ref: "#/components/schemas/Region" + /games/{id}: + get: + summary: Fetch a game + parameters: + - in: path + name: id + required: true + schema: + type: string + format: uuid + responses: + "200": + description: Successfully fetched game + content: + application/json: + schema: + type: object + properties: + data: + $ref: "#/components/schemas/Game" + included: + type: array + items: + oneOf: + - $ref: "#/components/schemas/Incarnation" + - $ref: "#/components/schemas/Region" + - $ref: "#/components/schemas/Answer" + /answers: + post: + summary: Create a new answer + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AnswerInput" + responses: + "201": + description: Successfully created answer + content: + application/json: + schema: + $ref: "#/components/schemas/Answer" +components: + schemas: + GameInput: + type: object + properties: + incarnation_id: + type: string + format: uuid + Game: + type: object + properties: + id: + type: string + format: uuid + attributes: + type: object + properties: + complete: + type: boolean + relationships: + type: object + properties: + incarnation: + type: object + properties: + data: + type: object + properties: + id: + type: string + format: uuid + winner_answer: + type: object + properties: + data: + type: object + nullable: true + properties: + id: + type: string + format: uuid + Incarnation: + type: object + properties: + id: + type: string + format: uuid + attributes: + type: object + properties: + concept: + type: string + mask: + type: string + answer: + type: string + answers: + type: array + items: + type: string + relationships: + type: object + properties: + region: + type: object + properties: + data: + type: object + properties: + id: + type: string + format: uuid + Region: + type: object + properties: + id: + type: string + format: uuid + attributes: + type: object + properties: + name: + type: string + description: + type: string + relationships: + type: object + properties: + parent: + type: object + properties: + data: + type: object + nullable: true + properties: + id: + type: string + format: uuid + AnswerInput: + type: object + properties: + answer: + type: string + game: + type: object + properties: + data: + type: object + properties: + id: + type: string + format: uuid + Answer: + type: object + properties: + id: + type: string + format: uuid + attributes: + type: object + properties: + answer: + type: string + correct: + type: boolean + relationships: + type: object + properties: + game: + type: object + properties: + data: + type: object + properties: + id: + type: string + format: uuid diff --git a/waydowntown/waydowntown-server-axum/.gitignore b/waydowntown/waydowntown-server-axum/.gitignore new file mode 100644 index 00000000..a9d37c56 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/waydowntown/waydowntown-server-axum/.openapi-generator-ignore b/waydowntown/waydowntown-server-axum/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/waydowntown/waydowntown-server-axum/.openapi-generator/FILES b/waydowntown/waydowntown-server-axum/.openapi-generator/FILES new file mode 100644 index 00000000..683914c4 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/.openapi-generator/FILES @@ -0,0 +1,10 @@ +.gitignore +Cargo.toml +README.md +src/apis/default.rs +src/apis/mod.rs +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/types.rs diff --git a/waydowntown/waydowntown-server-axum/.openapi-generator/VERSION b/waydowntown/waydowntown-server-axum/.openapi-generator/VERSION new file mode 100644 index 00000000..1985849f --- /dev/null +++ b/waydowntown/waydowntown-server-axum/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.7.0 diff --git a/waydowntown/waydowntown-server-axum/Cargo.toml b/waydowntown/waydowntown-server-axum/Cargo.toml new file mode 100644 index 00000000..9beed129 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "waydowntown-server" +version = "1.0.0" +authors = ["OpenAPI Generator team and contributors"] +description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)" +edition = "2021" + +[features] +default = ["server"] +server = [] +conversion = [ + "frunk", + "frunk_derives", + "frunk_core", + "frunk-enum-core", + "frunk-enum-derive", +] + +[dependencies] +async-trait = "0.1" +axum = { version = "0.7" } +axum-extra = { version = "0.9", features = ["cookie", "multipart"] } +base64 = "0.22" +bytes = "1" +chrono = { version = "0.4", features = ["serde"] } +frunk = { version = "0.4", optional = true } +frunk-enum-core = { version = "0.3", optional = true } +frunk-enum-derive = { version = "0.3", optional = true } +frunk_core = { version = "0.4", optional = true } +frunk_derives = { version = "0.4", optional = true } +http = "1" +lazy_static = "1" +regex = "1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1", features = ["raw_value"] } +serde_urlencoded = "0.7" +sqlx = { version = "0.7", features = [ + "runtime-tokio-rustls", + "postgres", + "uuid", + "chrono", +] } +tokio = { version = "1", default-features = false, features = [ + "signal", + "rt-multi-thread", +] } +tracing = { version = "0.1", features = ["attributes"] } +tracing-subscriber = "0.3" +uuid = { version = "1", features = ["serde"] } +validator = { version = "0.18", features = ["derive"] } + +[dev-dependencies] +tracing-subscriber = "0.3" diff --git a/waydowntown/waydowntown-server-axum/README.md b/waydowntown/waydowntown-server-axum/README.md new file mode 100644 index 00000000..ad996c8e --- /dev/null +++ b/waydowntown/waydowntown-server-axum/README.md @@ -0,0 +1,92 @@ +# Rust API for waydowntown-server + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +## Overview + +This server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: [README]((https://openapi-generator.tech)) + +- API version: 1.0.0 +- Build date: 2024-08-17T11:31:42.596392-05:00[America/Winnipeg] +- Generator version: 7.7.0 + + + +This autogenerated project defines an API crate `waydowntown-server` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* Axum router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + * Request validations (path, query, body params) are included. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on Axum. + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +### Example + +```rust +struct ServerImpl { + // database: sea_orm::DbConn, +} + +#[allow(unused_variables)] +#[async_trait] +impl waydowntown-server::Api for ServerImpl { + // API implementation goes here +} + +pub async fn start_server(addr: &str) { + // initialize tracing + tracing_subscriber::fmt::init(); + + // Init Axum router + let app = waydowntown-server::server::new(Arc::new(ServerImpl)); + + // Add layers to the router + let app = app.layer(...); + + // Run the server with graceful shutdown + let listener = TcpListener::bind(addr).await.unwrap(); + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await + .unwrap(); +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } +} +``` diff --git a/waydowntown/waydowntown-server-axum/src/apis/default.rs b/waydowntown/waydowntown-server-axum/src/apis/default.rs new file mode 100644 index 00000000..ceb50f89 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/src/apis/default.rs @@ -0,0 +1,75 @@ +use async_trait::async_trait; +use axum::extract::*; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::Method; +use serde::{Deserialize, Serialize}; + +use crate::{models, types::*}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum AnswersPostResponse { + /// Successfully created answer + Status201_SuccessfullyCreatedAnswer + (models::Answer) +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GamesIdGetResponse { + /// Successfully fetched game + Status200_SuccessfullyFetchedGame + (models::GamesIdGet200Response) +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[must_use] +#[allow(clippy::large_enum_variant)] +pub enum GamesPostResponse { + /// Successfully created game + Status201_SuccessfullyCreatedGame + (models::GamesPost201Response) +} + + +/// Default +#[async_trait] +#[allow(clippy::ptr_arg)] +pub trait Default { + /// Create a new answer. + /// + /// AnswersPost - POST /answers + async fn answers_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + body: models::AnswerInput, + ) -> Result; + + /// Fetch a game. + /// + /// GamesIdGet - GET /games/{id} + async fn games_id_get( + &self, + method: Method, + host: Host, + cookies: CookieJar, + path_params: models::GamesIdGetPathParams, + ) -> Result; + + /// Create a new game. + /// + /// GamesPost - POST /games + async fn games_post( + &self, + method: Method, + host: Host, + cookies: CookieJar, + query_params: models::GamesPostQueryParams, + body: models::GameInput, + ) -> Result; +} diff --git a/waydowntown/waydowntown-server-axum/src/apis/mod.rs b/waydowntown/waydowntown-server-axum/src/apis/mod.rs new file mode 100644 index 00000000..84831b18 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/src/apis/mod.rs @@ -0,0 +1,2 @@ +pub mod default; + diff --git a/waydowntown/waydowntown-server-axum/src/header.rs b/waydowntown/waydowntown-server-axum/src/header.rs new file mode 100644 index 00000000..5c714f8c --- /dev/null +++ b/waydowntown/waydowntown-server-axum/src/header.rs @@ -0,0 +1,181 @@ +use std::{convert::TryFrom, fmt, ops::Deref}; + +use chrono::{DateTime, Utc}; +use http::HeaderValue; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in http::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse {} as a string: {}", + stringify!($t), e)), + }, + Err(e) => Err(format!("Unable to parse header {:?} as a string - {}", + hdr_value, e)), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect())), + Err(e) => Err(format!("Unable to parse header: {:?} as a string - {}", + hdr_value, e)), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} into a header - {}", + hdr_value, e)) + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", + hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} from a header {}", + hdr_value, e)) + } + } +} + +// Bool + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", + hdr_value, e)), + }, + Err(e) => Err(format!("Unable to convert {:?} from a header {}", + hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert: {:?} into a header: {}", + hdr_value, e)) + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", + hdr_value, e)), + }, + Err(e) => Err(format!("Unable to convert header {:?} to string {}", + hdr_value, e)), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} to a header: {}", + hdr_value, e)), + } + } +} + diff --git a/waydowntown/waydowntown-server-axum/src/lib.rs b/waydowntown/waydowntown-server-axum/src/lib.rs new file mode 100644 index 00000000..24a4c6f4 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/src/lib.rs @@ -0,0 +1,28 @@ +#![allow( + missing_docs, + trivial_casts, + unused_variables, + unused_mut, + unused_extern_crates, + non_camel_case_types, + unused_imports, + unused_attributes, +)] +#![allow( + clippy::derive_partial_eq_without_eq, + clippy::disallowed_names, + clippy::too_many_arguments +)] + +pub const BASE_PATH: &str = ""; +pub const API_VERSION: &str = "1.0.0"; + +#[cfg(feature = "server")] +pub mod server; + +pub mod models; +pub mod types; +pub mod apis; + +#[cfg(feature = "server")] +pub(crate) mod header; diff --git a/waydowntown/waydowntown-server-axum/src/main.rs b/waydowntown/waydowntown-server-axum/src/main.rs new file mode 100644 index 00000000..cec9f540 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/src/main.rs @@ -0,0 +1,152 @@ +use std::net::SocketAddr; +use std::sync::Arc; +use tokio::signal; +use waydowntown_server::server; +use waydowntown_server::models; +use waydowntown_server::apis::default::{self, Default as WaydowntownDefault}; +use sqlx::postgres::PgPoolOptions; +use sqlx::PgPool; + +struct ServerImpl { + db: PgPool, +} + +#[async_trait::async_trait] +impl WaydowntownDefault for ServerImpl { + async fn answers_post( + &self, + method: axum::http::Method, + host: axum::extract::Host, + cookies: axum_extra::extract::CookieJar, + body: models::AnswerInput, + ) -> Result { + let answer = sqlx::query_as!( + models::Answer, + r#" + INSERT INTO waydowntown.answers (game_id, answer) + VALUES ($1, $2) + RETURNING id, answer, correct + "#, + body.game.data.id, + body.answer + ) + .fetch_one(&self.db) + .await + .map_err(|e| e.to_string())?; + + Ok(default::AnswersPostResponse::Status201_SuccessfullyCreatedAnswer(answer)) + } + + async fn games_id_get( + &self, + method: axum::http::Method, + host: axum::extract::Host, + cookies: axum_extra::extract::CookieJar, + path_params: models::GamesIdGetPathParams, + ) -> Result { + let game = sqlx::query_as!( + models::Game, + r#" + SELECT id, CASE WHEN winner_answer_id IS NOT NULL THEN true ELSE false END as complete + FROM waydowntown.games + WHERE id = $1 + "#, + path_params.id + ) + .fetch_one(&self.db) + .await + .map_err(|e| e.to_string())?; + + Ok(default::GamesIdGetResponse::Status200_SuccessfullyFetchedGame( + models::GamesIdGet200Response { + data: game, + included: vec![], + } + )) + } + + async fn games_post( + &self, + method: axum::http::Method, + host: axum::extract::Host, + cookies: axum_extra::extract::CookieJar, + query_params: models::GamesPostQueryParams, + body: models::GameInput, + ) -> Result { + let game = sqlx::query_as!( + models::Game, + r#" + INSERT INTO waydowntown.games (incarnation_id) + VALUES ($1) + RETURNING id, CASE WHEN winner_answer_id IS NOT NULL THEN true ELSE false END as complete + "#, + body.incarnation_id + ) + .fetch_one(&self.db) + .await + .map_err(|e| e.to_string())?; + + Ok(default::GamesPostResponse::Status201_SuccessfullyCreatedGame( + models::GamesPost201Response { + data: game, + included: vec![], + } + )) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Initialize tracing + tracing_subscriber::fmt::init(); + + // Set up database connection + let database_url = "postgres://postgres:postgres@localhost/registrations_dev"; + let db_pool = PgPoolOptions::new() + .max_connections(5) + .connect(database_url) + .await?; + + // Create ServerImpl + let server_impl = Arc::new(ServerImpl { db: db_pool }); + + let app = server::new(server_impl); + + // Run the server + let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); + println!("Listening on {}", addr); + + let listener = tokio::net::TcpListener::bind(addr).await?; + + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await?; + + Ok(()) +} + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } + + println!("Shutdown signal received, starting graceful shutdown"); +} diff --git a/waydowntown/waydowntown-server-axum/src/models.rs b/waydowntown/waydowntown-server-axum/src/models.rs new file mode 100644 index 00000000..adca36ac --- /dev/null +++ b/waydowntown/waydowntown-server-axum/src/models.rs @@ -0,0 +1,4020 @@ +#![allow(unused_qualifications)] + +use http::HeaderValue; +use validator::Validate; + +#[cfg(feature = "server")] +use crate::header; +use crate::{models, types::*}; + + + + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] + #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] + pub struct GamesIdGetPathParams { + pub id: uuid::Uuid, + } + + + + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] + #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] + pub struct GamesPostQueryParams { + /// Optional concept for the game + #[serde(rename = "concept")] + #[serde(skip_serializing_if="Option::is_none")] + pub concept: Option, + /// Filter incarnations by concept + #[serde(rename = "incarnation_filter[concept]")] + #[serde(skip_serializing_if="Option::is_none")] + pub incarnation_filter_left_square_bracket_concept_right_square_bracket: Option, + } + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Answer { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if="Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if="Option::is_none")] + pub relationships: Option, + +} + + +impl Answer { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Answer { + Answer { + id: None, + r#type: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the Answer value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for Answer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Answer value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Answer { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing Answer".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing Answer".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Answer { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for Answer - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into Answer - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerAttributes { + #[serde(rename = "answer")] + #[serde(skip_serializing_if="Option::is_none")] + pub answer: Option, + + #[serde(rename = "correct")] + #[serde(skip_serializing_if="Option::is_none")] + pub correct: Option, + +} + + +impl AnswerAttributes { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AnswerAttributes { + AnswerAttributes { + answer: None, + correct: None, + } + } +} + +/// Converts the AnswerAttributes value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for AnswerAttributes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + + self.answer.as_ref().map(|answer| { + [ + "answer".to_string(), + answer.to_string(), + ].join(",") + }), + + + self.correct.as_ref().map(|correct| { + [ + "correct".to_string(), + correct.to_string(), + ].join(",") + }), + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerAttributes value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerAttributes { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub answer: Vec, + pub correct: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing AnswerAttributes".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "answer" => intermediate_rep.answer.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "correct" => intermediate_rep.correct.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing AnswerAttributes".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerAttributes { + answer: intermediate_rep.answer.into_iter().next(), + correct: intermediate_rep.correct.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for AnswerAttributes - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AnswerAttributes - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerInput { + #[serde(rename = "answer")] + #[serde(skip_serializing_if="Option::is_none")] + pub answer: Option, + + #[serde(rename = "game")] + #[serde(skip_serializing_if="Option::is_none")] + pub game: Option, + +} + + +impl AnswerInput { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AnswerInput { + AnswerInput { + answer: None, + game: None, + } + } +} + +/// Converts the AnswerInput value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for AnswerInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + + self.answer.as_ref().map(|answer| { + [ + "answer".to_string(), + answer.to_string(), + ].join(",") + }), + + // Skipping game in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerInput value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerInput { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub answer: Vec, + pub game: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing AnswerInput".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "answer" => intermediate_rep.answer.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "game" => intermediate_rep.game.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing AnswerInput".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerInput { + answer: intermediate_rep.answer.into_iter().next(), + game: intermediate_rep.game.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for AnswerInput - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AnswerInput - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerInputGame { + #[serde(rename = "data")] + #[serde(skip_serializing_if="Option::is_none")] + pub data: Option, + +} + + +impl AnswerInputGame { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AnswerInputGame { + AnswerInputGame { + data: None, + } + } +} + +/// Converts the AnswerInputGame value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for AnswerInputGame { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerInputGame value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerInputGame { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing AnswerInputGame".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing AnswerInputGame".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerInputGame { + data: intermediate_rep.data.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for AnswerInputGame - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AnswerInputGame - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerInputGameData { +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +} + + +impl AnswerInputGameData { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AnswerInputGameData { + AnswerInputGameData { + r#type: None, + id: None, + } + } +} + +/// Converts the AnswerInputGameData value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for AnswerInputGameData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + // Skipping id in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerInputGameData value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerInputGameData { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub r#type: Vec, + pub id: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing AnswerInputGameData".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing AnswerInputGameData".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerInputGameData { + r#type: intermediate_rep.r#type.into_iter().next(), + id: intermediate_rep.id.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for AnswerInputGameData - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AnswerInputGameData - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerRelationships { + #[serde(rename = "game")] + #[serde(skip_serializing_if="Option::is_none")] + pub game: Option, + +} + + +impl AnswerRelationships { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AnswerRelationships { + AnswerRelationships { + game: None, + } + } +} + +/// Converts the AnswerRelationships value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for AnswerRelationships { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping game in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerRelationships value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerRelationships { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub game: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing AnswerRelationships".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "game" => intermediate_rep.game.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing AnswerRelationships".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerRelationships { + game: intermediate_rep.game.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for AnswerRelationships - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AnswerRelationships - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerRelationshipsGame { + #[serde(rename = "data")] + #[serde(skip_serializing_if="Option::is_none")] + pub data: Option, + +} + + +impl AnswerRelationshipsGame { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AnswerRelationshipsGame { + AnswerRelationshipsGame { + data: None, + } + } +} + +/// Converts the AnswerRelationshipsGame value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for AnswerRelationshipsGame { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerRelationshipsGame value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerRelationshipsGame { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing AnswerRelationshipsGame".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing AnswerRelationshipsGame".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerRelationshipsGame { + data: intermediate_rep.data.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for AnswerRelationshipsGame - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AnswerRelationshipsGame - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerRelationshipsGameData { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + +} + + +impl AnswerRelationshipsGameData { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> AnswerRelationshipsGameData { + AnswerRelationshipsGameData { + id: None, + r#type: None, + } + } +} + +/// Converts the AnswerRelationshipsGameData value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for AnswerRelationshipsGameData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerRelationshipsGameData value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerRelationshipsGameData { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing AnswerRelationshipsGameData".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing AnswerRelationshipsGameData".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerRelationshipsGameData { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for AnswerRelationshipsGameData - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AnswerRelationshipsGameData - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Game { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if="Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if="Option::is_none")] + pub relationships: Option, + +} + + +impl Game { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Game { + Game { + id: None, + r#type: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the Game value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for Game { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Game value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Game { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing Game".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing Game".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Game { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for Game - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into Game - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameAttributes { + #[serde(rename = "inserted_at")] + #[serde(skip_serializing_if="Option::is_none")] + pub inserted_at: Option>, + + #[serde(rename = "updated_at")] + #[serde(skip_serializing_if="Option::is_none")] + pub updated_at: Option>, + + #[serde(rename = "complete")] + #[serde(skip_serializing_if="Option::is_none")] + pub complete: Option, + +} + + +impl GameAttributes { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GameAttributes { + GameAttributes { + inserted_at: None, + updated_at: None, + complete: None, + } + } +} + +/// Converts the GameAttributes value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GameAttributes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping inserted_at in query parameter serialization + + // Skipping updated_at in query parameter serialization + + + self.complete.as_ref().map(|complete| { + [ + "complete".to_string(), + complete.to_string(), + ].join(",") + }), + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameAttributes value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameAttributes { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub inserted_at: Vec>, + pub updated_at: Vec>, + pub complete: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GameAttributes".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "inserted_at" => intermediate_rep.inserted_at.push( as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "updated_at" => intermediate_rep.updated_at.push( as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "complete" => intermediate_rep.complete.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing GameAttributes".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameAttributes { + inserted_at: intermediate_rep.inserted_at.into_iter().next(), + updated_at: intermediate_rep.updated_at.into_iter().next(), + complete: intermediate_rep.complete.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameAttributes - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameAttributes - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameInput { + #[serde(rename = "incarnation_id")] + #[serde(skip_serializing_if="Option::is_none")] + pub incarnation_id: Option, + +} + + +impl GameInput { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GameInput { + GameInput { + incarnation_id: None, + } + } +} + +/// Converts the GameInput value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GameInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping incarnation_id in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameInput value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameInput { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub incarnation_id: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GameInput".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "incarnation_id" => intermediate_rep.incarnation_id.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing GameInput".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameInput { + incarnation_id: intermediate_rep.incarnation_id.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameInput - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameInput - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationships { + #[serde(rename = "incarnation")] + #[serde(skip_serializing_if="Option::is_none")] + pub incarnation: Option, + + #[serde(rename = "winner_answer")] + #[serde(skip_serializing_if="Option::is_none")] + pub winner_answer: Option, + +} + + +impl GameRelationships { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GameRelationships { + GameRelationships { + incarnation: None, + winner_answer: None, + } + } +} + +/// Converts the GameRelationships value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GameRelationships { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping incarnation in query parameter serialization + + // Skipping winner_answer in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationships value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationships { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub incarnation: Vec, + pub winner_answer: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GameRelationships".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "incarnation" => intermediate_rep.incarnation.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "winner_answer" => intermediate_rep.winner_answer.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing GameRelationships".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationships { + incarnation: intermediate_rep.incarnation.into_iter().next(), + winner_answer: intermediate_rep.winner_answer.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameRelationships - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationships - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationshipsIncarnation { + #[serde(rename = "data")] + #[serde(skip_serializing_if="Option::is_none")] + pub data: Option, + +} + + +impl GameRelationshipsIncarnation { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GameRelationshipsIncarnation { + GameRelationshipsIncarnation { + data: None, + } + } +} + +/// Converts the GameRelationshipsIncarnation value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GameRelationshipsIncarnation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationshipsIncarnation value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationshipsIncarnation { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GameRelationshipsIncarnation".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing GameRelationshipsIncarnation".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationshipsIncarnation { + data: intermediate_rep.data.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameRelationshipsIncarnation - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationshipsIncarnation - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationshipsIncarnationData { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + +} + + +impl GameRelationshipsIncarnationData { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GameRelationshipsIncarnationData { + GameRelationshipsIncarnationData { + id: None, + r#type: None, + } + } +} + +/// Converts the GameRelationshipsIncarnationData value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GameRelationshipsIncarnationData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationshipsIncarnationData value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationshipsIncarnationData { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GameRelationshipsIncarnationData".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing GameRelationshipsIncarnationData".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationshipsIncarnationData { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameRelationshipsIncarnationData - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationshipsIncarnationData - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationshipsWinnerAnswer { + #[serde(rename = "data")] + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + #[serde(skip_serializing_if="Option::is_none")] + pub data: Option>, + +} + + +impl GameRelationshipsWinnerAnswer { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GameRelationshipsWinnerAnswer { + GameRelationshipsWinnerAnswer { + data: None, + } + } +} + +/// Converts the GameRelationshipsWinnerAnswer value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GameRelationshipsWinnerAnswer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationshipsWinnerAnswer value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationshipsWinnerAnswer { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GameRelationshipsWinnerAnswer".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "data" => return std::result::Result::Err("Parsing a nullable type in this style is not supported in GameRelationshipsWinnerAnswer".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing GameRelationshipsWinnerAnswer".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationshipsWinnerAnswer { + data: std::result::Result::Err("Nullable types not supported in GameRelationshipsWinnerAnswer".to_string())?, + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameRelationshipsWinnerAnswer - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationshipsWinnerAnswer - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationshipsWinnerAnswerData { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + +} + + +impl GameRelationshipsWinnerAnswerData { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GameRelationshipsWinnerAnswerData { + GameRelationshipsWinnerAnswerData { + id: None, + r#type: None, + } + } +} + +/// Converts the GameRelationshipsWinnerAnswerData value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GameRelationshipsWinnerAnswerData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationshipsWinnerAnswerData value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationshipsWinnerAnswerData { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GameRelationshipsWinnerAnswerData".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing GameRelationshipsWinnerAnswerData".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationshipsWinnerAnswerData { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameRelationshipsWinnerAnswerData - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationshipsWinnerAnswerData - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GamesIdGet200Response { + #[serde(rename = "data")] + #[serde(skip_serializing_if="Option::is_none")] + pub data: Option, + + #[serde(rename = "included")] + #[serde(skip_serializing_if="Option::is_none")] + pub included: Option>, + +} + + +impl GamesIdGet200Response { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GamesIdGet200Response { + GamesIdGet200Response { + data: None, + included: None, + } + } +} + +/// Converts the GamesIdGet200Response value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GamesIdGet200Response { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + // Skipping included in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GamesIdGet200Response value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GamesIdGet200Response { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + pub included: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GamesIdGet200Response".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push(::from_str(val).map_err(|x| x.to_string())?), + "included" => return std::result::Result::Err("Parsing a container in this style is not supported in GamesIdGet200Response".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing GamesIdGet200Response".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GamesIdGet200Response { + data: intermediate_rep.data.into_iter().next(), + included: intermediate_rep.included.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GamesIdGet200Response - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GamesIdGet200Response - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + +/// One of: +/// - Answer +/// - Incarnation +/// - Region +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct GamesIdGet200ResponseIncludedInner(Box); + +impl validator::Validate for GamesIdGet200ResponseIncludedInner +{ + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GamesIdGet200ResponseIncludedInner value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GamesIdGet200ResponseIncludedInner { + type Err = serde_json::Error; + + fn from_str(s: &str) -> std::result::Result { + serde_json::from_str(s) + } +} + +impl PartialEq for GamesIdGet200ResponseIncludedInner { + fn eq(&self, other: &Self) -> bool { + self.0.get() == other.0.get() + } +} + + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GamesPost201Response { + #[serde(rename = "data")] + #[serde(skip_serializing_if="Option::is_none")] + pub data: Option, + + #[serde(rename = "included")] + #[serde(skip_serializing_if="Option::is_none")] + pub included: Option>, + +} + + +impl GamesPost201Response { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> GamesPost201Response { + GamesPost201Response { + data: None, + included: None, + } + } +} + +/// Converts the GamesPost201Response value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for GamesPost201Response { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + // Skipping included in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GamesPost201Response value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GamesPost201Response { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + pub included: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing GamesPost201Response".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push(::from_str(val).map_err(|x| x.to_string())?), + "included" => return std::result::Result::Err("Parsing a container in this style is not supported in GamesPost201Response".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing GamesPost201Response".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GamesPost201Response { + data: intermediate_rep.data.into_iter().next(), + included: intermediate_rep.included.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GamesPost201Response - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GamesPost201Response - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + +/// One of: +/// - Incarnation +/// - Region +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct GamesPost201ResponseIncludedInner(Box); + +impl validator::Validate for GamesPost201ResponseIncludedInner +{ + fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> { + std::result::Result::Ok(()) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GamesPost201ResponseIncludedInner value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GamesPost201ResponseIncludedInner { + type Err = serde_json::Error; + + fn from_str(s: &str) -> std::result::Result { + serde_json::from_str(s) + } +} + +impl PartialEq for GamesPost201ResponseIncludedInner { + fn eq(&self, other: &Self) -> bool { + self.0.get() == other.0.get() + } +} + + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Incarnation { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if="Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if="Option::is_none")] + pub relationships: Option, + +} + + +impl Incarnation { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Incarnation { + Incarnation { + id: None, + r#type: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the Incarnation value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for Incarnation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Incarnation value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Incarnation { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing Incarnation".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing Incarnation".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Incarnation { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for Incarnation - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into Incarnation - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct IncarnationAttributes { + #[serde(rename = "concept")] + #[serde(skip_serializing_if="Option::is_none")] + pub concept: Option, + + #[serde(rename = "mask")] + #[serde(skip_serializing_if="Option::is_none")] + pub mask: Option, + + #[serde(rename = "answer")] + #[serde(skip_serializing_if="Option::is_none")] + pub answer: Option, + + #[serde(rename = "answers")] + #[serde(skip_serializing_if="Option::is_none")] + pub answers: Option>, + + #[serde(rename = "inserted_at")] + #[serde(skip_serializing_if="Option::is_none")] + pub inserted_at: Option>, + + #[serde(rename = "updated_at")] + #[serde(skip_serializing_if="Option::is_none")] + pub updated_at: Option>, + +} + + +impl IncarnationAttributes { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> IncarnationAttributes { + IncarnationAttributes { + concept: None, + mask: None, + answer: None, + answers: None, + inserted_at: None, + updated_at: None, + } + } +} + +/// Converts the IncarnationAttributes value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for IncarnationAttributes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + + self.concept.as_ref().map(|concept| { + [ + "concept".to_string(), + concept.to_string(), + ].join(",") + }), + + + self.mask.as_ref().map(|mask| { + [ + "mask".to_string(), + mask.to_string(), + ].join(",") + }), + + + self.answer.as_ref().map(|answer| { + [ + "answer".to_string(), + answer.to_string(), + ].join(",") + }), + + + self.answers.as_ref().map(|answers| { + [ + "answers".to_string(), + answers.iter().map(|x| x.to_string()).collect::>().join(","), + ].join(",") + }), + + // Skipping inserted_at in query parameter serialization + + // Skipping updated_at in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a IncarnationAttributes value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for IncarnationAttributes { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub concept: Vec, + pub mask: Vec, + pub answer: Vec, + pub answers: Vec>, + pub inserted_at: Vec>, + pub updated_at: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing IncarnationAttributes".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "concept" => intermediate_rep.concept.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "mask" => intermediate_rep.mask.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "answer" => intermediate_rep.answer.push(::from_str(val).map_err(|x| x.to_string())?), + "answers" => return std::result::Result::Err("Parsing a container in this style is not supported in IncarnationAttributes".to_string()), + #[allow(clippy::redundant_clone)] + "inserted_at" => intermediate_rep.inserted_at.push( as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "updated_at" => intermediate_rep.updated_at.push( as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing IncarnationAttributes".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(IncarnationAttributes { + concept: intermediate_rep.concept.into_iter().next(), + mask: intermediate_rep.mask.into_iter().next(), + answer: intermediate_rep.answer.into_iter().next(), + answers: intermediate_rep.answers.into_iter().next(), + inserted_at: intermediate_rep.inserted_at.into_iter().next(), + updated_at: intermediate_rep.updated_at.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for IncarnationAttributes - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into IncarnationAttributes - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct IncarnationRelationships { + #[serde(rename = "region")] + #[serde(skip_serializing_if="Option::is_none")] + pub region: Option, + +} + + +impl IncarnationRelationships { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> IncarnationRelationships { + IncarnationRelationships { + region: None, + } + } +} + +/// Converts the IncarnationRelationships value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for IncarnationRelationships { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping region in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a IncarnationRelationships value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for IncarnationRelationships { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub region: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing IncarnationRelationships".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "region" => intermediate_rep.region.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing IncarnationRelationships".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(IncarnationRelationships { + region: intermediate_rep.region.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for IncarnationRelationships - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into IncarnationRelationships - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct IncarnationRelationshipsRegion { + #[serde(rename = "data")] + #[serde(skip_serializing_if="Option::is_none")] + pub data: Option, + +} + + +impl IncarnationRelationshipsRegion { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> IncarnationRelationshipsRegion { + IncarnationRelationshipsRegion { + data: None, + } + } +} + +/// Converts the IncarnationRelationshipsRegion value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for IncarnationRelationshipsRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a IncarnationRelationshipsRegion value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for IncarnationRelationshipsRegion { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing IncarnationRelationshipsRegion".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing IncarnationRelationshipsRegion".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(IncarnationRelationshipsRegion { + data: intermediate_rep.data.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for IncarnationRelationshipsRegion - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into IncarnationRelationshipsRegion - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct IncarnationRelationshipsRegionData { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + +} + + +impl IncarnationRelationshipsRegionData { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> IncarnationRelationshipsRegionData { + IncarnationRelationshipsRegionData { + id: None, + r#type: None, + } + } +} + +/// Converts the IncarnationRelationshipsRegionData value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for IncarnationRelationshipsRegionData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a IncarnationRelationshipsRegionData value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for IncarnationRelationshipsRegionData { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing IncarnationRelationshipsRegionData".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing IncarnationRelationshipsRegionData".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(IncarnationRelationshipsRegionData { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for IncarnationRelationshipsRegionData - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into IncarnationRelationshipsRegionData - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Region { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if="Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if="Option::is_none")] + pub relationships: Option, + +} + + +impl Region { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> Region { + Region { + id: None, + r#type: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the Region value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for Region { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Region value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Region { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing Region".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing Region".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Region { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for Region - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into Region - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct RegionAttributes { + #[serde(rename = "name")] + #[serde(skip_serializing_if="Option::is_none")] + pub name: Option, + + #[serde(rename = "description")] + #[serde(skip_serializing_if="Option::is_none")] + pub description: Option, + +} + + +impl RegionAttributes { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> RegionAttributes { + RegionAttributes { + name: None, + description: None, + } + } +} + +/// Converts the RegionAttributes value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for RegionAttributes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + + self.name.as_ref().map(|name| { + [ + "name".to_string(), + name.to_string(), + ].join(",") + }), + + + self.description.as_ref().map(|description| { + [ + "description".to_string(), + description.to_string(), + ].join(",") + }), + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a RegionAttributes value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for RegionAttributes { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub name: Vec, + pub description: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing RegionAttributes".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "description" => intermediate_rep.description.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing RegionAttributes".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(RegionAttributes { + name: intermediate_rep.name.into_iter().next(), + description: intermediate_rep.description.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for RegionAttributes - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into RegionAttributes - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct RegionRelationships { + #[serde(rename = "parent")] + #[serde(skip_serializing_if="Option::is_none")] + pub parent: Option, + +} + + +impl RegionRelationships { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> RegionRelationships { + RegionRelationships { + parent: None, + } + } +} + +/// Converts the RegionRelationships value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for RegionRelationships { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping parent in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a RegionRelationships value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for RegionRelationships { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub parent: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing RegionRelationships".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "parent" => intermediate_rep.parent.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing RegionRelationships".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(RegionRelationships { + parent: intermediate_rep.parent.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for RegionRelationships - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into RegionRelationships - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct RegionRelationshipsParent { + #[serde(rename = "data")] + #[serde(deserialize_with = "deserialize_optional_nullable")] + #[serde(default = "default_optional_nullable")] + #[serde(skip_serializing_if="Option::is_none")] + pub data: Option>, + +} + + +impl RegionRelationshipsParent { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> RegionRelationshipsParent { + RegionRelationshipsParent { + data: None, + } + } +} + +/// Converts the RegionRelationshipsParent value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for RegionRelationshipsParent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a RegionRelationshipsParent value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for RegionRelationshipsParent { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing RegionRelationshipsParent".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "data" => return std::result::Result::Err("Parsing a nullable type in this style is not supported in RegionRelationshipsParent".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing RegionRelationshipsParent".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(RegionRelationshipsParent { + data: std::result::Result::Err("Nullable types not supported in RegionRelationshipsParent".to_string())?, + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for RegionRelationshipsParent - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into RegionRelationshipsParent - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + + + + + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct RegionRelationshipsParentData { + #[serde(rename = "id")] + #[serde(skip_serializing_if="Option::is_none")] + pub id: Option, + +/// Note: inline enums are not fully supported by openapi-generator + #[serde(rename = "type")] + #[serde(skip_serializing_if="Option::is_none")] + pub r#type: Option, + +} + + +impl RegionRelationshipsParentData { + #[allow(clippy::new_without_default, clippy::too_many_arguments)] + pub fn new() -> RegionRelationshipsParentData { + RegionRelationshipsParentData { + id: None, + r#type: None, + } + } +} + +/// Converts the RegionRelationshipsParentData value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::fmt::Display for RegionRelationshipsParentData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + + self.r#type.as_ref().map(|r#type| { + [ + "type".to_string(), + r#type.to_string(), + ].join(",") + }), + + ]; + + write!(f, "{}", params.into_iter().flatten().collect::>().join(",")) + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a RegionRelationshipsParentData value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for RegionRelationshipsParentData { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub r#type: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => return std::result::Result::Err("Missing value while parsing RegionRelationshipsParentData".to_string()) + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "type" => intermediate_rep.r#type.push(::from_str(val).map_err(|x| x.to_string())?), + _ => return std::result::Result::Err("Unexpected key while parsing RegionRelationshipsParentData".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(RegionRelationshipsParentData { + id: intermediate_rep.id.into_iter().next(), + r#type: intermediate_rep.r#type.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and HeaderValue + +#[cfg(feature = "server")] +impl std::convert::TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for RegionRelationshipsParentData - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into RegionRelationshipsParentData - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + + + diff --git a/waydowntown/waydowntown-server-axum/src/server/mod.rs b/waydowntown/waydowntown-server-axum/src/server/mod.rs new file mode 100644 index 00000000..df661624 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/src/server/mod.rs @@ -0,0 +1,311 @@ +use std::collections::HashMap; + +use axum::{body::Body, extract::*, response::Response, routing::*}; +use axum_extra::extract::{CookieJar, Multipart}; +use bytes::Bytes; +use http::{header::CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; +use tracing::error; +use validator::{Validate, ValidationErrors}; + +use crate::{header, types::*}; + +#[allow(unused_imports)] +use crate::{apis, models}; + + +/// Setup API Server. +pub fn new(api_impl: I) -> Router +where + I: AsRef + Clone + Send + Sync + 'static, + A: apis::default::Default + 'static, +{ + // build our application with a route + Router::new() + .route("/answers", + post(answers_post::) + ) + .route("/games", + post(games_post::) + ) + .route("/games/:id", + get(games_id_get::) + ) + .with_state(api_impl) +} + + #[derive(validator::Validate)] + #[allow(dead_code)] + struct AnswersPostBodyValidator<'a> { + #[validate(nested)] + body: &'a models::AnswerInput, + } + + +#[tracing::instrument(skip_all)] +fn answers_post_validation( + body: models::AnswerInput, +) -> std::result::Result<( + models::AnswerInput, +), ValidationErrors> +{ + let b = AnswersPostBodyValidator { body: &body }; + b.validate()?; + +Ok(( + body, +)) +} +/// AnswersPost - POST /answers +#[tracing::instrument(skip_all)] +async fn answers_post( + method: Method, + host: Host, + cookies: CookieJar, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: apis::default::Default, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + answers_post_validation( + body, + ) + ).await.unwrap(); + + let Ok(( + body, + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().answers_post( + method, + host, + cookies, + body, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + apis::default::AnswersPostResponse::Status201_SuccessfullyCreatedAnswer + (body) + => { + let mut response = response.status(201); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?); + } + + let body_content = tokio::task::spawn_blocking(move || + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })).await.unwrap()?; + response.body(Body::from(body_content)) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + +#[tracing::instrument(skip_all)] +fn games_id_get_validation( + path_params: models::GamesIdGetPathParams, +) -> std::result::Result<( + models::GamesIdGetPathParams, +), ValidationErrors> +{ + path_params.validate()?; + +Ok(( + path_params, +)) +} +/// GamesIdGet - GET /games/{id} +#[tracing::instrument(skip_all)] +async fn games_id_get( + method: Method, + host: Host, + cookies: CookieJar, + Path(path_params): Path, + State(api_impl): State, +) -> Result +where + I: AsRef + Send + Sync, + A: apis::default::Default, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + games_id_get_validation( + path_params, + ) + ).await.unwrap(); + + let Ok(( + path_params, + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().games_id_get( + method, + host, + cookies, + path_params, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + apis::default::GamesIdGetResponse::Status200_SuccessfullyFetchedGame + (body) + => { + let mut response = response.status(200); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?); + } + + let body_content = tokio::task::spawn_blocking(move || + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })).await.unwrap()?; + response.body(Body::from(body_content)) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + + #[derive(validator::Validate)] + #[allow(dead_code)] + struct GamesPostBodyValidator<'a> { + #[validate(nested)] + body: &'a models::GameInput, + } + + +#[tracing::instrument(skip_all)] +fn games_post_validation( + query_params: models::GamesPostQueryParams, + body: models::GameInput, +) -> std::result::Result<( + models::GamesPostQueryParams, + models::GameInput, +), ValidationErrors> +{ + query_params.validate()?; + let b = GamesPostBodyValidator { body: &body }; + b.validate()?; + +Ok(( + query_params, + body, +)) +} +/// GamesPost - POST /games +#[tracing::instrument(skip_all)] +async fn games_post( + method: Method, + host: Host, + cookies: CookieJar, + Query(query_params): Query, + State(api_impl): State, + Json(body): Json, +) -> Result +where + I: AsRef + Send + Sync, + A: apis::default::Default, +{ + + #[allow(clippy::redundant_closure)] + let validation = tokio::task::spawn_blocking(move || + games_post_validation( + query_params, + body, + ) + ).await.unwrap(); + + let Ok(( + query_params, + body, + )) = validation else { + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(validation.unwrap_err().to_string())) + .map_err(|_| StatusCode::BAD_REQUEST); + }; + + let result = api_impl.as_ref().games_post( + method, + host, + cookies, + query_params, + body, + ).await; + + let mut response = Response::builder(); + + let resp = match result { + Ok(rsp) => match rsp { + apis::default::GamesPostResponse::Status201_SuccessfullyCreatedGame + (body) + => { + let mut response = response.status(201); + { + let mut response_headers = response.headers_mut().unwrap(); + response_headers.insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json").map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR })?); + } + + let body_content = tokio::task::spawn_blocking(move || + serde_json::to_vec(&body).map_err(|e| { + error!(error = ?e); + StatusCode::INTERNAL_SERVER_ERROR + })).await.unwrap()?; + response.body(Body::from(body_content)) + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + response.status(500).body(Body::empty()) + }, + }; + + resp.map_err(|e| { error!(error = ?e); StatusCode::INTERNAL_SERVER_ERROR }) +} + diff --git a/waydowntown/waydowntown-server-axum/src/types.rs b/waydowntown/waydowntown-server-axum/src/types.rs new file mode 100644 index 00000000..66b4e5f0 --- /dev/null +++ b/waydowntown/waydowntown-server-axum/src/types.rs @@ -0,0 +1,665 @@ +use std::{mem, str::FromStr}; + +use base64::{engine::general_purpose, Engine}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[allow(dead_code)] +pub struct Object(serde_json::Value); + +impl validator::Validate for Object { + fn validate(&self) -> Result<(), validator::ValidationErrors> { + Ok(()) + } +} + +impl FromStr for Object { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(Self(serde_json::Value::String(s.to_owned()))) + } +} + +/// Serde helper function to create a default `Option>` while +/// deserializing +pub fn default_optional_nullable() -> Option> { + None +} + +/// Serde helper function to deserialize into an `Option>` +pub fn deserialize_optional_nullable<'de, D, T>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + Option::::deserialize(deserializer).map(|val| match val { + Some(inner) => Some(Nullable::Present(inner)), + None => Some(Nullable::Null), + }) +} + +/// The Nullable type. Represents a value which may be specified as null on an API. +/// Note that this is distinct from a value that is optional and not present! +/// +/// Nullable implements many of the same methods as the Option type (map, unwrap, etc). +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum Nullable { + /// Null value + Null, + /// Value is present + Present(T), +} + +impl Nullable { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the Nullable is a `Present` value. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_present(), true); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_present(), false); + /// ``` + #[inline] + pub fn is_present(&self) -> bool { + match *self { + Nullable::Present(_) => true, + Nullable::Null => false, + } + } + + /// Returns `true` if the Nullable is a `Null` value. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x: Nullable = Nullable::Present(2); + /// assert_eq!(x.is_null(), false); + /// + /// let x: Nullable = Nullable::Null; + /// assert_eq!(x.is_null(), true); + /// ``` + #[inline] + pub fn is_null(&self) -> bool { + !self.is_present() + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `Nullable` to `Nullable<&T>`. + /// + /// # Examples + /// + /// Convert an `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take a `Nullable` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.Nullable.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let num_as_str: Nullable = Nullable::Present("10".to_string()); + /// // First, cast `Nullable` to `Nullable<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `num_as_str` on the stack. + /// let num_as_int: Nullable = num_as_str.as_ref().map(|n| n.len()); + /// println!("still can print num_as_str: {:?}", num_as_str); + /// ``` + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match *self { + Nullable::Present(ref x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + /// Converts from `Nullable` to `Nullable<&mut T>`. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// match x.as_mut() { + /// Nullable::Present(v) => *v = 42, + /// Nullable::Null => {}, + /// } + /// assert_eq!(x, Nullable::Present(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> Nullable<&mut T> { + match *self { + Nullable::Present(ref mut x) => Nullable::Present(x), + Nullable::Null => Nullable::Null, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps a Nullable, yielding the content of a `Nullable::Present`. + /// + /// # Panics + /// + /// Panics if the value is a [`Nullable::Null`] with a custom panic message provided by + /// `msg`. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = Nullable::Present("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```{.should_panic} + /// # use waydowntown_server::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `Nullable` if it is `Nullable::Present(v)`. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the `Nullable::Null` + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`Nullable::Null`]. + /// + /// [`Nullable::Null`]: #variant.Null + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = Nullable::Present("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```{.should_panic} + /// # use waydowntown_server::types::Nullable; + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + Nullable::Present(val) => val, + Nullable::Null => panic!("called `Nullable::unwrap()` on a `Nullable::Null` value"), + } + } + + /// Returns the contained value or a default. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// assert_eq!(Nullable::Present("car").unwrap_or("bike"), "car"); + /// assert_eq!(Nullable::Null.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let k = 10; + /// assert_eq!(Nullable::Present(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(Nullable::Null.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// + /// # Examples + /// + /// Convert a `Nullable<`[`String`]`>` into a `Nullable<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let maybe_some_string = Nullable::Present(String::from("Hello, World!")); + /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, Nullable::Present(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => Nullable::Present(f(x)), + Nullable::Null => Nullable::Null, + } + } + + /// Applies a function to the contained value (if any), + /// or returns a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a `default` (if not). + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let k = 21; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + Nullable::Present(t) => f(t), + Nullable::Null => default(), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err)`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err), + } + } + + /// Transforms the `Nullable` into a [`Result`], mapping `Nullable::Present(v)` to + /// [`Ok(v)`] and `Nullable::Null` to [`Err(err())`][Err]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [Err]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = Nullable::Present("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + Nullable::Present(v) => Ok(v), + Nullable::Null => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Null); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present("foo"); + /// assert_eq!(x.and(y), Nullable::Present("foo")); + /// + /// let x: Nullable = Nullable::Null; + /// let y: Nullable<&str> = Nullable::Null; + /// assert_eq!(x.and(y), Nullable::Null); + /// ``` + #[inline] + pub fn and(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => optb, + Nullable::Null => Nullable::Null, + } + } + + /// Returns `Nullable::Null` if the Nullable is `Nullable::Null`, otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// Some languages call this operation flatmap. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// fn sq(x: u32) -> Nullable { Nullable::Present(x * x) } + /// fn nope(_: u32) -> Nullable { Nullable::Null } + /// + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(sq), Nullable::Present(16)); + /// assert_eq!(Nullable::Present(2).and_then(sq).and_then(nope), Nullable::Null); + /// assert_eq!(Nullable::Present(2).and_then(nope).and_then(sq), Nullable::Null); + /// assert_eq!(Nullable::Null.and_then(sq).and_then(sq), Nullable::Null); + /// ``` + #[inline] + pub fn and_then Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(x) => f(x), + Nullable::Null => Nullable::Null, + } + } + + /// Returns the Nullable if it contains a value, otherwise returns `optb`. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x = Nullable::Null; + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(100)); + /// + /// let x = Nullable::Present(2); + /// let y = Nullable::Present(100); + /// assert_eq!(x.or(y), Nullable::Present(2)); + /// + /// let x: Nullable = Nullable::Null; + /// let y = Nullable::Null; + /// assert_eq!(x.or(y), Nullable::Null); + /// ``` + #[inline] + pub fn or(self, optb: Nullable) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => optb, + } + } + + /// Returns the Nullable if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// fn nobody() -> Nullable<&'static str> { Nullable::Null } + /// fn vikings() -> Nullable<&'static str> { Nullable::Present("vikings") } + /// + /// assert_eq!(Nullable::Present("barbarians").or_else(vikings), + /// Nullable::Present("barbarians")); + /// assert_eq!(Nullable::Null.or_else(vikings), Nullable::Present("vikings")); + /// assert_eq!(Nullable::Null.or_else(nobody), Nullable::Null); + /// ``` + #[inline] + pub fn or_else Nullable>(self, f: F) -> Nullable { + match self { + Nullable::Present(_) => self, + Nullable::Null => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Takes the value out of the Nullable, leaving a `Nullable::Null` in its place. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let mut x = Nullable::Present(2); + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// + /// let mut x: Nullable = Nullable::Null; + /// x.take(); + /// assert_eq!(x, Nullable::Null); + /// ``` + #[inline] + pub fn take(&mut self) -> Nullable { + mem::replace(self, Nullable::Null) + } +} + +impl<'a, T: Clone> Nullable<&'a T> { + /// Maps an `Nullable<&T>` to an `Nullable` by cloning the contents of the + /// Nullable. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = 12; + /// let opt_x = Nullable::Present(&x); + /// assert_eq!(opt_x, Nullable::Present(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, Nullable::Present(12)); + /// ``` + pub fn cloned(self) -> Nullable { + self.map(Clone::clone) + } +} + +impl Nullable { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if `Nullable::Present`, returns the contained + /// value, otherwise if `Nullable::Null`, returns the default value for that + /// type. + /// + /// # Examples + /// + /// ``` + /// # use waydowntown_server::types::Nullable; + /// + /// let x = Nullable::Present(42); + /// assert_eq!(42, x.unwrap_or_default()); + /// + /// let y: Nullable = Nullable::Null; + /// assert_eq!(0, y.unwrap_or_default()); + /// ``` + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + Nullable::Present(x) => x, + Nullable::Null => Default::default(), + } + } +} + +impl Default for Nullable { + /// Returns None. + #[inline] + fn default() -> Nullable { + Nullable::Null + } +} + +impl From for Nullable { + fn from(val: T) -> Nullable { + Nullable::Present(val) + } +} + +impl Serialize for Nullable +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Nullable::Present(ref inner) => serializer.serialize_some(&inner), + Nullable::Null => serializer.serialize_none(), + } + } +} + +impl<'de, T> Deserialize<'de> for Nullable +where + T: serde::de::DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + // In order to deserialize a required, but nullable, value, we first have to check whether + // the value is present at all. To do this, we deserialize to a serde_json::Value, which + // fails if the value is missing, or gives serde_json::Value::Null if the value is present. + // If that succeeds as null, we can easily return a Null. + // If that succeeds as some value, we deserialize that value and return a Present. + // If that errors, we return the error. + let presence: Result<::serde_json::Value, _> = + serde::Deserialize::deserialize(deserializer); + match presence { + Ok(serde_json::Value::Null) => Ok(Nullable::Null), + Ok(some_value) => serde_json::from_value(some_value) + .map(Nullable::Present) + .map_err(serde::de::Error::custom), + Err(x) => Err(x), + } + } +} + +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// Base64-encoded byte array +pub struct ByteArray(pub Vec); + +impl Serialize for ByteArray { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&general_purpose::STANDARD.encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for ByteArray { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match general_purpose::STANDARD.decode(s) { + Ok(bin) => Ok(ByteArray(bin)), + _ => Err(serde::de::Error::custom("invalid base64")), + } + } +} diff --git a/waydowntown/waydowntown_rails_generated/.openapi-generator-ignore b/waydowntown/waydowntown_rails_generated/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/waydowntown/waydowntown_rails_generated/.openapi-generator/FILES b/waydowntown/waydowntown_rails_generated/.openapi-generator/FILES new file mode 100644 index 00000000..d3e4c7c9 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/.openapi-generator/FILES @@ -0,0 +1,85 @@ +.openapi-generator-ignore +Dockerfile +Gemfile +README.md +Rakefile +app/channels/application_cable/channel.rb +app/channels/application_cable/connection.rb +app/controllers/application_controller.rb +app/controllers/default_controller.rb +app/jobs/application_job.rb +app/mailers/application_mailer.rb +app/models/_games__id__get_200_response.rb +app/models/_games__id__get_200_response_included_inner.rb +app/models/_games_post_201_response.rb +app/models/_games_post_201_response_included_inner.rb +app/models/answer.rb +app/models/answer_attributes.rb +app/models/answer_input.rb +app/models/answer_relationships.rb +app/models/application_record.rb +app/models/game.rb +app/models/game_attributes.rb +app/models/game_input.rb +app/models/game_relationships.rb +app/models/game_relationships_incarnation.rb +app/models/game_relationships_incarnation_data.rb +app/models/game_relationships_winner_answer.rb +app/models/game_relationships_winner_answer_data.rb +app/models/incarnation.rb +app/models/incarnation_attributes.rb +app/models/incarnation_relationships.rb +app/models/region.rb +app/models/region_attributes.rb +app/models/region_relationships.rb +app/views/layouts/mailer.html.erb +app/views/layouts/mailer.text.erb +bin/bundle +bin/rails +bin/rake +bin/setup +bin/update +config.ru +config/application.rb +config/boot.rb +config/cable.yml +config/database.yml +config/environment.rb +config/environments/development.rb +config/environments/production.rb +config/initializers/active_record_belongs_to_required_by_default.rb +config/initializers/application_controller_renderer.rb +config/initializers/backtrace_silencers.rb +config/initializers/callback_terminator.rb +config/initializers/cors.rb +config/initializers/filter_parameter_logging.rb +config/initializers/inflections.rb +config/initializers/mime_types.rb +config/initializers/ssl_options.rb +config/initializers/to_time_preserves_timezone.rb +config/locales/en.yml +config/puma.rb +config/routes.rb +config/secrets.yml +config/spring.rb +db/migrate/.keep +db/migrate/0_init_tables.rb +db/schema.rb +db/seeds.rb +docker-entrypoint.sh +lib/tasks/.keep +log/.keep +public/404.html +public/422.html +public/500.html +public/apple-touch-icon-precomposed.png +public/apple-touch-icon.png +public/favicon.ico +public/robots.txt +public/robots.txt +test/test_helper.rb +tmp/cache/.keep +tmp/pids/.keep +tmp/restart.txt +tmp/sockets/.keep +vendor/.keep diff --git a/waydowntown/waydowntown_rails_generated/.openapi-generator/VERSION b/waydowntown/waydowntown_rails_generated/.openapi-generator/VERSION new file mode 100644 index 00000000..1985849f --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.7.0 diff --git a/waydowntown/waydowntown_rails_generated/.ruby-version b/waydowntown/waydowntown_rails_generated/.ruby-version new file mode 100644 index 00000000..6a81b4c8 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/.ruby-version @@ -0,0 +1 @@ +2.7.8 diff --git a/waydowntown/waydowntown_rails_generated/Dockerfile b/waydowntown/waydowntown_rails_generated/Dockerfile new file mode 100644 index 00000000..d53cf3aa --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/Dockerfile @@ -0,0 +1,29 @@ +FROM ruby:2.5-alpine + +ENV BUILD_PACKAGES="curl-dev ruby-dev build-base" \ + DEV_PACKAGES="zlib-dev libxml2-dev libxslt-dev tzdata yaml-dev sqlite-dev" \ + RUBY_PACKAGES="ruby-json yaml nodejs" + +RUN apk update && \ + apk upgrade && \ + apk add --no-cache --update\ + $BUILD_PACKAGES \ + $DEV_PACKAGES \ + $RUBY_PACKAGES && \ + mkdir -p /usr/src/myapp + +WORKDIR /usr/src/myapp + +COPY Gemfile ./ +RUN gem install bundler +RUN bundle config build.nokogiri --use-system-libraries && \ + bundle install --jobs=4 --retry=10 --clean + +COPY . ./ +RUN chmod +x bin/rails + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh +ENTRYPOINT ["docker-entrypoint.sh"] + +CMD ["bin/rails", "s", "-b", "0.0.0.0"] diff --git a/waydowntown/waydowntown_rails_generated/Gemfile b/waydowntown/waydowntown_rails_generated/Gemfile new file mode 100644 index 00000000..851d4e91 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/Gemfile @@ -0,0 +1,36 @@ +source 'https://rubygems.org' + +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 5.0.0' + +gem 'pg', '~> 1.5' + +# Use Puma as the app server +gem 'puma', '~> 3.0' +# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder +gem 'jbuilder', '~> 2.0' +# Use Redis adapter to run Action Cable in production +# gem 'redis', '~> 3.0' +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible +# gem 'rack-cors' + +group :development, :test do + # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'byebug', platform: :mri +end + +group :development do + gem 'listen', '~> 3.0.5' + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' + gem 'spring-watcher-listen', '~> 2.0.0' +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] diff --git a/waydowntown/waydowntown_rails_generated/Gemfile.lock b/waydowntown/waydowntown_rails_generated/Gemfile.lock new file mode 100644 index 00000000..b8e155e3 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/Gemfile.lock @@ -0,0 +1,154 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (5.0.7.2) + actionpack (= 5.0.7.2) + nio4r (>= 1.2, < 3.0) + websocket-driver (~> 0.6.1) + actionmailer (5.0.7.2) + actionpack (= 5.0.7.2) + actionview (= 5.0.7.2) + activejob (= 5.0.7.2) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.0.7.2) + actionview (= 5.0.7.2) + activesupport (= 5.0.7.2) + rack (~> 2.0) + rack-test (~> 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.0.7.2) + activesupport (= 5.0.7.2) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.3) + activejob (5.0.7.2) + activesupport (= 5.0.7.2) + globalid (>= 0.3.6) + activemodel (5.0.7.2) + activesupport (= 5.0.7.2) + activerecord (5.0.7.2) + activemodel (= 5.0.7.2) + activesupport (= 5.0.7.2) + arel (~> 7.0) + activesupport (5.0.7.2) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + arel (7.1.4) + builder (3.3.0) + byebug (11.1.3) + concurrent-ruby (1.3.4) + crass (1.0.6) + date (3.3.4) + erubis (2.7.0) + ffi (1.17.0) + globalid (1.1.0) + activesupport (>= 5.0) + i18n (1.14.5) + concurrent-ruby (~> 1.0) + jbuilder (2.12.0) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + loofah (2.22.0) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + method_source (1.1.0) + mini_mime (1.1.5) + mini_portile2 (2.8.7) + minitest (5.25.1) + net-imap (0.4.14) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.0) + net-protocol + nio4r (2.7.3) + nokogiri (1.15.6) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) + pg (1.5.7) + puma (3.12.6) + racc (1.8.1) + rack (2.2.9) + rack-test (0.6.3) + rack (>= 1.0) + rails (5.0.7.2) + actioncable (= 5.0.7.2) + actionmailer (= 5.0.7.2) + actionpack (= 5.0.7.2) + actionview (= 5.0.7.2) + activejob (= 5.0.7.2) + activemodel (= 5.0.7.2) + activerecord (= 5.0.7.2) + activesupport (= 5.0.7.2) + bundler (>= 1.3.0) + railties (= 5.0.7.2) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (5.0.7.2) + actionpack (= 5.0.7.2) + activesupport (= 5.0.7.2) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + spring (2.1.1) + spring-watcher-listen (2.0.1) + listen (>= 2.7, < 4.0) + spring (>= 1.2, < 3.0) + sprockets (4.2.1) + concurrent-ruby (~> 1.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.2.2) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + thor (1.3.1) + thread_safe (0.3.6) + timeout (0.4.1) + tzinfo (1.2.11) + thread_safe (~> 0.1) + websocket-driver (0.6.5) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + +PLATFORMS + ruby + +DEPENDENCIES + byebug + jbuilder (~> 2.0) + listen (~> 3.0.5) + pg (~> 1.5) + puma (~> 3.0) + rails (~> 5.0.0) + spring + spring-watcher-listen (~> 2.0.0) + tzinfo-data + +BUNDLED WITH + 2.1.4 diff --git a/waydowntown/waydowntown_rails_generated/README.md b/waydowntown/waydowntown_rails_generated/README.md new file mode 100644 index 00000000..cf74536c --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/README.md @@ -0,0 +1,25 @@ +# OpenAPI for Rails 5 + +This is a project to provide OpenAPI support inside the [Ruby on Rails](http://rubyonrails.org/) framework. + +## Prerequisites +You need to install ruby >= 2.2.2 and run: + +``` +bundle install +``` + +## Getting started + +This sample was generated with [openapi-generator](https://github.com/openapitools/openapi-generator) project. + +``` +bin/rake db:create db:migrate +bin/rails s +``` + +To list all your routes, use: + +``` +bin/rake routes +``` diff --git a/waydowntown/waydowntown_rails_generated/Rakefile b/waydowntown/waydowntown_rails_generated/Rakefile new file mode 100644 index 00000000..e85f9139 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/waydowntown/waydowntown_rails_generated/app/channels/application_cable/channel.rb b/waydowntown/waydowntown_rails_generated/app/channels/application_cable/channel.rb new file mode 100644 index 00000000..d56fa30f --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/channels/application_cable/channel.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/waydowntown/waydowntown_rails_generated/app/channels/application_cable/connection.rb b/waydowntown/waydowntown_rails_generated/app/channels/application_cable/connection.rb new file mode 100644 index 00000000..b4f41389 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/channels/application_cable/connection.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/waydowntown/waydowntown_rails_generated/app/controllers/application_controller.rb b/waydowntown/waydowntown_rails_generated/app/controllers/application_controller.rb new file mode 100644 index 00000000..4ac8823b --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/controllers/application_controller.rb @@ -0,0 +1,2 @@ +class ApplicationController < ActionController::API +end diff --git a/waydowntown/waydowntown_rails_generated/app/controllers/default_controller.rb b/waydowntown/waydowntown_rails_generated/app/controllers/default_controller.rb new file mode 100644 index 00000000..fd02f785 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/controllers/default_controller.rb @@ -0,0 +1,111 @@ +# Games API +# +# No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) +# +# The version of the OpenAPI document: 1.0.0 +# Generated by: https://github.com/openapitools/openapi-generator.git +# +class DefaultController < ApplicationController + def answers_post + # Your code here + + render json: { 'message' => 'yes, it worked' } + end + + def games_id_get + game = Game.find_by(id: params[:id]) + + if game + render json: game_json(game) + else + render json: { error: 'Game not found' }, status: :not_found + end + end + + def games_post + # Your code here + + render json: { 'message' => 'yes, it worked' } + end + + private + + def game_json(game) + { + data: { + id: game.id, + type: 'games', + relationships: { + incarnation: { + links: { + related: "#{Rails.application.routes.default_url_options[:host]}/api/v1/incarnations/#{game.incarnation.id}" + }, + data: { + type: 'incarnations', + id: game.incarnation.id + } + } + } + }, + included: [incarnation_json(game.incarnation)] + region_tree_json(game.incarnation.region), + meta: {} + } + end + + def incarnation_json(incarnation) + { + id: incarnation.id, + type: 'incarnations', + attributes: { + concept: incarnation.concept, + mask: incarnation.mask + }, + relationships: { + region: { + links: { + related: "#{Rails.application.routes.default_url_options[:host]}/api/v1/regions/#{incarnation.region.id}" + }, + data: { + type: 'regions', + id: incarnation.region.id + } + } + } + } + end + + def region_json(region) + { + id: region.id, + type: 'regions', + attributes: { + name: region.name, + description: region.description + }, + relationships: { + parent: if region.parent + { + links: { + related: "#{Rails.application.routes.default_url_options[:host]}/api/v1/regions/#{region.parent.id}" + }, + data: { + type: 'regions', + id: region.parent.id + } + } + else + { + links: { related: nil }, + data: nil + } + end + } + } + end + + def region_tree_json(region) + result = [region_json(region)] + result += region_tree_json(region.parent) if region.parent + result + end +end diff --git a/waydowntown/waydowntown_rails_generated/app/jobs/application_job.rb b/waydowntown/waydowntown_rails_generated/app/jobs/application_job.rb new file mode 100644 index 00000000..a009ace5 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/waydowntown/waydowntown_rails_generated/app/mailers/application_mailer.rb b/waydowntown/waydowntown_rails_generated/app/mailers/application_mailer.rb new file mode 100644 index 00000000..286b2239 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/_games__id__get_200_response.rb b/waydowntown/waydowntown_rails_generated/app/models/_games__id__get_200_response.rb new file mode 100644 index 00000000..e53ad49d --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/_games__id__get_200_response.rb @@ -0,0 +1,15 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GamesIdGet200Response < ApplicationRecord + + serialize :included, Array +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/_games__id__get_200_response_included_inner.rb b/waydowntown/waydowntown_rails_generated/app/models/_games__id__get_200_response_included_inner.rb new file mode 100644 index 00000000..333eadf2 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/_games__id__get_200_response_included_inner.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GamesIdGet200ResponseIncludedInner < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/_games_post_201_response.rb b/waydowntown/waydowntown_rails_generated/app/models/_games_post_201_response.rb new file mode 100644 index 00000000..9b76bb3b --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/_games_post_201_response.rb @@ -0,0 +1,15 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GamesPost201Response < ApplicationRecord + + serialize :included, Array +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/_games_post_201_response_included_inner.rb b/waydowntown/waydowntown_rails_generated/app/models/_games_post_201_response_included_inner.rb new file mode 100644 index 00000000..0df6d401 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/_games_post_201_response_included_inner.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GamesPost201ResponseIncludedInner < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/answer.rb b/waydowntown/waydowntown_rails_generated/app/models/answer.rb new file mode 100644 index 00000000..d1616df8 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/answer.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class Answer < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/answer_attributes.rb b/waydowntown/waydowntown_rails_generated/app/models/answer_attributes.rb new file mode 100644 index 00000000..1fe96ff2 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/answer_attributes.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class AnswerAttributes < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/answer_input.rb b/waydowntown/waydowntown_rails_generated/app/models/answer_input.rb new file mode 100644 index 00000000..807dcffc --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/answer_input.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class AnswerInput < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/answer_relationships.rb b/waydowntown/waydowntown_rails_generated/app/models/answer_relationships.rb new file mode 100644 index 00000000..d14abe09 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/answer_relationships.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class AnswerRelationships < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/application_record.rb b/waydowntown/waydowntown_rails_generated/app/models/application_record.rb new file mode 100644 index 00000000..10a4cba8 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/game.rb b/waydowntown/waydowntown_rails_generated/app/models/game.rb new file mode 100644 index 00000000..c37b3f0f --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/game.rb @@ -0,0 +1,12 @@ +# Games API +# +# No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) +# +# The version of the OpenAPI document: 1.0.0 +# Generated by: https://github.com/openapitools/openapi-generator.git +# + +class Game < ApplicationRecord + belongs_to :incarnation + has_many :answers, dependent: :destroy +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/game_attributes.rb b/waydowntown/waydowntown_rails_generated/app/models/game_attributes.rb new file mode 100644 index 00000000..0bb733bf --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/game_attributes.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GameAttributes < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/game_input.rb b/waydowntown/waydowntown_rails_generated/app/models/game_input.rb new file mode 100644 index 00000000..8c448061 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/game_input.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GameInput < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/game_relationships.rb b/waydowntown/waydowntown_rails_generated/app/models/game_relationships.rb new file mode 100644 index 00000000..b9d3cb27 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/game_relationships.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GameRelationships < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/game_relationships_incarnation.rb b/waydowntown/waydowntown_rails_generated/app/models/game_relationships_incarnation.rb new file mode 100644 index 00000000..f55dfb89 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/game_relationships_incarnation.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GameRelationshipsIncarnation < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/game_relationships_incarnation_data.rb b/waydowntown/waydowntown_rails_generated/app/models/game_relationships_incarnation_data.rb new file mode 100644 index 00000000..720f6df5 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/game_relationships_incarnation_data.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GameRelationshipsIncarnationData < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/game_relationships_winner_answer.rb b/waydowntown/waydowntown_rails_generated/app/models/game_relationships_winner_answer.rb new file mode 100644 index 00000000..173e5016 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/game_relationships_winner_answer.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GameRelationshipsWinnerAnswer < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/game_relationships_winner_answer_data.rb b/waydowntown/waydowntown_rails_generated/app/models/game_relationships_winner_answer_data.rb new file mode 100644 index 00000000..8eac95f3 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/game_relationships_winner_answer_data.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class GameRelationshipsWinnerAnswerData < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/incarnation.rb b/waydowntown/waydowntown_rails_generated/app/models/incarnation.rb new file mode 100644 index 00000000..1b7a160e --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/incarnation.rb @@ -0,0 +1,12 @@ +# Games API +# +# No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) +# +# The version of the OpenAPI document: 1.0.0 +# Generated by: https://github.com/openapitools/openapi-generator.git +# + +class Incarnation < ApplicationRecord + has_many :games, dependent: :destroy + belongs_to :region +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/incarnation_attributes.rb b/waydowntown/waydowntown_rails_generated/app/models/incarnation_attributes.rb new file mode 100644 index 00000000..2bacd8ad --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/incarnation_attributes.rb @@ -0,0 +1,15 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class IncarnationAttributes < ApplicationRecord + + serialize :answers, Array +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/incarnation_relationships.rb b/waydowntown/waydowntown_rails_generated/app/models/incarnation_relationships.rb new file mode 100644 index 00000000..d775426e --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/incarnation_relationships.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class IncarnationRelationships < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/region.rb b/waydowntown/waydowntown_rails_generated/app/models/region.rb new file mode 100644 index 00000000..e002fa87 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/region.rb @@ -0,0 +1,11 @@ +# Games API +# +# No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) +# +# The version of the OpenAPI document: 1.0.0 +# Generated by: https://github.com/openapitools/openapi-generator.git +# + +class Region < ApplicationRecord + belongs_to :parent, class_name: 'Region', optional: true +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/region_attributes.rb b/waydowntown/waydowntown_rails_generated/app/models/region_attributes.rb new file mode 100644 index 00000000..9dca95a3 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/region_attributes.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class RegionAttributes < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/models/region_relationships.rb b/waydowntown/waydowntown_rails_generated/app/models/region_relationships.rb new file mode 100644 index 00000000..0b686a99 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/models/region_relationships.rb @@ -0,0 +1,14 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end + + +class RegionRelationships < ApplicationRecord + +end diff --git a/waydowntown/waydowntown_rails_generated/app/views/layouts/mailer.html.erb b/waydowntown/waydowntown_rails_generated/app/views/layouts/mailer.html.erb new file mode 100644 index 00000000..cbd34d2e --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/waydowntown/waydowntown_rails_generated/app/views/layouts/mailer.text.erb b/waydowntown/waydowntown_rails_generated/app/views/layouts/mailer.text.erb new file mode 100644 index 00000000..37f0bddb --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/waydowntown/waydowntown_rails_generated/bin/bundle b/waydowntown/waydowntown_rails_generated/bin/bundle new file mode 100755 index 00000000..66e9889e --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/bin/bundle @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +load Gem.bin_path('bundler', 'bundle') diff --git a/waydowntown/waydowntown_rails_generated/bin/rails b/waydowntown/waydowntown_rails_generated/bin/rails new file mode 100755 index 00000000..07396602 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/waydowntown/waydowntown_rails_generated/bin/rake b/waydowntown/waydowntown_rails_generated/bin/rake new file mode 100755 index 00000000..17240489 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/waydowntown/waydowntown_rails_generated/bin/setup b/waydowntown/waydowntown_rails_generated/bin/setup new file mode 100755 index 00000000..e620b4da --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/bin/setup @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # puts "\n== Copying sample files ==" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:setup' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/waydowntown/waydowntown_rails_generated/bin/update b/waydowntown/waydowntown_rails_generated/bin/update new file mode 100755 index 00000000..a8e4462f --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/bin/update @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/waydowntown/waydowntown_rails_generated/config.ru b/waydowntown/waydowntown_rails_generated/config.ru new file mode 100644 index 00000000..f7ba0b52 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config.ru @@ -0,0 +1,5 @@ +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/waydowntown/waydowntown_rails_generated/config/application.rb b/waydowntown/waydowntown_rails_generated/config/application.rb new file mode 100644 index 00000000..70bea9ec --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/application.rb @@ -0,0 +1,30 @@ +require_relative 'boot' + +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +require "active_record/railtie" +require "action_controller/railtie" +require "action_mailer/railtie" +require "action_view/railtie" +require "action_cable/engine" +# require "sprockets/railtie" +require "rails/test_unit/railtie" + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module ApiDemo + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + + # Only loads a smaller set of middleware suitable for API only apps. + # Middleware like session, flash, cookies can be added back manually. + # Skip views, helpers and assets when generating a new resource. + config.api_only = true + end +end diff --git a/waydowntown/waydowntown_rails_generated/config/boot.rb b/waydowntown/waydowntown_rails_generated/config/boot.rb new file mode 100644 index 00000000..30f5120d --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/boot.rb @@ -0,0 +1,3 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/waydowntown/waydowntown_rails_generated/config/cable.yml b/waydowntown/waydowntown_rails_generated/config/cable.yml new file mode 100644 index 00000000..aa4e8327 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/cable.yml @@ -0,0 +1,10 @@ +# Action Cable uses Redis by default to administer connections, channels, and sending/receiving messages over the WebSocket. +production: + adapter: redis + url: redis://localhost:6379/1 + +development: + adapter: async + +test: + adapter: async diff --git a/waydowntown/waydowntown_rails_generated/config/database.yml b/waydowntown/waydowntown_rails_generated/config/database.yml new file mode 100644 index 00000000..4e7d443b --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/database.yml @@ -0,0 +1,20 @@ +default: &default + adapter: postgresql + schema_search_path: "waydowntown,public" + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + +development: + <<: *default + database: registrations_dev + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: registrations_test + +production: + <<: *default + database: registrations_prod diff --git a/waydowntown/waydowntown_rails_generated/config/environment.rb b/waydowntown/waydowntown_rails_generated/config/environment.rb new file mode 100644 index 00000000..426333bb --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/waydowntown/waydowntown_rails_generated/config/environments/development.rb b/waydowntown/waydowntown_rails_generated/config/environments/development.rb new file mode 100644 index 00000000..082a013a --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/environments/development.rb @@ -0,0 +1,47 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + if Rails.root.join('tmp/caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=172800' + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + config.action_mailer.perform_caching = false + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + + # Raises error for missing translations + # config.action_view.raise_on_missing_translations = true + + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + config.file_watcher = ActiveSupport::EventedFileUpdateChecker +end diff --git a/waydowntown/waydowntown_rails_generated/config/environments/production.rb b/waydowntown/waydowntown_rails_generated/config/environments/production.rb new file mode 100644 index 00000000..6cb0a8fe --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/environments/production.rb @@ -0,0 +1,80 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Action Cable endpoint configuration + # config.action_cable.url = 'wss://example.com/cable' + # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] + + # Don't mount Action Cable in the main server process. + # config.action_cable.mount_path = nil + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment) + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "api_demo_#{Rails.env}" + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/active_record_belongs_to_required_by_default.rb b/waydowntown/waydowntown_rails_generated/config/initializers/active_record_belongs_to_required_by_default.rb new file mode 100644 index 00000000..f613b40f --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/active_record_belongs_to_required_by_default.rb @@ -0,0 +1,6 @@ +# Be sure to restart your server when you modify this file. + +# Require `belongs_to` associations by default. This is a new Rails 5.0 +# default, so it is introduced as a configuration option to ensure that apps +# made on earlier versions of Rails are not affected when upgrading. +Rails.application.config.active_record.belongs_to_required_by_default = true diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/application_controller_renderer.rb b/waydowntown/waydowntown_rails_generated/config/initializers/application_controller_renderer.rb new file mode 100644 index 00000000..51639b67 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/application_controller_renderer.rb @@ -0,0 +1,6 @@ +# Be sure to restart your server when you modify this file. + +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/backtrace_silencers.rb b/waydowntown/waydowntown_rails_generated/config/initializers/backtrace_silencers.rb new file mode 100644 index 00000000..59385cdf --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/callback_terminator.rb b/waydowntown/waydowntown_rails_generated/config/initializers/callback_terminator.rb new file mode 100644 index 00000000..649e8228 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/callback_terminator.rb @@ -0,0 +1,6 @@ +# Be sure to restart your server when you modify this file. + +# Do not halt callback chains when a callback returns false. This is a new +# Rails 5.0 default, so it is introduced as a configuration option to ensure +# that apps made with earlier versions of Rails are not affected when upgrading. +ActiveSupport.halt_callback_chains_on_return_false = false diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/cors.rb b/waydowntown/waydowntown_rails_generated/config/initializers/cors.rb new file mode 100644 index 00000000..3b1c1b5e --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/cors.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Avoid CORS issues when API is called from the frontend app. +# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. + +# Read more: https://github.com/cyu/rack-cors + +# Rails.application.config.middleware.insert_before 0, Rack::Cors do +# allow do +# origins 'example.com' +# +# resource '*', +# headers: :any, +# methods: [:get, :post, :put, :patch, :delete, :options, :head] +# end +# end diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/filter_parameter_logging.rb b/waydowntown/waydowntown_rails_generated/config/initializers/filter_parameter_logging.rb new file mode 100644 index 00000000..4a994e1e --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/inflections.rb b/waydowntown/waydowntown_rails_generated/config/initializers/inflections.rb new file mode 100644 index 00000000..ac033bf9 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/mime_types.rb b/waydowntown/waydowntown_rails_generated/config/initializers/mime_types.rb new file mode 100644 index 00000000..dc189968 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/mime_types.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/ssl_options.rb b/waydowntown/waydowntown_rails_generated/config/initializers/ssl_options.rb new file mode 100644 index 00000000..1775dea1 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/ssl_options.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure SSL options to enable HSTS with subdomains. +Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/waydowntown/waydowntown_rails_generated/config/initializers/to_time_preserves_timezone.rb b/waydowntown/waydowntown_rails_generated/config/initializers/to_time_preserves_timezone.rb new file mode 100644 index 00000000..8674be32 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/initializers/to_time_preserves_timezone.rb @@ -0,0 +1,10 @@ +# Be sure to restart your server when you modify this file. + +# Preserve the timezone of the receiver when calling to `to_time`. +# Ruby 2.4 will change the behavior of `to_time` to preserve the timezone +# when converting to an instance of `Time` instead of the previous behavior +# of converting to the local system timezone. +# +# Rails 5.0 introduced this config option so that apps made with earlier +# versions of Rails are not affected when upgrading. +ActiveSupport.to_time_preserves_timezone = true diff --git a/waydowntown/waydowntown_rails_generated/config/locales/en.yml b/waydowntown/waydowntown_rails_generated/config/locales/en.yml new file mode 100644 index 00000000..06539571 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/locales/en.yml @@ -0,0 +1,23 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t 'hello' +# +# In views, this is aliased to just `t`: +# +# <%= t('hello') %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more, please read the Rails Internationalization guide +# available at http://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/waydowntown/waydowntown_rails_generated/config/puma.rb b/waydowntown/waydowntown_rails_generated/config/puma.rb new file mode 100644 index 00000000..c7f311f8 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/puma.rb @@ -0,0 +1,47 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum, this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests, default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked webserver processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted this block will be run, if you are using `preload_app!` +# option you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/waydowntown/waydowntown_rails_generated/config/routes.rb b/waydowntown/waydowntown_rails_generated/config/routes.rb new file mode 100644 index 00000000..4846940c --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/routes.rb @@ -0,0 +1,20 @@ +=begin +Games API + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +The version of the OpenAPI document: 1.0.0 +Generated by: https://github.com/openapitools/openapi-generator.git + +=end +Rails.application.routes.draw do + + def add_openapi_route http_method, path, opts = {} + full_path = path.gsub(/{(.*?)}/, ':\1') + match full_path, to: "#{opts.fetch(:controller_name)}##{opts[:action_name]}", via: http_method + end + + add_openapi_route 'POST', '/answers', controller_name: 'default', action_name: 'answers_post' + add_openapi_route 'GET', '/games/{id}', controller_name: 'default', action_name: 'games_id_get' + add_openapi_route 'POST', '/games', controller_name: 'default', action_name: 'games_post' +end diff --git a/waydowntown/waydowntown_rails_generated/config/secrets.yml b/waydowntown/waydowntown_rails_generated/config/secrets.yml new file mode 100644 index 00000000..179b066b --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/secrets.yml @@ -0,0 +1,22 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rails secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +development: + secret_key_base: 2e13a003477a557ba3fcc6260c2ec69e411238b9b8b530c3c70e71f7cfc905b5f746f5c195a0282342a77ad6acd4e6ef8949106723200a99414fe83393d67344 + +test: + secret_key_base: fedf54236a94882c00e2fa0af308a4135f50941396437b9fbc38dd340c0a8bcf38cfcf6264cc2abc2af5bda26252293fe48e9a3e90fbfc4d25d6886d6d14b300 + +# Do not keep production secrets in the repository, +# instead read values from the environment. +production: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/waydowntown/waydowntown_rails_generated/config/spring.rb b/waydowntown/waydowntown_rails_generated/config/spring.rb new file mode 100644 index 00000000..c9119b40 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } diff --git a/waydowntown/waydowntown_rails_generated/db/migrate/.keep b/waydowntown/waydowntown_rails_generated/db/migrate/.keep new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/db/schema.rb b/waydowntown/waydowntown_rails_generated/db/schema.rb new file mode 100644 index 00000000..4dfbb168 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/db/schema.rb @@ -0,0 +1,16 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 0) do + +end diff --git a/waydowntown/waydowntown_rails_generated/db/seeds.rb b/waydowntown/waydowntown_rails_generated/db/seeds.rb new file mode 100644 index 00000000..1beea2ac --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/db/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). +# +# Examples: +# +# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) +# Character.create(name: 'Luke', movie: movies.first) diff --git a/waydowntown/waydowntown_rails_generated/docker-entrypoint.sh b/waydowntown/waydowntown_rails_generated/docker-entrypoint.sh new file mode 100644 index 00000000..93372e83 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -e + +if [ "$1" = 'bin/rails' ] && [ "$2" = 's' ]; then + rm -f /myapp/tmp/pids/server.pid + bin/rails db:migrate +fi + +exec "$@" diff --git a/waydowntown/waydowntown_rails_generated/lib/tasks/.keep b/waydowntown/waydowntown_rails_generated/lib/tasks/.keep new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/log/.keep b/waydowntown/waydowntown_rails_generated/log/.keep new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/log/development.log b/waydowntown/waydowntown_rails_generated/log/development.log new file mode 100644 index 00000000..348e4024 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/log/development.log @@ -0,0 +1 @@ +DEPRECATION WARNING: The `cache_control` argument is deprecated,replaced by `headers: { 'Cache-Control' => {:index=>"index", :headers=>{}} }`, and will be removed in Rails 5.1. (called from at /Users/b/Documents/Events/waydowntown/Code/adventures/waydowntown/waydowntown_rails_generated/config/environment.rb:5) diff --git a/waydowntown/waydowntown_rails_generated/public/404.html b/waydowntown/waydowntown_rails_generated/public/404.html new file mode 100644 index 00000000..b612547f --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/waydowntown/waydowntown_rails_generated/public/422.html b/waydowntown/waydowntown_rails_generated/public/422.html new file mode 100644 index 00000000..a21f82b3 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/waydowntown/waydowntown_rails_generated/public/500.html b/waydowntown/waydowntown_rails_generated/public/500.html new file mode 100644 index 00000000..061abc58 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/waydowntown/waydowntown_rails_generated/public/apple-touch-icon-precomposed.png b/waydowntown/waydowntown_rails_generated/public/apple-touch-icon-precomposed.png new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/public/apple-touch-icon.png b/waydowntown/waydowntown_rails_generated/public/apple-touch-icon.png new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/public/favicon.ico b/waydowntown/waydowntown_rails_generated/public/favicon.ico new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/public/robots.txt b/waydowntown/waydowntown_rails_generated/public/robots.txt new file mode 100644 index 00000000..3c9c7c01 --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/public/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-agent: * +# Disallow: / diff --git a/waydowntown/waydowntown_rails_generated/test/test_helper.rb b/waydowntown/waydowntown_rails_generated/test/test_helper.rb new file mode 100644 index 00000000..92e39b2d --- /dev/null +++ b/waydowntown/waydowntown_rails_generated/test/test_helper.rb @@ -0,0 +1,10 @@ +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +require 'rails/test_help' + +class ActiveSupport::TestCase + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + + # Add more helper methods to be used by all tests here... +end diff --git a/waydowntown/waydowntown_rails_generated/tmp/cache/.keep b/waydowntown/waydowntown_rails_generated/tmp/cache/.keep new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/tmp/pids/.keep b/waydowntown/waydowntown_rails_generated/tmp/pids/.keep new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/tmp/restart.txt b/waydowntown/waydowntown_rails_generated/tmp/restart.txt new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/tmp/sockets/.keep b/waydowntown/waydowntown_rails_generated/tmp/sockets/.keep new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rails_generated/vendor/.keep b/waydowntown/waydowntown_rails_generated/vendor/.keep new file mode 100644 index 00000000..e69de29b diff --git a/waydowntown/waydowntown_rust_server/.cargo/config b/waydowntown/waydowntown_rust_server/.cargo/config new file mode 100644 index 00000000..b8acc9c0 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/.cargo/config @@ -0,0 +1,18 @@ +[build] +rustflags = [ + "-W", "missing_docs", # detects missing documentation for public members + + "-W", "trivial_casts", # detects trivial casts which could be removed + + "-W", "trivial_numeric_casts", # detects trivial casts of numeric types which could be removed + + "-W", "unsafe_code", # usage of `unsafe` code + + "-W", "unused_qualifications", # detects unnecessarily qualified names + + "-W", "unused_extern_crates", # extern crates that are never used + + "-W", "unused_import_braces", # unnecessary braces around an imported item + + "-D", "warnings", # all warnings should be denied +] diff --git a/waydowntown/waydowntown_rust_server/.gitignore b/waydowntown/waydowntown_rust_server/.gitignore new file mode 100644 index 00000000..a9d37c56 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/waydowntown/waydowntown_rust_server/.openapi-generator-ignore b/waydowntown/waydowntown_rust_server/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/waydowntown/waydowntown_rust_server/.openapi-generator/FILES b/waydowntown/waydowntown_rust_server/.openapi-generator/FILES new file mode 100644 index 00000000..00922687 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/.openapi-generator/FILES @@ -0,0 +1,45 @@ +.cargo/config +.gitignore +.openapi-generator-ignore +Cargo.toml +README.md +api/openapi.yaml +docs/Answer.md +docs/AnswerAttributes.md +docs/AnswerInput.md +docs/AnswerRelationships.md +docs/Game.md +docs/GameAttributes.md +docs/GameInput.md +docs/GameRelationships.md +docs/GameRelationshipsIncarnation.md +docs/GameRelationshipsIncarnationData.md +docs/GameRelationshipsWinnerAnswer.md +docs/GameRelationshipsWinnerAnswerData.md +docs/GamesIdGet200Response.md +docs/GamesIdGet200ResponseIncludedInner.md +docs/GamesPost201Response.md +docs/GamesPost201ResponseIncludedInner.md +docs/Incarnation.md +docs/IncarnationAttributes.md +docs/IncarnationRelationships.md +docs/Region.md +docs/RegionAttributes.md +docs/RegionRelationships.md +docs/default_api.md +examples/ca.pem +examples/client/client_auth.rs +examples/client/main.rs +examples/server-chain.pem +examples/server-key.pem +examples/server/main.rs +examples/server/server.rs +examples/server/server_auth.rs +src/auth.rs +src/client/mod.rs +src/context.rs +src/header.rs +src/lib.rs +src/models.rs +src/server/mod.rs +src/server/server_auth.rs diff --git a/waydowntown/waydowntown_rust_server/.openapi-generator/VERSION b/waydowntown/waydowntown_rust_server/.openapi-generator/VERSION new file mode 100644 index 00000000..1985849f --- /dev/null +++ b/waydowntown/waydowntown_rust_server/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.7.0 diff --git a/waydowntown/waydowntown_rust_server/Cargo.toml b/waydowntown/waydowntown_rust_server/Cargo.toml new file mode 100644 index 00000000..9514ce3a --- /dev/null +++ b/waydowntown/waydowntown_rust_server/Cargo.toml @@ -0,0 +1,83 @@ +[package] +name = "waydowntown_server" +version = "1.0.0" +authors = ["OpenAPI Generator team and contributors"] +description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)" +# Override this license by providing a License Object in the OpenAPI. +license = "Unlicense" +edition = "2018" + +[features] +default = ["client", "server"] +client = [ + "hyper", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" +] +server = [ + "serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static" +] +conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"] + +[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.5", optional = true } + +[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] +hyper-openssl = { version = "0.9", optional = true } +openssl = {version = "0.10", optional = true } + +[dependencies] +# Common +async-trait = "0.1.24" +chrono = { version = "0.4", features = ["serde"] } +futures = "0.3" +swagger = { version = "6.1", features = ["serdejson", "server", "client", "tls", "tcp"] } +log = "0.4.0" +mime = "0.3" + +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "uuid", "chrono"] } +validator = { version = "0.16", features = ["derive"] } + +# Crates included if required by the API definition +uuid = {version = "1.3.1", features = ["serde", "v4"]} + +# Common between server and client features +hyper = {version = "0.14", features = ["full"], optional = true} +serde_ignored = {version = "0.1.1", optional = true} +url = {version = "2.1", optional = true} + +# Client-specific + +# Server, and client callback-specific +lazy_static = { version = "1.4", optional = true } +percent-encoding = {version = "2.1.0", optional = true} +regex = {version = "1.3", optional = true} + +# Conversion +frunk = { version = "0.3.0", optional = true } +frunk_derives = { version = "0.3.0", optional = true } +frunk_core = { version = "0.3.0", optional = true } +frunk-enum-derive = { version = "0.2.0", optional = true } +frunk-enum-core = { version = "0.2.0", optional = true } + +# Bearer authentication +jsonwebtoken = { version = "9.3.0", optional = false } + +[dev-dependencies] +clap = "2.25" +env_logger = "0.7" +tokio = { version = "1.14", features = ["full"] } +native-tls = "0.2" + +[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] +tokio-openssl = "0.6" +openssl = "0.10" + +[[example]] +name = "client" +required-features = ["client"] + +[[example]] +name = "server" +required-features = ["server"] diff --git a/waydowntown/waydowntown_rust_server/README.md b/waydowntown/waydowntown_rust_server/README.md new file mode 100644 index 00000000..b540b590 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/README.md @@ -0,0 +1,137 @@ +# Rust API for waydowntown_server + +No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + +## Overview + +This client/server was generated by the [openapi-generator] +(https://openapi-generator.tech) project. By using the +[OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote +server, you can easily generate a server stub. + +To see how to make this your own, look here: + +[README]((https://openapi-generator.tech)) + +- API version: 1.0.0 +- Build date: 2024-08-17T14:03:57.472491-05:00[America/Winnipeg] +- Generator version: 7.7.0 + + + +This autogenerated project defines an API crate `waydowntown_server` which contains: +* An `Api` trait defining the API in Rust. +* Data types representing the underlying data model. +* A `Client` type which implements `Api` and issues HTTP requests for each operation. +* A router which accepts HTTP requests and invokes the appropriate `Api` method for each operation. + +It also contains an example server and client which make use of `waydowntown_server`: + +* The example server starts up a web server using the `waydowntown_server` + router, and supplies a trivial implementation of `Api` which returns failure + for every operation. +* The example client provides a CLI which lets you invoke + any single operation on the `waydowntown_server` client by passing appropriate + arguments on the command line. + +You can use the example server and client as a basis for your own code. +See below for [more detail on implementing a server](#writing-a-server). + +## Examples + +Run examples with: + +``` +cargo run --example +``` + +To pass in arguments to the examples, put them after `--`, for example: + +``` +cargo run --example client -- --help +``` + +### Running the example server +To run the server, follow these simple steps: + +``` +cargo run --example server +``` + +### Running the example client +To run a client, follow one of the following simple steps: + +``` +cargo run --example client GamesIdGet +``` + +### HTTPS +The examples can be run in HTTPS mode by passing in the flag `--https`, for example: + +``` +cargo run --example server -- --https +``` + +This will use the keys/certificates from the examples directory. Note that the +server chain is signed with `CN=localhost`. + +## Using the generated library + +The generated library has a few optional features that can be activated through Cargo. + +* `server` + * This defaults to enabled and creates the basic skeleton of a server implementation based on hyper + * To create the server stack you'll need to provide an implementation of the API trait to provide the server function. +* `client` + * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper + * The constructed client implements the API trait by making remote API call. +* `conversions` + * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. + +See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. + +## Documentation for API Endpoints + +All URIs are relative to *http://localhost* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[****](docs/default_api.md#) | **POST** /answers | Create a new answer +[****](docs/default_api.md#) | **GET** /games/{id} | Fetch a game +[****](docs/default_api.md#) | **POST** /games | Create a new game + + +## Documentation For Models + + - [Answer](docs/Answer.md) + - [AnswerAttributes](docs/AnswerAttributes.md) + - [AnswerInput](docs/AnswerInput.md) + - [AnswerRelationships](docs/AnswerRelationships.md) + - [Game](docs/Game.md) + - [GameAttributes](docs/GameAttributes.md) + - [GameInput](docs/GameInput.md) + - [GameRelationships](docs/GameRelationships.md) + - [GameRelationshipsIncarnation](docs/GameRelationshipsIncarnation.md) + - [GameRelationshipsIncarnationData](docs/GameRelationshipsIncarnationData.md) + - [GameRelationshipsWinnerAnswer](docs/GameRelationshipsWinnerAnswer.md) + - [GameRelationshipsWinnerAnswerData](docs/GameRelationshipsWinnerAnswerData.md) + - [GamesIdGet200Response](docs/GamesIdGet200Response.md) + - [GamesIdGet200ResponseIncludedInner](docs/GamesIdGet200ResponseIncludedInner.md) + - [GamesPost201Response](docs/GamesPost201Response.md) + - [GamesPost201ResponseIncludedInner](docs/GamesPost201ResponseIncludedInner.md) + - [Incarnation](docs/Incarnation.md) + - [IncarnationAttributes](docs/IncarnationAttributes.md) + - [IncarnationRelationships](docs/IncarnationRelationships.md) + - [Region](docs/Region.md) + - [RegionAttributes](docs/RegionAttributes.md) + - [RegionRelationships](docs/RegionRelationships.md) + + +## Documentation For Authorization +Endpoints do not require authorization. + + +## Author + + + diff --git a/waydowntown/waydowntown_rust_server/api/openapi.yaml b/waydowntown/waydowntown_rust_server/api/openapi.yaml new file mode 100644 index 00000000..5296c587 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/api/openapi.yaml @@ -0,0 +1,386 @@ +openapi: 3.0.0 +info: + title: Games API + version: 1.0.0 +servers: +- url: / +paths: + /games: + post: + parameters: + - description: Optional concept for the game + explode: true + in: query + name: concept + required: false + schema: + type: string + style: form + - description: Filter incarnations by concept + explode: true + in: query + name: "incarnation_filter[concept]" + required: false + schema: + type: string + style: form + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/GameInput' + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/_games_post_201_response' + description: Successfully created game + summary: Create a new game + /games/{id}: + get: + parameters: + - explode: false + in: path + name: id + required: true + schema: + format: uuid + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/_games__id__get_200_response' + description: Successfully fetched game + summary: Fetch a game + /answers: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AnswerInput' + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/Answer' + description: Successfully created answer + summary: Create a new answer +components: + schemas: + GameInput: + example: + incarnation_id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + incarnation_id: + format: uuid + type: string + type: object + Game: + example: + relationships: + winner_answer: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + incarnation: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + complete: true + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + id: + format: uuid + type: string + attributes: + $ref: '#/components/schemas/Game_attributes' + relationships: + $ref: '#/components/schemas/Game_relationships' + type: object + Incarnation: + example: + relationships: + region: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + answer: answer + concept: concept + answers: + - answers + - answers + mask: mask + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + id: + format: uuid + type: string + attributes: + $ref: '#/components/schemas/Incarnation_attributes' + relationships: + $ref: '#/components/schemas/Incarnation_relationships' + type: object + Region: + properties: + id: + format: uuid + type: string + attributes: + $ref: '#/components/schemas/Region_attributes' + relationships: + $ref: '#/components/schemas/Region_relationships' + type: object + AnswerInput: + example: + game: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + answer: answer + properties: + answer: + type: string + game: + $ref: '#/components/schemas/Game_relationships_incarnation' + type: object + Answer: + example: + relationships: + game: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + answer: answer + correct: true + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + id: + format: uuid + type: string + attributes: + $ref: '#/components/schemas/Answer_attributes' + relationships: + $ref: '#/components/schemas/Answer_relationships' + type: object + _games_post_201_response_included_inner: + oneOf: + - $ref: '#/components/schemas/Incarnation' + - $ref: '#/components/schemas/Region' + _games_post_201_response: + example: + data: + relationships: + winner_answer: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + incarnation: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + complete: true + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + included: + - relationships: + region: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + answer: answer + concept: concept + answers: + - answers + - answers + mask: mask + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + - relationships: + region: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + answer: answer + concept: concept + answers: + - answers + - answers + mask: mask + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + data: + $ref: '#/components/schemas/Game' + included: + items: + $ref: '#/components/schemas/_games_post_201_response_included_inner' + type: array + type: object + _games__id__get_200_response_included_inner: + oneOf: + - $ref: '#/components/schemas/Incarnation' + - $ref: '#/components/schemas/Region' + - $ref: '#/components/schemas/Answer' + _games__id__get_200_response: + example: + data: + relationships: + winner_answer: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + incarnation: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + complete: true + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + included: + - relationships: + region: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + answer: answer + concept: concept + answers: + - answers + - answers + mask: mask + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + - relationships: + region: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + attributes: + answer: answer + concept: concept + answers: + - answers + - answers + mask: mask + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + data: + $ref: '#/components/schemas/Game' + included: + items: + $ref: '#/components/schemas/_games__id__get_200_response_included_inner' + type: array + type: object + Game_attributes: + example: + complete: true + properties: + complete: + type: boolean + type: object + Game_relationships_incarnation_data: + example: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + id: + format: uuid + type: string + type: object + Game_relationships_incarnation: + example: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + data: + $ref: '#/components/schemas/Game_relationships_incarnation_data' + type: object + Game_relationships_winner_answer_data: + example: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + nullable: true + properties: + id: + format: uuid + type: string + type: object + Game_relationships_winner_answer: + example: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + data: + $ref: '#/components/schemas/Game_relationships_winner_answer_data' + type: object + Game_relationships: + example: + winner_answer: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + incarnation: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + incarnation: + $ref: '#/components/schemas/Game_relationships_incarnation' + winner_answer: + $ref: '#/components/schemas/Game_relationships_winner_answer' + type: object + Incarnation_attributes: + example: + answer: answer + concept: concept + answers: + - answers + - answers + mask: mask + properties: + concept: + type: string + mask: + type: string + answer: + type: string + answers: + items: + type: string + type: array + type: object + Incarnation_relationships: + example: + region: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + region: + $ref: '#/components/schemas/Game_relationships_incarnation' + type: object + Region_attributes: + properties: + name: + type: string + description: + type: string + type: object + Region_relationships: + properties: + parent: + $ref: '#/components/schemas/Game_relationships_winner_answer' + type: object + Answer_attributes: + example: + answer: answer + correct: true + properties: + answer: + type: string + correct: + type: boolean + type: object + Answer_relationships: + example: + game: + data: + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + properties: + game: + $ref: '#/components/schemas/Game_relationships_incarnation' + type: object + diff --git a/waydowntown/waydowntown_rust_server/docs/Answer.md b/waydowntown/waydowntown_rust_server/docs/Answer.md new file mode 100644 index 00000000..d2a83487 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/Answer.md @@ -0,0 +1,12 @@ +# Answer + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] +**attributes** | [***models::AnswerAttributes**](Answer_attributes.md) | | [optional] [default to None] +**relationships** | [***models::AnswerRelationships**](Answer_relationships.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/AnswerAttributes.md b/waydowntown/waydowntown_rust_server/docs/AnswerAttributes.md new file mode 100644 index 00000000..d9763ad1 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/AnswerAttributes.md @@ -0,0 +1,11 @@ +# AnswerAttributes + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**answer** | **String** | | [optional] [default to None] +**correct** | **bool** | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/AnswerInput.md b/waydowntown/waydowntown_rust_server/docs/AnswerInput.md new file mode 100644 index 00000000..909a4a77 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/AnswerInput.md @@ -0,0 +1,11 @@ +# AnswerInput + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**answer** | **String** | | [optional] [default to None] +**game** | [***models::GameRelationshipsIncarnation**](Game_relationships_incarnation.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/AnswerRelationships.md b/waydowntown/waydowntown_rust_server/docs/AnswerRelationships.md new file mode 100644 index 00000000..f469646a --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/AnswerRelationships.md @@ -0,0 +1,10 @@ +# AnswerRelationships + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**game** | [***models::GameRelationshipsIncarnation**](Game_relationships_incarnation.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/Game.md b/waydowntown/waydowntown_rust_server/docs/Game.md new file mode 100644 index 00000000..a9eb9223 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/Game.md @@ -0,0 +1,12 @@ +# Game + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] +**attributes** | [***models::GameAttributes**](Game_attributes.md) | | [optional] [default to None] +**relationships** | [***models::GameRelationships**](Game_relationships.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GameAttributes.md b/waydowntown/waydowntown_rust_server/docs/GameAttributes.md new file mode 100644 index 00000000..66ea4efa --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GameAttributes.md @@ -0,0 +1,10 @@ +# GameAttributes + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**complete** | **bool** | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GameInput.md b/waydowntown/waydowntown_rust_server/docs/GameInput.md new file mode 100644 index 00000000..01b13bae --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GameInput.md @@ -0,0 +1,10 @@ +# GameInput + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**incarnation_id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GameRelationships.md b/waydowntown/waydowntown_rust_server/docs/GameRelationships.md new file mode 100644 index 00000000..c6a26ddc --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GameRelationships.md @@ -0,0 +1,11 @@ +# GameRelationships + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**incarnation** | [***models::GameRelationshipsIncarnation**](Game_relationships_incarnation.md) | | [optional] [default to None] +**winner_answer** | [***models::GameRelationshipsWinnerAnswer**](Game_relationships_winner_answer.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GameRelationshipsIncarnation.md b/waydowntown/waydowntown_rust_server/docs/GameRelationshipsIncarnation.md new file mode 100644 index 00000000..86a1b7f4 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GameRelationshipsIncarnation.md @@ -0,0 +1,10 @@ +# GameRelationshipsIncarnation + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**data** | [***models::GameRelationshipsIncarnationData**](Game_relationships_incarnation_data.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GameRelationshipsIncarnationData.md b/waydowntown/waydowntown_rust_server/docs/GameRelationshipsIncarnationData.md new file mode 100644 index 00000000..a0baea08 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GameRelationshipsIncarnationData.md @@ -0,0 +1,10 @@ +# GameRelationshipsIncarnationData + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GameRelationshipsWinnerAnswer.md b/waydowntown/waydowntown_rust_server/docs/GameRelationshipsWinnerAnswer.md new file mode 100644 index 00000000..75445f89 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GameRelationshipsWinnerAnswer.md @@ -0,0 +1,10 @@ +# GameRelationshipsWinnerAnswer + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**data** | [***models::GameRelationshipsWinnerAnswerData**](Game_relationships_winner_answer_data.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GameRelationshipsWinnerAnswerData.md b/waydowntown/waydowntown_rust_server/docs/GameRelationshipsWinnerAnswerData.md new file mode 100644 index 00000000..3cb673ef --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GameRelationshipsWinnerAnswerData.md @@ -0,0 +1,10 @@ +# GameRelationshipsWinnerAnswerData + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GamesIdGet200Response.md b/waydowntown/waydowntown_rust_server/docs/GamesIdGet200Response.md new file mode 100644 index 00000000..61230cff --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GamesIdGet200Response.md @@ -0,0 +1,11 @@ +# GamesIdGet200Response + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**data** | [***models::Game**](Game.md) | | [optional] [default to None] +**included** | [**Vec**](_games__id__get_200_response_included_inner.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GamesIdGet200ResponseIncludedInner.md b/waydowntown/waydowntown_rust_server/docs/GamesIdGet200ResponseIncludedInner.md new file mode 100644 index 00000000..7574e9fd --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GamesIdGet200ResponseIncludedInner.md @@ -0,0 +1,12 @@ +# GamesIdGet200ResponseIncludedInner + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] +**attributes** | [***models::AnswerAttributes**](Answer_attributes.md) | | [optional] [default to None] +**relationships** | [***models::AnswerRelationships**](Answer_relationships.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GamesPost201Response.md b/waydowntown/waydowntown_rust_server/docs/GamesPost201Response.md new file mode 100644 index 00000000..55fa71fe --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GamesPost201Response.md @@ -0,0 +1,11 @@ +# GamesPost201Response + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**data** | [***models::Game**](Game.md) | | [optional] [default to None] +**included** | [**Vec**](_games_post_201_response_included_inner.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/GamesPost201ResponseIncludedInner.md b/waydowntown/waydowntown_rust_server/docs/GamesPost201ResponseIncludedInner.md new file mode 100644 index 00000000..1eeacae0 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/GamesPost201ResponseIncludedInner.md @@ -0,0 +1,12 @@ +# GamesPost201ResponseIncludedInner + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] +**attributes** | [***models::RegionAttributes**](Region_attributes.md) | | [optional] [default to None] +**relationships** | [***models::RegionRelationships**](Region_relationships.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/Incarnation.md b/waydowntown/waydowntown_rust_server/docs/Incarnation.md new file mode 100644 index 00000000..9db270f2 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/Incarnation.md @@ -0,0 +1,12 @@ +# Incarnation + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] +**attributes** | [***models::IncarnationAttributes**](Incarnation_attributes.md) | | [optional] [default to None] +**relationships** | [***models::IncarnationRelationships**](Incarnation_relationships.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/IncarnationAttributes.md b/waydowntown/waydowntown_rust_server/docs/IncarnationAttributes.md new file mode 100644 index 00000000..72283c91 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/IncarnationAttributes.md @@ -0,0 +1,13 @@ +# IncarnationAttributes + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**concept** | **String** | | [optional] [default to None] +**mask** | **String** | | [optional] [default to None] +**answer** | **String** | | [optional] [default to None] +**answers** | **Vec** | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/IncarnationRelationships.md b/waydowntown/waydowntown_rust_server/docs/IncarnationRelationships.md new file mode 100644 index 00000000..3096d3e8 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/IncarnationRelationships.md @@ -0,0 +1,10 @@ +# IncarnationRelationships + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**region** | [***models::GameRelationshipsIncarnation**](Game_relationships_incarnation.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/Region.md b/waydowntown/waydowntown_rust_server/docs/Region.md new file mode 100644 index 00000000..38c7a014 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/Region.md @@ -0,0 +1,12 @@ +# Region + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | [***uuid::Uuid**](UUID.md) | | [optional] [default to None] +**attributes** | [***models::RegionAttributes**](Region_attributes.md) | | [optional] [default to None] +**relationships** | [***models::RegionRelationships**](Region_relationships.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/RegionAttributes.md b/waydowntown/waydowntown_rust_server/docs/RegionAttributes.md new file mode 100644 index 00000000..8df4173b --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/RegionAttributes.md @@ -0,0 +1,11 @@ +# RegionAttributes + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**name** | **String** | | [optional] [default to None] +**description** | **String** | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/RegionRelationships.md b/waydowntown/waydowntown_rust_server/docs/RegionRelationships.md new file mode 100644 index 00000000..8e101a6a --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/RegionRelationships.md @@ -0,0 +1,10 @@ +# RegionRelationships + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**parent** | [***models::GameRelationshipsWinnerAnswer**](Game_relationships_winner_answer.md) | | [optional] [default to None] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/waydowntown/waydowntown_rust_server/docs/default_api.md b/waydowntown/waydowntown_rust_server/docs/default_api.md new file mode 100644 index 00000000..0568a630 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/docs/default_api.md @@ -0,0 +1,96 @@ +# default_api + +All URIs are relative to *http://localhost* + +Method | HTTP request | Description +------------- | ------------- | ------------- +****](default_api.md#) | **POST** /answers | Create a new answer +****](default_api.md#) | **GET** /games/{id} | Fetch a game +****](default_api.md#) | **POST** /games | Create a new game + + +# **** +> models::Answer (answer_input) +Create a new answer + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **answer_input** | [**AnswerInput**](AnswerInput.md)| | + +### Return type + +[**models::Answer**](Answer.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **** +> models::GamesIdGet200Response (id) +Fetch a game + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **id** | [****](.md)| | + +### Return type + +[**models::GamesIdGet200Response**](_games__id__get_200_response.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **** +> models::GamesPost201Response (game_input, optional) +Create a new game + +### Required Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **game_input** | [**GameInput**](GameInput.md)| | + **optional** | **map[string]interface{}** | optional parameters | nil if no parameters + +### Optional Parameters +Optional parameters are passed through a map[string]interface{}. + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **game_input** | [**GameInput**](GameInput.md)| | + **concept** | **String**| Optional concept for the game | + **incarnation_filter_left_square_bracket_concept_right_square_bracket** | **String**| Filter incarnations by concept | + +### Return type + +[**models::GamesPost201Response**](_games_post_201_response.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/waydowntown/waydowntown_rust_server/examples/ca.pem b/waydowntown/waydowntown_rust_server/examples/ca.pem new file mode 100644 index 00000000..d2317fb5 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/examples/ca.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICtjCCAZ4CCQDpKecRERZ0xDANBgkqhkiG9w0BAQsFADAdMQswCQYDVQQGEwJV +UzEOMAwGA1UEAxMFTXkgQ0EwHhcNMTcwNTIzMTYwMDIzWhcNMTcwNjIyMTYwMDIz +WjAdMQswCQYDVQQGEwJVUzEOMAwGA1UEAxMFTXkgQ0EwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCt66py3x7sCSASRF2D05L5wkNDxAUjQKYx23W8Gbwv +GMGykk89BIdU5LX1JB1cKiUOkoIxfwAYuWc2V/wzTvVV7+11besnk3uX1c9KiqUF +LIX7kn/z5hzS4aelhKvH+MJlSZCSlp1ytpZbwo5GB5Pi2SGH56jDBiBoDRNBVdWL +z4wH7TdrQjqWwNxIZumD5OGMtcfJyuX08iPiEOaslOeoMqzObhvjc9aUgjVjhqyA +FkJGTXsi0oaD7oml+NE+mTNfEeZvEJQpLSjBY0OvQHzuHkyGBShBnfu/9x7/NRwd +WaqsLiF7/re9KDGYdJwP7Cu6uxYfKAyWarp6h2mG/GIdAgMBAAEwDQYJKoZIhvcN +AQELBQADggEBAGIl/VVIafeq/AJOQ9r7TzzB2ABJYr7NZa6bTu5O1jSp1Fonac15 +SZ8gvRxODgH22ZYSqghPG4xzq4J3hkytlQqm57ZEt2I2M3OqIp17Ndcc1xDYzpLl +tA0FrVn6crQTM8vQkTDtGesaCWX+7Fir5dK7HnYWzfpSmsOpST07PfbNisEXKOxG +Dj4lBL1OnhTjsJeymVS1pFvkKkrcEJO+IxFiHL3CDsWjcXB0Z+E1zBtPoYyYsNsO +rBrjUxcZewF4xqWZhpW90Mt61fY2nRgU0uUwHcvDQUqvmzKcsqYa4mPKzfBI5mxo +01Ta96cDD6pS5Y1hOflZ0g84f2g/7xBLLDA= +-----END CERTIFICATE----- diff --git a/waydowntown/waydowntown_rust_server/examples/client/client_auth.rs b/waydowntown/waydowntown_rust_server/examples/client/client_auth.rs new file mode 100644 index 00000000..0771d5cb --- /dev/null +++ b/waydowntown/waydowntown_rust_server/examples/client/client_auth.rs @@ -0,0 +1,17 @@ +use waydowntown_server::Claims; +use jsonwebtoken::{encode, errors::Error as JwtError, Algorithm, EncodingKey, Header}; +use log::debug; + +/// build an encrypted token with the provided claims. +pub fn build_token(my_claims: Claims, key: &[u8]) -> Result { + + // Ensure that you set the correct algorithm and correct key. + // See https://github.com/Keats/jsonwebtoken for more information. + let header = + Header { kid: Some("signing_key".to_owned()), alg: Algorithm::HS512, ..Default::default() }; + + let token = encode(&header, &my_claims, &EncodingKey::from_secret(key))?; + debug!("Derived token: {:?}", token); + + Ok(token) +} diff --git a/waydowntown/waydowntown_rust_server/examples/client/main.rs b/waydowntown/waydowntown_rust_server/examples/client/main.rs new file mode 100644 index 00000000..12bf73a0 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/examples/client/main.rs @@ -0,0 +1,137 @@ +#![allow(missing_docs, unused_variables, trivial_casts)] + + +#[allow(unused_imports)] +use futures::{future, Stream, stream}; +#[allow(unused_imports)] +use waydowntown_server::{Api, ApiNoContext, Claims, Client, ContextWrapperExt, models, + AnswersPostResponse, + GamesIdGetResponse, + GamesPostResponse, + }; +use clap::{App, Arg}; + +// NOTE: Set environment variable RUST_LOG to the name of the executable (or "cargo run") to activate console logging for all loglevels. +// See https://docs.rs/env_logger/latest/env_logger/ for more details + +#[allow(unused_imports)] +use log::info; + +// swagger::Has may be unused if there are no examples +#[allow(unused_imports)] +use swagger::{AuthData, ContextBuilder, EmptyContext, Has, Push, XSpanIdString}; + +type ClientContext = swagger::make_context_ty!(ContextBuilder, EmptyContext, Option, XSpanIdString); + +mod client_auth; +use client_auth::build_token; + + +// rt may be unused if there are no examples +#[allow(unused_mut)] +fn main() { + env_logger::init(); + + let matches = App::new("client") + .arg(Arg::with_name("operation") + .help("Sets the operation to run") + .possible_values(&[ + "GamesIdGet", + ]) + .required(true) + .index(1)) + .arg(Arg::with_name("https") + .long("https") + .help("Whether to use HTTPS or not")) + .arg(Arg::with_name("host") + .long("host") + .takes_value(true) + .default_value("localhost") + .help("Hostname to contact")) + .arg(Arg::with_name("port") + .long("port") + .takes_value(true) + .default_value("8080") + .help("Port to contact")) + .get_matches(); + + // Create Bearer-token with a fixed key (secret) for test purposes. + // In a real (production) system this Bearer token should be obtained via an external Identity/Authentication-server + // Ensure that you set the correct algorithm and encodingkey that matches what is used on the server side. + // See https://github.com/Keats/jsonwebtoken for more information + + let auth_token = build_token( + Claims { + sub: "tester@acme.com".to_owned(), + company: "ACME".to_owned(), + iss: "my_identity_provider".to_owned(), + // added a very long expiry time + aud: "org.acme.Resource_Server".to_string(), + exp: 10000000000, + // In this example code all available Scopes are added, so the current Bearer Token gets fully authorization. + scopes: [ + ].join(", ") + }, + b"secret").unwrap(); + + let auth_data = if !auth_token.is_empty() { + Some(AuthData::Bearer(swagger::auth::Bearer { token: auth_token})) + } else { + // No Bearer-token available, so return None + None + }; + + let is_https = matches.is_present("https"); + let base_url = format!("{}://{}:{}", + if is_https { "https" } else { "http" }, + matches.value_of("host").unwrap(), + matches.value_of("port").unwrap()); + + let context: ClientContext = + swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); + + let mut client : Box> = if matches.is_present("https") { + // Using Simple HTTPS + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http( + &base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + }; + + let mut rt = tokio::runtime::Runtime::new().unwrap(); + + match matches.value_of("operation") { + /* Disabled because there's no example. + Some("AnswersPost") => { + let result = rt.block_on(client.answers_post( + ??? + )); + info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has).get().clone()); + }, + */ + Some("GamesIdGet") => { + let result = rt.block_on(client.games_id_get( + serde_json::from_str::(r#"38400000-8cf0-11bd-b23e-10b96e4ef00d"#).expect("Failed to parse JSON example") + )); + info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has).get().clone()); + }, + /* Disabled because there's no example. + Some("GamesPost") => { + let result = rt.block_on(client.games_post( + ???, + Some("concept_example".to_string()), + Some("incarnation_filter_left_square_bracket_concept_right_square_bracket_example".to_string()) + )); + info!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has).get().clone()); + }, + */ + _ => { + panic!("Invalid operation provided") + } + } +} diff --git a/waydowntown/waydowntown_rust_server/examples/server-chain.pem b/waydowntown/waydowntown_rust_server/examples/server-chain.pem new file mode 100644 index 00000000..47d7e201 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/examples/server-chain.pem @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 4096 (0x1000) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, CN=My CA + Validity + Not Before: May 23 16:00:23 2017 GMT + Not After : Apr 29 16:00:23 2117 GMT + Subject: CN=localhost, C=US + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c9:d4:43:60:50:fc:d6:0f:38:4d:5d:5e:aa:7c: + c0:5e:a9:ec:d9:93:78:d3:93:72:28:41:f5:08:a5: + ea:ac:67:07:d7:1f:f7:7d:74:69:7e:46:89:20:4b: + 7a:2d:9b:02:08:e7:6f:0f:1d:0c:0f:c7:60:69:19: + 4b:df:7e:ca:75:94:0b:49:71:e3:6d:f2:e8:79:fd: + ed:0a:94:67:55:f3:ca:6b:61:ba:58:b7:2e:dd:7b: + ca:b9:02:9f:24:36:ac:26:8f:04:8f:81:c8:35:10: + f4:aa:33:b2:24:16:f8:f7:1e:ea:f7:16:fe:fa:34: + c3:dd:bb:2c:ba:7a:df:4d:e2:da:1e:e5:d2:28:44: + 6e:c8:96:e0:fd:09:0c:14:0c:31:dc:e0:ca:c1:a7: + 9b:bf:16:8c:f7:36:3f:1b:2e:dd:90:eb:45:78:51: + bf:59:22:1e:c6:8c:0a:69:88:e5:03:5e:73:b7:fc: + 93:7f:1b:46:1b:97:68:c5:c0:8b:35:1f:bb:1e:67: + 7f:55:b7:3b:55:3f:ea:f2:ca:db:cc:52:cd:16:89: + db:15:47:bd:f2:cd:6c:7a:d7:b4:1a:ac:c8:15:6c: + 6a:fb:77:c4:e9:f2:30:e0:14:24:66:65:6f:2a:e5: + 2d:cc:f6:81:ae:57:c8:d1:9b:38:90:dc:60:93:02: + 5e:cb + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + 1c:7c:39:e8:3d:49:b2:09:1e:68:5a:2f:74:18:f4:63:b5:8c: + f6:e6:a1:e3:4d:95:90:99:ef:32:5c:34:40:e8:55:13:0e:e0: + 1c:be:cd:ab:3f:64:38:99:5e:2b:c1:81:53:a0:18:a8:f6:ee: + 6a:33:73:6c:9a:73:9d:86:08:5d:c7:11:38:46:4c:cd:a0:47: + 37:8f:fe:a6:50:a9:02:21:99:42:86:5e:47:fe:65:56:60:1d: + 16:53:86:bd:e4:63:c5:69:cf:fa:30:51:ab:a1:c3:50:53:cc: + 66:1c:4c:ff:3f:2a:39:4d:a2:8f:9d:d1:a7:8b:22:e4:78:69: + 24:06:83:4d:cc:0a:c0:87:69:9b:bc:80:a9:d2:b7:a5:23:84: + 7e:a2:32:26:7c:78:0e:bd:db:cd:3b:69:18:33:b8:44:ef:96: + b4:99:86:ee:06:bd:51:1c:c7:a1:a4:0c:c4:4c:51:a0:df:ac: + 14:07:88:8e:d7:39:45:fe:52:e0:a3:4c:db:5d:7a:ab:4d:e4: + ca:06:e8:bd:74:6f:46:e7:93:4a:4f:1b:67:e7:a5:9f:ef:9c: + 02:49:d1:f2:d5:e9:53:ee:09:21:ac:08:c8:15:f7:af:35:b9: + 4f:11:0f:43:ae:46:8e:fd:5b:8d:a3:4e:a7:2c:b7:25:ed:e4: + e5:94:1d:e3 +-----BEGIN CERTIFICATE----- +MIICtTCCAZ0CAhAAMA0GCSqGSIb3DQEBCwUAMB0xCzAJBgNVBAYTAlVTMQ4wDAYD +VQQDEwVNeSBDQTAgFw0xNzA1MjMxNjAwMjNaGA8yMTE3MDQyOTE2MDAyM1owITES +MBAGA1UEAxMJbG9jYWxob3N0MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMnUQ2BQ/NYPOE1dXqp8wF6p7NmTeNOTcihB9Qil6qxn +B9cf9310aX5GiSBLei2bAgjnbw8dDA/HYGkZS99+ynWUC0lx423y6Hn97QqUZ1Xz +ymthuli3Lt17yrkCnyQ2rCaPBI+ByDUQ9KozsiQW+Pce6vcW/vo0w927LLp6303i +2h7l0ihEbsiW4P0JDBQMMdzgysGnm78WjPc2Pxsu3ZDrRXhRv1kiHsaMCmmI5QNe +c7f8k38bRhuXaMXAizUfux5nf1W3O1U/6vLK28xSzRaJ2xVHvfLNbHrXtBqsyBVs +avt3xOnyMOAUJGZlbyrlLcz2ga5XyNGbOJDcYJMCXssCAwEAATANBgkqhkiG9w0B +AQsFAAOCAQEAHHw56D1JsgkeaFovdBj0Y7WM9uah402VkJnvMlw0QOhVEw7gHL7N +qz9kOJleK8GBU6AYqPbuajNzbJpznYYIXccROEZMzaBHN4/+plCpAiGZQoZeR/5l +VmAdFlOGveRjxWnP+jBRq6HDUFPMZhxM/z8qOU2ij53Rp4si5HhpJAaDTcwKwIdp +m7yAqdK3pSOEfqIyJnx4Dr3bzTtpGDO4RO+WtJmG7ga9URzHoaQMxExRoN+sFAeI +jtc5Rf5S4KNM2116q03kygbovXRvRueTSk8bZ+eln++cAknR8tXpU+4JIawIyBX3 +rzW5TxEPQ65Gjv1bjaNOpyy3Je3k5ZQd4w== +-----END CERTIFICATE----- diff --git a/waydowntown/waydowntown_rust_server/examples/server-key.pem b/waydowntown/waydowntown_rust_server/examples/server-key.pem new file mode 100644 index 00000000..29c00682 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/examples/server-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ1ENgUPzWDzhN +XV6qfMBeqezZk3jTk3IoQfUIpeqsZwfXH/d9dGl+RokgS3otmwII528PHQwPx2Bp +GUvffsp1lAtJceNt8uh5/e0KlGdV88prYbpYty7de8q5Ap8kNqwmjwSPgcg1EPSq +M7IkFvj3Hur3Fv76NMPduyy6et9N4toe5dIoRG7IluD9CQwUDDHc4MrBp5u/Foz3 +Nj8bLt2Q60V4Ub9ZIh7GjAppiOUDXnO3/JN/G0Ybl2jFwIs1H7seZ39VtztVP+ry +ytvMUs0WidsVR73yzWx617QarMgVbGr7d8Tp8jDgFCRmZW8q5S3M9oGuV8jRmziQ +3GCTAl7LAgMBAAECggEBAKEd1q9j14KWYc64s6KLthGbutyxsinMMbxbct11fdIk +6YhdF3fJ35ETg9IJDr6rWEN9ZRX+jStncNpVfFEs6ThVd3Eo/nI+EEGaaIkikR93 +X2a7fEPn7/yVHu70XdBN6L1bPDvHUeiy4W2hmRrgT90OjGm1rNRWHOm7yugOwIZu +HclzbR9Ca7EInFnotUiDQm9sw9VKHbJHqWx6OORdZrxR2ytYs0Qkq0XpGMvti2HW +7WAmKTg5QM8myXW7+/4iqb/u68wVBR2BBalShKmIf7lim9O3W2a1RjDdsvm/wNe9 +I+D+Iq825vpqkKXcrxYlpVg7hYiaQaW/MNsEb7lQRjECgYEA/RJYby0POW+/k0Jn +jO8UmJVEMiuGa8WIUu/JJWMOmzRCukjSRNQOkt7niQrZPJYE8W6clM6RJTolWf9L +IL6mIb+mRaoudUk8SHGDq7ho1iMg9GK8lhYxvKh1Q6uv8EyVSkgLknAEY0NANKC1 +zNdU5Dhven9aRX2gq9vP4XwMz2MCgYEAzCogQ7IFk+gkp3k491dOZnrGRoRCfuzo +4CJtyKFgOSd7BjmpcKkj0IPfVBjw6GjMIxfQRMTQmxAjjWevH45vG8l0Iiwz/gSp +81b5nsDEX5uv2Olcmcz5zxRFy36jOZ9ihMWinxcIlT2oDbyCdbruDKZq9ieJ9S8g +4qGx0OkwE3kCgYEA7CmAiU89U9YqqttfEq/RQoqY91CSwmO10d+ej9seuEtOsdRf +FIfnibulycdr7hP5TOxyBpO1802NqayJiWcgVYIpQf2MGTtcnCYCP+95NcvWZvj1 +EAJqK6nwtFO1fcOZ1ZXh5qfOEGujsPkAbsXLnKXlsiTCMvMHSxl3pu5Cbg0CgYBf +JjbZNctRrjv+7Qj2hPLd4dQsIxGWc7ToWENP4J2mpVa5hQAJqFovoHXhjKohtk2F +AWEn243Y5oGbMjo0e74edhmwn2cvuF64MM2vBem/ISCn98IXT6cQskMA3qkVfsl8 +VVs/x41ReGWs2TD3y0GMFbb9t1mdMfSiincDhNnKCQKBgGfeT4jKyYeCoCw4OLI1 +G75Gd0METt/IkppwODPpNwj3Rp9I5jctWZFA/3wCX/zk0HgBeou5AFNS4nQZ/X/L +L9axbSdR7UJTGkT1r4gu3rLkPV4Tk+8XM03/JT2cofMlzQBuhvl1Pn4SgKowz7hl +lS76ECw4Av3T0S34VW9Z5oye +-----END PRIVATE KEY----- diff --git a/waydowntown/waydowntown_rust_server/examples/server/main.rs b/waydowntown/waydowntown_rust_server/examples/server/main.rs new file mode 100644 index 00000000..37fc2b43 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/examples/server/main.rs @@ -0,0 +1,32 @@ +//! Main binary entry point for waydowntown_server implementation. +// This is the amended version that adds Authorization via Inversion of Control. + +#![allow(missing_docs)] + + +use clap::{App, Arg}; + +mod server; +mod server_auth; + + +/// Create custom server, wire it to the autogenerated router, +/// and pass it to the web server. +#[tokio::main] +async fn main() -> Result<(), Box> { + env_logger::init(); + + let matches = App::new("server") + .arg(Arg::with_name("https") + .long("https") + .help("Whether to use HTTPS or not")) + .get_matches(); + + let addr = "127.0.0.1:8080"; + let https = matches.is_present("https"); + let database_url = "postgres://localhost/registrations_dev"; + + server::create(addr, https, database_url).await?; + + Ok(()) +} diff --git a/waydowntown/waydowntown_rust_server/examples/server/server.rs b/waydowntown/waydowntown_rust_server/examples/server/server.rs new file mode 100644 index 00000000..3c72f20f --- /dev/null +++ b/waydowntown/waydowntown_rust_server/examples/server/server.rs @@ -0,0 +1,316 @@ +//! Main library entry point for waydowntown_server implementation. + +#![allow(unused_imports)] + +use async_trait::async_trait; +use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt}; +use hyper::server::conn::Http; +use hyper::service::Service; +use log::info; +use std::future::Future; +use std::marker::PhantomData; +use std::net::SocketAddr; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll}; +use swagger::{Has, XSpanIdString, Nullable}; +use swagger::auth::MakeAllowAllAuthenticator; +use swagger::EmptyContext; +use tokio::net::TcpListener; +use sqlx::postgres::PgPool; + +#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +use openssl::ssl::{Ssl, SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod}; + +use waydowntown_server::models; + +/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names +pub async fn create(addr: &str, https: bool, database_url: &str) -> Result<(), Box> { + let addr = addr.parse().expect("Failed to parse bind address"); + + // Establish database connection + let db_pool = PgPool::connect(database_url).await?; + + let server = Server::new(db_pool); + + let service = MakeService::new(server); + + // This pushes a fourth layer of the middleware-stack even though Swagger assumes only three levels. + // This fourth layer creates an accept-all policy, hower the example-code already acchieves the same via a Bearer-token with full permissions, so next line is not needed (anymore). + // let service = MakeAllowAllAuthenticator::new(service, "cosmo"); + + #[allow(unused_mut)] + let mut service = + waydowntown_server::server::context::MakeAddContext::<_, EmptyContext>::new( + service + ); + + if https { + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + { + unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS"); + } + + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + { + let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor"); + + // Server authentication + ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key"); + ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set certificate chain"); + ssl.check_private_key().expect("Failed to check private key"); + + let tls_acceptor = ssl.build(); + let tcp_listener = TcpListener::bind(&addr).await.unwrap(); + + info!("Starting a server (with https)"); + loop { + if let Ok((tcp, _)) = tcp_listener.accept().await { + let ssl = Ssl::new(tls_acceptor.context()).unwrap(); + let addr = tcp.peer_addr().expect("Unable to get remote address"); + let service = service.call(addr); + + tokio::spawn(async move { + let tls = tokio_openssl::SslStream::new(ssl, tcp).map_err(|_| ())?; + let service = service.await.map_err(|_| ())?; + + Http::new() + .serve_connection(tls, service) + .await + .map_err(|_| ()) + }); + } + } + } + } else { + info!("Starting a server (over http, so no TLS)"); + // Using HTTP + hyper::server::Server::bind(&addr).serve(service).await?; + Ok(()) + } +} + +#[derive(Clone)] +pub struct Server { + marker: PhantomData, + db: PgPool, +} + +impl Server { + pub fn new(db: PgPool) -> Self { + Server{marker: PhantomData, db} + } +} + + +use jsonwebtoken::{decode, encode, errors::Error as JwtError, Algorithm, DecodingKey, EncodingKey, Header, TokenData, Validation}; +use serde::{Deserialize, Serialize}; +use swagger::auth::Authorization; +use crate::server_auth; + + +use waydowntown_server::{ + Api, + AnswersPostResponse, + GamesIdGetResponse, + GamesPostResponse, +}; +use waydowntown_server::server::MakeService; +use std::error::Error; +use swagger::ApiError; +struct CustomApiError(String); + +impl From for CustomApiError { + fn from(error: String) -> Self { + CustomApiError(error) + } +} + +impl From for ApiError { + fn from(error: CustomApiError) -> Self { + ApiError(error.0) + } +} + +#[async_trait] +impl Api for Server where C: Has + Send + Sync +{ + /// Create a new answer + async fn answers_post( + &self, + answer_input: models::AnswerInput, + context: &C) -> Result + { + info!("answers_post({:?}) - X-Span-ID: {:?}", answer_input, context.get().0.clone()); + Err(ApiError("Api-Error: Operation is NOT implemented".into())) + } + + /// Fetch a game + async fn games_id_get( + &self, + id: uuid::Uuid, + _context: &C) -> Result + { + // Fetch the game and its related data + let result = sqlx::query!( + r#" + WITH RECURSIVE region_hierarchy AS ( + SELECT r.id, r.name, r.description, r.parent_id + FROM waydowntown.regions r + JOIN waydowntown.incarnations i ON i.region_id = r.id + JOIN waydowntown.games g ON g.incarnation_id = i.id + WHERE g.id = $1 + + UNION ALL + + SELECT r.id, r.name, r.description, r.parent_id + FROM waydowntown.regions r + JOIN region_hierarchy rh ON r.id = rh.parent_id + ) + SELECT + g.id AS game_id, + g.winner_answer_id IS NOT NULL AS game_complete, + i.id AS incarnation_id, + i.concept AS incarnation_concept, + i.mask AS incarnation_mask, + i.answer AS incarnation_answer, + i.answers AS incarnation_answers, + r.id AS region_id, + r.name AS region_name, + r.description AS region_description, + p.id AS parent_region_id, + p.name AS parent_region_name, + p.description AS parent_region_description + FROM waydowntown.games g + JOIN waydowntown.incarnations i ON g.incarnation_id = i.id + JOIN region_hierarchy r ON i.region_id = r.id + LEFT JOIN region_hierarchy p ON r.parent_id = p.id + WHERE g.id = $1 + "#, + id + ) + .fetch_one(&self.db) + .await + .map_err(|e| CustomApiError(e.to_string()))?; + + // Construct the Game object + let game = models::Game { + id: Some(result.game_id), + attributes: Some(models::GameAttributes { + complete: result.game_complete, + }), + relationships: Some(models::GameRelationships { + incarnation: Some(models::GameRelationshipsIncarnation { + data: Some(models::GameRelationshipsIncarnationData { + id: Some(result.incarnation_id), + }), + }), + winner_answer: Some(models::GameRelationshipsWinnerAnswer { + data: None, // We're not fetching the winner answer in this query + }), + }), + }; + + // Construct the Incarnation object + let incarnation = models::Incarnation { + id: Some(result.incarnation_id), + attributes: Some(models::IncarnationAttributes { + concept: result.incarnation_concept.clone(), + mask: result.incarnation_mask.clone(), + answer: result.incarnation_answer.clone(), + answers: result.incarnation_answers.clone(), + }), + relationships: Some(models::IncarnationRelationships { + region: Some(models::IncarnationRelationshipsRegion { + data: Some(models::IncarnationRelationshipsRegionData { + id: result.region_id, + }), + }), + }), + }; + + // Construct the Region object + let region = models::Region { + id: result.region_id, + attributes: Some(models::RegionAttributes { + name: result.region_name.clone(), + description: result.region_description.clone(), + }), + relationships: Some(models::RegionRelationships { + parent: Some(models::GameRelationshipsWinnerAnswer { + data: result.parent_region_id.map(|id| swagger::Nullable::Present(models::GameRelationshipsWinnerAnswerData { id: Some(id) })), + }), + }), + }; + + // Construct the Parent Region object if it exists + let parent_region = result.parent_region_id.map(|id| models::Region { + id: Some(id), + attributes: Some(models::RegionAttributes { + name: result.parent_region_name.clone(), + description: result.parent_region_description.clone(), + }), + relationships: Some(models::RegionRelationships { + parent: Some(models::GameRelationshipsWinnerAnswer { data: None }), + }), + }); + + // Construct the included data + let mut included = vec![ + models::GamesIdGet200ResponseIncludedInner { + id: incarnation.id, + attributes: Some(models::AnswerAttributes { + answer: incarnation.attributes.as_ref().and_then(|attr| attr.answer.clone()), + correct: None, // You might want to set this based on your logic + }), + relationships: Some(models::AnswerRelationships { + game: None, // You might want to set this based on your logic + }), + }, + models::GamesIdGet200ResponseIncludedInner { + id: region.id, + attributes: Some(models::AnswerAttributes { + answer: Some(region.attributes.as_ref().map_or_else(String::new, |attr| attr.name.clone().unwrap_or_default())), + correct: None, + }), + relationships: Some(models::AnswerRelationships { + game: None, + }), + }, + ]; + + // Add parent region to included if it exists + if let Some(parent) = parent_region { + included.push(models::GamesIdGet200ResponseIncludedInner { + id: parent.id, + attributes: Some(models::AnswerAttributes { + answer: Some(parent.attributes.as_ref().map_or_else(String::new, |attr| attr.name.clone().unwrap_or_default())), + correct: None, + }), + relationships: Some(models::AnswerRelationships { + game: None, + }), + }); + } + + // Construct the response + let response = models::GamesIdGet200Response { + data: Some(game), + included: Some(included), + }; + + Ok(GamesIdGetResponse::SuccessfullyFetchedGame(response)) + } + + /// Create a new game + async fn games_post( + &self, + game_input: models::GameInput, + concept: Option, + incarnation_filter_left_square_bracket_concept_right_square_bracket: Option, + context: &C) -> Result + { + info!("games_post({:?}, {:?}, {:?}) - X-Span-ID: {:?}", game_input, concept, incarnation_filter_left_square_bracket_concept_right_square_bracket, context.get().0.clone()); + Err(ApiError("Api-Error: Operation is NOT implemented".into())) + } + +} diff --git a/waydowntown/waydowntown_rust_server/examples/server/server_auth.rs b/waydowntown/waydowntown_rust_server/examples/server/server_auth.rs new file mode 100644 index 00000000..9ed0fd23 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/examples/server/server_auth.rs @@ -0,0 +1,115 @@ +use crate::server::Server; +use jsonwebtoken::{decode, decode_header, errors as JwtError, DecodingKey, TokenData, Validation}; +use log::{debug, error}; +use swagger::auth::Authorization; +use swagger::{ + auth::{Basic, Bearer}, + ApiError, Has, XSpanIdString, +}; +use waydowntown_server::{AuthenticationApi, Claims}; + +// NOTE: Set environment variable RUST_LOG to the name of the executable (or "cargo run") to activate console logging for all loglevels. +// See https://docs.rs/env_logger/latest/env_logger/ for more details + +/// Get a dummy claim with full permissions (all scopes) for testing purposes +fn full_permission_claim() -> Claims { + Claims { + sub: "tester@acme.com".to_owned(), + company: "ACME".to_owned(), + iss: "mini-bank-IDP".to_owned(), + aud: "org.acme.Resource_Server".to_string(), + // added a very long expiry time + exp: 10000000000, + // In this example code all available Scopes are added, so the current Bearer Token gets fully authorization. + scopes: Vec::::new().join(", "), + } +} + +/// Extract the data from a Bearer token using the provided Key (secret) and using the HS512-algorithm in this example. +fn extract_token_data(token: &str, key: &[u8]) -> Result, JwtError::Error> { + // Ensure that you set the correct algorithm and correct key. + // See https://github.com/Keats/jsonwebtoken for more information. + let header = decode_header(token)?; + let validation = { + let mut validation = Validation::new(header.alg); + validation.set_audience(&["org.acme.Resource_Server"]); + validation.validate_exp = true; + validation + }; + + let token_data = decode::(&token, &DecodingKey::from_secret(key), &validation)?; + + Ok(token_data) +} + +/// Build a swagger-Authorization based on the claims (Assuming claims have been extracted from a validated token) +fn build_authorization(claims: Claims) -> Authorization { + let mut scopes = std::collections::BTreeSet::::new(); + claims.scopes.split(",").map(|s| s.trim()).for_each(|s| { + let _ = scopes.insert(s.to_string()); + }); + let scopes = swagger::auth::Scopes::Some(scopes); + + Authorization { + subject: claims.sub, + scopes, + issuer: Some(claims.iss), + } +} + +fn get_jwt_error_string(error: JwtError::Error) -> String { + match error.kind() { + JwtError::ErrorKind::InvalidSignature => "Incorrect token signature".to_owned(), + JwtError::ErrorKind::InvalidAlgorithm => "The Algorithm is not correct".to_owned(), + JwtError::ErrorKind::ExpiredSignature => "The token has expired".to_owned(), + JwtError::ErrorKind::Base64(e) => format!("Base64 decode failed: {e}"), + JwtError::ErrorKind::Json(e) => format!("JSON decoding: {e}"), + JwtError::ErrorKind::Utf8(e) => format!("Invalid UTF-8: {e}"), + _ => error.to_string(), + } +} + +impl AuthenticationApi for Server +where + C: Has + Send + Sync, +{ + /// Implementation of the method to map a Bearer-token to an Authorization + fn bearer_authorization(&self, bearer: &Bearer) -> Result { + debug!("\tAuthorizationApi: Received Bearer-token, {bearer:#?}"); + + match extract_token_data(&bearer.token, b"secret") { + Ok(auth_data) => { + debug!("\tUnpack auth_data as: {auth_data:#?}"); + let authorization = build_authorization(auth_data.claims); + Ok(authorization) + } + Err(err) => { + let msg = get_jwt_error_string(err); + error!("Failed to unpack Bearer-token: {msg}"); + Err(ApiError(msg)) + } + } + } + + /// Implementation of the method to map an api-key to an Authorization + fn apikey_authorization(&self, api_key: &str) -> Result { + debug!("\tAuthorizationApi: Received api-key, {api_key:#?}"); + + // TODO: insert the logic to map received apikey to the set of claims + let claims = full_permission_claim(); + + // and build an authorization out of it + Ok(build_authorization(claims)) + } + + /// Implementation of the method to map a basic authentication (username and password) to an Authorization + fn basic_authorization(&self, basic: &Basic) -> Result { + debug!("\tAuthorizationApi: Received Basic-token, {basic:#?}"); + + // TODO: insert the logic to map received apikey to the set of claims + let claims = full_permission_claim(); + + // and build an authorization out of it + Ok(build_authorization(claims)) + } +} diff --git a/waydowntown/waydowntown_rust_server/src/auth.rs b/waydowntown/waydowntown_rust_server/src/auth.rs new file mode 100644 index 00000000..cbaba3dc --- /dev/null +++ b/waydowntown/waydowntown_rust_server/src/auth.rs @@ -0,0 +1,62 @@ +use std::collections::BTreeSet; +use crate::server::Authorization; +use serde::{Deserialize, Serialize}; +use swagger::{ApiError, auth::{Basic, Bearer}}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub sub: String, + pub iss: String, + pub aud: String, + pub company: String, + pub exp: u64, + pub scopes: String, +} + + +pub trait AuthenticationApi { + + /// Method should be implemented (see example-code) to map Bearer-token to an Authorization + fn bearer_authorization(&self, token: &Bearer) -> Result; + + /// Method should be implemented (see example-code) to map ApiKey to an Authorization + fn apikey_authorization(&self, token: &str) -> Result; + + /// Method should be implemented (see example-code) to map Basic (Username:password) to an Authorization + fn basic_authorization(&self, basic: &Basic) -> Result; +} + +// Implement it for AllowAllAuthenticator (dummy is needed, but should not used as we have Bearer authorization) +use swagger::auth::{AllowAllAuthenticator, RcBound, Scopes}; + +fn dummy_authorization() -> Authorization { + // Is called when MakeAllowAllAuthenticator is added to the stack. This is not needed as we have Bearer-authorization in the example-code. + // However, if you want to use it anyway this can not be unimplemented, so dummy implementation added. + // unimplemented!() + Authorization{ + subject: "Dummmy".to_owned(), + scopes: Scopes::Some(BTreeSet::new()), // create an empty scope, as this should not be used + issuer: None + } +} + +impl AuthenticationApi for AllowAllAuthenticator +where + RC: RcBound, + RC::Result: Send + 'static { + + /// Get method to map Bearer-token to an Authorization + fn bearer_authorization(&self, _token: &Bearer) -> Result { + Ok(dummy_authorization()) + } + + /// Get method to map api-key to an Authorization + fn apikey_authorization(&self, _apikey: &str) -> Result { + Ok(dummy_authorization()) + } + + /// Get method to map basic token to an Authorization + fn basic_authorization(&self, _basic: &Basic) -> Result { + Ok(dummy_authorization()) + } +} diff --git a/waydowntown/waydowntown_rust_server/src/client/mod.rs b/waydowntown/waydowntown_rust_server/src/client/mod.rs new file mode 100644 index 00000000..f0475b4d --- /dev/null +++ b/waydowntown/waydowntown_rust_server/src/client/mod.rs @@ -0,0 +1,649 @@ +use async_trait::async_trait; +use futures::{Stream, future, future::BoxFuture, stream, future::TryFutureExt, future::FutureExt, stream::StreamExt}; +use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; +use hyper::{Body, Request, Response, service::Service, Uri}; +use percent_encoding::{utf8_percent_encode, AsciiSet}; +use std::borrow::Cow; +use std::convert::TryInto; +use std::io::{ErrorKind, Read}; +use std::error::Error; +use std::future::Future; +use std::fmt; +use std::marker::PhantomData; +use std::path::Path; +use std::sync::{Arc, Mutex}; +use std::str; +use std::str::FromStr; +use std::string::ToString; +use std::task::{Context, Poll}; +use swagger::{ApiError, AuthData, BodyExt, Connector, DropContextService, Has, XSpanIdString}; +use url::form_urlencoded; + + +use crate::models; +use crate::header; + +/// https://url.spec.whatwg.org/#fragment-percent-encode-set +#[allow(dead_code)] +const FRAGMENT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS + .add(b' ').add(b'"').add(b'<').add(b'>').add(b'`'); + +/// This encode set is used for object IDs +/// +/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`, +/// the vertical bar (|) is encoded. +#[allow(dead_code)] +const ID_ENCODE_SET: &AsciiSet = &FRAGMENT_ENCODE_SET.add(b'|'); + +use crate::{Api, + AnswersPostResponse, + GamesIdGetResponse, + GamesPostResponse + }; + +/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes. +fn into_base_path(input: impl TryInto, correct_scheme: Option<&'static str>) -> Result { + // First convert to Uri, since a base path is a subset of Uri. + let uri = input.try_into()?; + + let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?; + + // Check the scheme if necessary + if let Some(correct_scheme) = correct_scheme { + if scheme != correct_scheme { + return Err(ClientInitError::InvalidScheme); + } + } + + let host = uri.host().ok_or(ClientInitError::MissingHost)?; + let port = uri.port_u16().map(|x| format!(":{}", x)).unwrap_or_default(); + Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/'))) +} + +/// A client that implements the API by making HTTP calls out to a server. +pub struct Client where + S: Service< + (Request, C), + Response=Response> + Clone + Sync + Send + 'static, + S::Future: Send + 'static, + S::Error: Into + fmt::Display, + C: Clone + Send + Sync + 'static +{ + /// Inner service + client_service: S, + + /// Base path of the API + base_path: String, + + /// Marker + marker: PhantomData, +} + +impl fmt::Debug for Client where + S: Service< + (Request, C), + Response=Response> + Clone + Sync + Send + 'static, + S::Future: Send + 'static, + S::Error: Into + fmt::Display, + C: Clone + Send + Sync + 'static +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Client {{ base_path: {} }}", self.base_path) + } +} + +impl Clone for Client where + S: Service< + (Request, C), + Response=Response> + Clone + Sync + Send + 'static, + S::Future: Send + 'static, + S::Error: Into + fmt::Display, + C: Clone + Send + Sync + 'static +{ + fn clone(&self) -> Self { + Self { + client_service: self.client_service.clone(), + base_path: self.base_path.clone(), + marker: PhantomData, + } + } +} + +impl Client, C>, C> where + Connector: hyper::client::connect::Connect + Clone + Send + Sync + 'static, + C: Clone + Send + Sync + 'static, +{ + /// Create a client with a custom implementation of hyper::client::Connect. + /// + /// Intended for use with custom implementations of connect for e.g. protocol logging + /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection, + /// this function should be used in conjunction with `swagger::Connector::builder()`. + /// + /// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https` + /// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer. + /// + /// # Arguments + /// + /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com" + /// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")` + /// * `connector` - Implementation of `hyper::client::Connect` to use for the client + pub fn try_new_with_connector( + base_path: &str, + protocol: Option<&'static str>, + connector: Connector, + ) -> Result + { + let client_service = hyper::client::Client::builder().build(connector); + let client_service = DropContextService::new(client_service); + + Ok(Self { + client_service, + base_path: into_base_path(base_path, protocol)?, + marker: PhantomData, + }) + } +} + +#[derive(Debug, Clone)] +pub enum HyperClient { + Http(hyper::client::Client), + Https(hyper::client::Client), +} + +impl Service> for HyperClient { + type Response = Response; + type Error = hyper::Error; + type Future = hyper::client::ResponseFuture; + + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + match self { + HyperClient::Http(client) => client.poll_ready(cx), + HyperClient::Https(client) => client.poll_ready(cx), + } + } + + fn call(&mut self, req: Request) -> Self::Future { + match self { + HyperClient::Http(client) => client.call(req), + HyperClient::Https(client) => client.call(req) + } + } +} + +impl Client, C> where + C: Clone + Send + Sync + 'static, +{ + /// Create an HTTP client. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com" + pub fn try_new( + base_path: &str, + ) -> Result { + let uri = Uri::from_str(base_path)?; + + let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?; + let scheme = scheme.to_ascii_lowercase(); + + let connector = Connector::builder(); + + let client_service = match scheme.as_str() { + "http" => { + HyperClient::Http(hyper::client::Client::builder().build(connector.build())) + }, + "https" => { + let connector = connector.https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper::client::Client::builder().build(connector)) + }, + _ => { + return Err(ClientInitError::InvalidScheme); + } + }; + + let client_service = DropContextService::new(client_service); + + Ok(Self { + client_service, + base_path: into_base_path(base_path, None)?, + marker: PhantomData, + }) + } +} + +impl Client, C>, C> where + C: Clone + Send + Sync + 'static +{ + /// Create an HTTP client. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com" + pub fn try_new_http( + base_path: &str, + ) -> Result { + let http_connector = Connector::builder().build(); + + Self::try_new_with_connector(base_path, Some("http"), http_connector) + } +} + +#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +type HttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HttpsConnector = hyper_openssl::HttpsConnector; + +impl Client, C>, C> where + C: Clone + Send + Sync + 'static +{ + /// Create a client with a TLS connection to the server + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com" + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using a pinned certificate + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com" + /// * `ca_certificate` - Path to CA certificate used to authenticate the server + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + pub fn try_new_https_pinned( + base_path: &str, + ca_certificate: CA, + ) -> Result + where + CA: AsRef, + { + let https_connector = Connector::builder() + .https() + .pin_server_certificate(ca_certificate) + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a mutually authenticated TLS connection to the server. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com" + /// * `ca_certificate` - Path to CA certificate used to authenticate the server + /// * `client_key` - Path to the client private key + /// * `client_certificate` - Path to the client's public certificate associated with the private key + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + pub fn try_new_https_mutual( + base_path: &str, + ca_certificate: CA, + client_key: K, + client_certificate: D, + ) -> Result + where + CA: AsRef, + K: AsRef, + D: AsRef, + { + let https_connector = Connector::builder() + .https() + .pin_server_certificate(ca_certificate) + .client_authentication(client_key, client_certificate) + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } +} + +impl Client where + S: Service< + (Request, C), + Response=Response> + Clone + Sync + Send + 'static, + S::Future: Send + 'static, + S::Error: Into + fmt::Display, + C: Clone + Send + Sync + 'static +{ + /// Constructor for creating a `Client` by passing in a pre-made `hyper::service::Service` / + /// `tower::Service` + /// + /// This allows adding custom wrappers around the underlying transport, for example for logging. + pub fn try_new_with_client_service( + client_service: S, + base_path: &str, + ) -> Result + { + Ok(Self { + client_service, + base_path: into_base_path(base_path, None)?, + marker: PhantomData, + }) + } +} + +/// Error type failing to create a Client +#[derive(Debug)] +pub enum ClientInitError { + /// Invalid URL Scheme + InvalidScheme, + + /// Invalid URI + InvalidUri(hyper::http::uri::InvalidUri), + + /// Missing Hostname + MissingHost, + + /// SSL Connection Error + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + SslError(native_tls::Error), + + /// SSL Connection Error + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + SslError(openssl::error::ErrorStack), +} + +impl From for ClientInitError { + fn from(err: hyper::http::uri::InvalidUri) -> ClientInitError { + ClientInitError::InvalidUri(err) + } +} + +impl fmt::Display for ClientInitError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s: &dyn fmt::Debug = self; + s.fmt(f) + } +} + +impl Error for ClientInitError { + fn description(&self) -> &str { + "Failed to produce a hyper client." + } +} + +#[async_trait] +impl Api for Client where + S: Service< + (Request, C), + Response=Response> + Clone + Sync + Send + 'static, + S::Future: Send + 'static, + S::Error: Into + fmt::Display, + C: Has + Clone + Send + Sync + 'static, +{ + fn poll_ready(&self, cx: &mut Context) -> Poll> { + match self.client_service.clone().poll_ready(cx) { + Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())), + Poll::Ready(Ok(o)) => Poll::Ready(Ok(o)), + Poll::Pending => Poll::Pending, + } + } + + async fn answers_post( + &self, + param_answer_input: models::AnswerInput, + context: &C) -> Result + { + let mut client_service = self.client_service.clone(); + let mut uri = format!( + "{}/answers", + self.base_path + ); + + // Query parameters + let query_string = { + let mut query_string = form_urlencoded::Serializer::new("".to_owned()); + query_string.finish() + }; + if !query_string.is_empty() { + uri += "?"; + uri += &query_string; + } + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))), + }; + + let mut request = match Request::builder() + .method("POST") + .uri(uri) + .body(Body::empty()) { + Ok(req) => req, + Err(e) => return Err(ApiError(format!("Unable to create request: {}", e))) + }; + + // Body parameter + let body = serde_json::to_string(¶m_answer_input).expect("impossible to fail to serialize"); + *request.body_mut() = Body::from(body); + + let header = "application/json"; + request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) { + Ok(h) => h, + Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e))) + }); + let header = HeaderValue::from_str(Has::::get(context).0.as_str()); + request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { + Ok(h) => h, + Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e))) + }); + + let response = client_service.call((request, context.clone())) + .map_err(|e| ApiError(format!("No response received: {}", e))).await?; + + match response.status().as_u16() { + 201 => { + let body = response.into_body(); + let body = body + .into_raw() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?; + let body = str::from_utf8(&body) + .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::(body).map_err(|e| { + ApiError(format!("Response body did not match the schema: {}", e)) + })?; + Ok(AnswersPostResponse::SuccessfullyCreatedAnswer + (body) + ) + } + code => { + let headers = response.headers().clone(); + let body = response.into_body() + .take(100) + .into_raw().await; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", + code, + headers, + match body { + Ok(body) => match String::from_utf8(body) { + Ok(body) => body, + Err(e) => format!("", e), + }, + Err(e) => format!("", e), + } + ))) + } + } + } + + async fn games_id_get( + &self, + param_id: uuid::Uuid, + context: &C) -> Result + { + let mut client_service = self.client_service.clone(); + let mut uri = format!( + "{}/games/{id}", + self.base_path + ,id=utf8_percent_encode(¶m_id.to_string(), ID_ENCODE_SET) + ); + + // Query parameters + let query_string = { + let mut query_string = form_urlencoded::Serializer::new("".to_owned()); + query_string.finish() + }; + if !query_string.is_empty() { + uri += "?"; + uri += &query_string; + } + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))), + }; + + let mut request = match Request::builder() + .method("GET") + .uri(uri) + .body(Body::empty()) { + Ok(req) => req, + Err(e) => return Err(ApiError(format!("Unable to create request: {}", e))) + }; + + let header = HeaderValue::from_str(Has::::get(context).0.as_str()); + request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { + Ok(h) => h, + Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e))) + }); + + let response = client_service.call((request, context.clone())) + .map_err(|e| ApiError(format!("No response received: {}", e))).await?; + + match response.status().as_u16() { + 200 => { + let body = response.into_body(); + let body = body + .into_raw() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?; + let body = str::from_utf8(&body) + .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::(body).map_err(|e| { + ApiError(format!("Response body did not match the schema: {}", e)) + })?; + Ok(GamesIdGetResponse::SuccessfullyFetchedGame + (body) + ) + } + code => { + let headers = response.headers().clone(); + let body = response.into_body() + .take(100) + .into_raw().await; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", + code, + headers, + match body { + Ok(body) => match String::from_utf8(body) { + Ok(body) => body, + Err(e) => format!("", e), + }, + Err(e) => format!("", e), + } + ))) + } + } + } + + async fn games_post( + &self, + param_game_input: models::GameInput, + param_concept: Option, + param_incarnation_filter_left_square_bracket_concept_right_square_bracket: Option, + context: &C) -> Result + { + let mut client_service = self.client_service.clone(); + let mut uri = format!( + "{}/games", + self.base_path + ); + + // Query parameters + let query_string = { + let mut query_string = form_urlencoded::Serializer::new("".to_owned()); + if let Some(param_concept) = param_concept { + query_string.append_pair("concept", + ¶m_concept); + } + if let Some(param_incarnation_filter_left_square_bracket_concept_right_square_bracket) = param_incarnation_filter_left_square_bracket_concept_right_square_bracket { + query_string.append_pair("incarnation_filter[concept]", + ¶m_incarnation_filter_left_square_bracket_concept_right_square_bracket); + } + query_string.finish() + }; + if !query_string.is_empty() { + uri += "?"; + uri += &query_string; + } + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))), + }; + + let mut request = match Request::builder() + .method("POST") + .uri(uri) + .body(Body::empty()) { + Ok(req) => req, + Err(e) => return Err(ApiError(format!("Unable to create request: {}", e))) + }; + + let body = serde_json::to_string(¶m_game_input).expect("impossible to fail to serialize"); + + *request.body_mut() = Body::from(body); + + let header = "application/json"; + request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) { + Ok(h) => h, + Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e))) + }); + + let header = HeaderValue::from_str(Has::::get(context).0.as_str()); + request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { + Ok(h) => h, + Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e))) + }); + + let response = client_service.call((request, context.clone())) + .map_err(|e| ApiError(format!("No response received: {}", e))).await?; + + match response.status().as_u16() { + 201 => { + let body = response.into_body(); + let body = body + .into_raw() + .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?; + let body = str::from_utf8(&body) + .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::(body).map_err(|e| { + ApiError(format!("Response body did not match the schema: {}", e)) + })?; + Ok(GamesPostResponse::SuccessfullyCreatedGame + (body) + ) + } + code => { + let headers = response.headers().clone(); + let body = response.into_body() + .take(100) + .into_raw().await; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", + code, + headers, + match body { + Ok(body) => match String::from_utf8(body) { + Ok(body) => body, + Err(e) => format!("", e), + }, + Err(e) => format!("", e), + } + ))) + } + } + } + +} diff --git a/waydowntown/waydowntown_rust_server/src/context.rs b/waydowntown/waydowntown_rust_server/src/context.rs new file mode 100644 index 00000000..ee8e1185 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/src/context.rs @@ -0,0 +1,114 @@ +use futures::future::BoxFuture; +use hyper::header::HeaderName; +use hyper::{Error, Request, Response, StatusCode, service::Service}; +use url::form_urlencoded; +use std::default::Default; +use std::io; +use std::marker::PhantomData; +use std::task::{Poll, Context}; +use swagger::auth::{AuthData, Authorization, Bearer, Scopes}; +use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString}; +use crate::{Api, AuthenticationApi}; +use log::error; + +pub struct MakeAddContext { + inner: T, + marker: PhantomData
, +} + +impl MakeAddContext +where + A: Default + Push, + B: Push, Result = C>, + C: Push, Result = D>, +{ + pub fn new(inner: T) -> MakeAddContext { + MakeAddContext { + inner, + marker: PhantomData, + } + } +} + +// Make a service that adds context. +impl Service for + MakeAddContext +where + Target: Send, + A: Default + Push + Send, + B: Push, Result = C>, + C: Push, Result = D>, + D: Send + 'static, + T: Service + Send, + T::Future: Send + 'static +{ + type Error = T::Error; + type Response = AddContext; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, target: Target) -> Self::Future { + let service = self.inner.call(target); + + Box::pin(async move { + Ok(AddContext::new(service.await?)) + }) + } +} + +/// Middleware to add context data from the request +pub struct AddContext +where + A: Default + Push, + B: Push, Result = C>, + C: Push, Result = D> +{ + inner: T, + marker: PhantomData, +} + +impl AddContext +where + A: Default + Push, + B: Push, Result = C>, + C: Push, Result = D>, +{ + pub fn new(inner: T) -> Self { + AddContext { + inner, + marker: PhantomData, + } + } +} + +impl Service> for AddContext + where + A: Default + Push, + B: Push, Result=C>, + C: Push, Result=D>, + D: Send + 'static, + T: Service<(Request, D)> + AuthenticationApi +{ + type Error = T::Error; + type Future = T::Future; + type Response = T::Response; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + + fn call(&mut self, request: Request) -> Self::Future { + let context = A::default().push(XSpanIdString::get_or_generate(&request)); + let headers = request.headers(); + + + let context = context.push(None::); + let context = context.push(None::); + + self.inner.call((request, context)) + } +} diff --git a/waydowntown/waydowntown_rust_server/src/header.rs b/waydowntown/waydowntown_rust_server/src/header.rs new file mode 100644 index 00000000..5bc6ebe9 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/src/header.rs @@ -0,0 +1,180 @@ +use chrono::{DateTime, Utc}; +use hyper::header::HeaderValue; +use std::convert::TryFrom; +use std::fmt; +use std::ops::Deref; + +/// A struct to allow homogeneous conversion into a HeaderValue. We can't +/// implement the From/Into trait on HeaderValue because we don't own +/// either of the types. +#[derive(Debug, Clone)] +pub(crate) struct IntoHeaderValue(pub T); + +// Generic implementations + +impl Deref for IntoHeaderValue { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +// Derive for each TryFrom in hyper::header::HeaderValue + +macro_rules! ihv_generate { + ($t:ident) => { + impl TryFrom for IntoHeaderValue<$t> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse::<$t>() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse {} as a string: {}", + stringify!($t), e)), + }, + Err(e) => Err(format!("Unable to parse header {:?} as a string - {}", + hdr_value, e)), + } + } + } + + impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue<$t>) -> Result { + Ok(hdr_value.0.into()) + } + } + }; +} + +ihv_generate!(u64); +ihv_generate!(i64); +ihv_generate!(i16); +ihv_generate!(u16); +ihv_generate!(u32); +ihv_generate!(usize); +ihv_generate!(isize); +ihv_generate!(i32); + +// Custom derivations + +// Vec + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue( + hdr_value + .split(',') + .filter_map(|x| match x.trim() { + "" => None, + y => Some(y.to_string()), + }) + .collect())), + Err(e) => Err(format!("Unable to parse header: {:?} as a string - {}", + hdr_value, e)), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(&hdr_value.0.join(", ")) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} into a header - {}", + hdr_value, e)) + } + } +} + +// String + +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value.to_string())), + Err(e) => Err(format!("Unable to convert header {:?} to {}", + hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} from a header {}", + hdr_value, e)) + } + } +} + +// bool +impl TryFrom for IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match hdr_value.parse() { + Ok(hdr_value) => Ok(IntoHeaderValue(hdr_value)), + Err(e) => Err(format!("Unable to parse bool from {} - {}", + hdr_value, e)), + }, + Err(e) => Err(format!("Unable to convert {:?} from a header {}", + hdr_value, e)), + } + } +} + +impl TryFrom> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue) -> Result { + match HeaderValue::from_str(&hdr_value.0.to_string()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert: {:?} into a header: {}", + hdr_value, e)) + } + } +} + +// DateTime + +impl TryFrom for IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_value: HeaderValue) -> Result { + match hdr_value.to_str() { + Ok(hdr_value) => match DateTime::parse_from_rfc3339(hdr_value) { + Ok(date) => Ok(IntoHeaderValue(date.with_timezone(&Utc))), + Err(e) => Err(format!("Unable to parse: {} as date - {}", + hdr_value, e)), + }, + Err(e) => Err(format!("Unable to convert header {:?} to string {}", + hdr_value, e)), + } + } +} + +impl TryFrom>> for HeaderValue { + type Error = String; + + fn try_from(hdr_value: IntoHeaderValue>) -> Result { + match HeaderValue::from_str(hdr_value.0.to_rfc3339().as_str()) { + Ok(hdr_value) => Ok(hdr_value), + Err(e) => Err(format!("Unable to convert {:?} to a header: {}", + hdr_value, e)), + } + } +} diff --git a/waydowntown/waydowntown_rust_server/src/lib.rs b/waydowntown/waydowntown_rust_server/src/lib.rs new file mode 100644 index 00000000..2555165f --- /dev/null +++ b/waydowntown/waydowntown_rust_server/src/lib.rs @@ -0,0 +1,183 @@ +#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, unused_attributes, non_camel_case_types)] +#![allow(clippy::derive_partial_eq_without_eq, clippy::disallowed_names)] + +use async_trait::async_trait; +use futures::Stream; +use std::error::Error; +use std::collections::BTreeSet; +use std::task::{Poll, Context}; +use swagger::{ApiError, ContextWrapper}; +use serde::{Serialize, Deserialize}; +use crate::server::Authorization; + + +type ServiceError = Box; + +pub const BASE_PATH: &str = ""; +pub const API_VERSION: &str = "1.0.0"; + +mod auth; +pub use auth::{AuthenticationApi, Claims}; + + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum AnswersPostResponse { + /// Successfully created answer + SuccessfullyCreatedAnswer + (models::Answer) +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum GamesIdGetResponse { + /// Successfully fetched game + SuccessfullyFetchedGame + (models::GamesIdGet200Response) +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum GamesPostResponse { + /// Successfully created game + SuccessfullyCreatedGame + (models::GamesPost201Response) +} + +/// API +#[async_trait] +#[allow(clippy::too_many_arguments, clippy::ptr_arg)] +pub trait Api { + fn poll_ready(&self, _cx: &mut Context) -> Poll>> { + Poll::Ready(Ok(())) + } + + /// Create a new answer + async fn answers_post( + &self, + answer_input: models::AnswerInput, + context: &C) -> Result; + + /// Fetch a game + async fn games_id_get( + &self, + id: uuid::Uuid, + context: &C) -> Result; + + /// Create a new game + async fn games_post( + &self, + game_input: models::GameInput, + concept: Option, + incarnation_filter_left_square_bracket_concept_right_square_bracket: Option, + context: &C) -> Result; + +} + +/// API where `Context` isn't passed on every API call +#[async_trait] +#[allow(clippy::too_many_arguments, clippy::ptr_arg)] +pub trait ApiNoContext { + + fn poll_ready(&self, _cx: &mut Context) -> Poll>>; + + fn context(&self) -> &C; + + /// Create a new answer + async fn answers_post( + &self, + answer_input: models::AnswerInput, + ) -> Result; + + /// Fetch a game + async fn games_id_get( + &self, + id: uuid::Uuid, + ) -> Result; + + /// Create a new game + async fn games_post( + &self, + game_input: models::GameInput, + concept: Option, + incarnation_filter_left_square_bracket_concept_right_square_bracket: Option, + ) -> Result; + +} + +/// Trait to extend an API to make it easy to bind it to a context. +pub trait ContextWrapperExt where Self: Sized +{ + /// Binds this API to a context. + fn with_context(self, context: C) -> ContextWrapper; +} + +impl + Send + Sync, C: Clone + Send + Sync> ContextWrapperExt for T { + fn with_context(self: T, context: C) -> ContextWrapper { + ContextWrapper::::new(self, context) + } +} + +#[async_trait] +impl + Send + Sync, C: Clone + Send + Sync> ApiNoContext for ContextWrapper { + fn poll_ready(&self, cx: &mut Context) -> Poll> { + self.api().poll_ready(cx) + } + + fn context(&self) -> &C { + ContextWrapper::context(self) + } + + /// Create a new answer + async fn answers_post( + &self, + answer_input: models::AnswerInput, + ) -> Result + { + let context = self.context().clone(); + self.api().answers_post(answer_input, &context).await + } + + /// Fetch a game + async fn games_id_get( + &self, + id: uuid::Uuid, + ) -> Result + { + let context = self.context().clone(); + self.api().games_id_get(id, &context).await + } + + /// Create a new game + async fn games_post( + &self, + game_input: models::GameInput, + concept: Option, + incarnation_filter_left_square_bracket_concept_right_square_bracket: Option, + ) -> Result + { + let context = self.context().clone(); + self.api().games_post(game_input, concept, incarnation_filter_left_square_bracket_concept_right_square_bracket, &context).await + } + +} + + +#[cfg(feature = "client")] +pub mod client; + +// Re-export Client as a top-level name +#[cfg(feature = "client")] +pub use client::Client; + +#[cfg(feature = "server")] +pub mod server; + +// Re-export router() as a top-level name +#[cfg(feature = "server")] +pub use self::server::Service; + +#[cfg(feature = "server")] +pub mod context; + +pub mod models; + +#[cfg(any(feature = "client", feature = "server"))] +pub(crate) mod header; diff --git a/waydowntown/waydowntown_rust_server/src/models.rs b/waydowntown/waydowntown_rust_server/src/models.rs new file mode 100644 index 00000000..ff448c7c --- /dev/null +++ b/waydowntown/waydowntown_rust_server/src/models.rs @@ -0,0 +1,3260 @@ +#![allow(unused_qualifications)] + +use std::str::FromStr; +use validator::Validate; + +#[cfg(any(feature = "client", feature = "server"))] +use crate::header; +use crate::models; + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Answer { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if = "Option::is_none")] + pub relationships: Option, +} + +impl Answer { + #[allow(clippy::new_without_default)] + pub fn new() -> Answer { + Answer { + id: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the Answer value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Answer { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Answer value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Answer { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Answer".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Answer".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Answer { + id: intermediate_rep.id.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> for hyper::header::HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Answer - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Answer - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerAttributes { + #[serde(rename = "answer")] + #[serde(skip_serializing_if = "Option::is_none")] + pub answer: Option, + + #[serde(rename = "correct")] + #[serde(skip_serializing_if = "Option::is_none")] + pub correct: Option, +} + +impl AnswerAttributes { + #[allow(clippy::new_without_default)] + pub fn new() -> AnswerAttributes { + AnswerAttributes { + answer: None, + correct: None, + } + } +} + +/// Converts the AnswerAttributes value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AnswerAttributes { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.answer + .as_ref() + .map(|answer| ["answer".to_string(), answer.to_string()].join(",")), + self.correct + .as_ref() + .map(|correct| ["correct".to_string(), correct.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerAttributes value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerAttributes { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub answer: Vec, + pub correct: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing AnswerAttributes".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "answer" => intermediate_rep.answer.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "correct" => intermediate_rep.correct.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing AnswerAttributes".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerAttributes { + answer: intermediate_rep.answer.into_iter().next(), + correct: intermediate_rep.correct.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AnswerAttributes - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AnswerAttributes - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerInput { + #[serde(rename = "answer")] + #[serde(skip_serializing_if = "Option::is_none")] + pub answer: Option, + + #[serde(rename = "game")] + #[serde(skip_serializing_if = "Option::is_none")] + pub game: Option, +} + +impl AnswerInput { + #[allow(clippy::new_without_default)] + pub fn new() -> AnswerInput { + AnswerInput { + answer: None, + game: None, + } + } +} + +/// Converts the AnswerInput value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AnswerInput { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.answer + .as_ref() + .map(|answer| ["answer".to_string(), answer.to_string()].join(",")), + // Skipping game in query parameter serialization + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerInput value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerInput { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub answer: Vec, + pub game: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing AnswerInput".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "answer" => intermediate_rep.answer.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "game" => intermediate_rep.game.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing AnswerInput".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerInput { + answer: intermediate_rep.answer.into_iter().next(), + game: intermediate_rep.game.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> for hyper::header::HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AnswerInput - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AnswerInput - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AnswerRelationships { + #[serde(rename = "game")] + #[serde(skip_serializing_if = "Option::is_none")] + pub game: Option, +} + +impl AnswerRelationships { + #[allow(clippy::new_without_default)] + pub fn new() -> AnswerRelationships { + AnswerRelationships { game: None } + } +} + +/// Converts the AnswerRelationships value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for AnswerRelationships { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping game in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AnswerRelationships value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for AnswerRelationships { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub game: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing AnswerRelationships".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "game" => intermediate_rep.game.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing AnswerRelationships".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(AnswerRelationships { + game: intermediate_rep.game.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for AnswerRelationships - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into AnswerRelationships - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Game { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if = "Option::is_none")] + pub relationships: Option, +} + +impl Game { + #[allow(clippy::new_without_default)] + pub fn new() -> Game { + Game { + id: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the Game value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Game { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Game value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Game { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err("Missing value while parsing Game".to_string()) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Game".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Game { + id: intermediate_rep.id.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> for hyper::header::HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Game - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Game - {}", + value, err + )), + }, + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameAttributes { + #[serde(rename = "complete")] + #[serde(skip_serializing_if = "Option::is_none")] + pub complete: Option, +} + +impl GameAttributes { + #[allow(clippy::new_without_default)] + pub fn new() -> GameAttributes { + GameAttributes { complete: None } + } +} + +/// Converts the GameAttributes value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GameAttributes { + fn to_string(&self) -> String { + let params: Vec> = vec![self + .complete + .as_ref() + .map(|complete| ["complete".to_string(), complete.to_string()].join(","))]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameAttributes value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameAttributes { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub complete: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GameAttributes".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "complete" => intermediate_rep.complete.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GameAttributes".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameAttributes { + complete: intermediate_rep.complete.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> for hyper::header::HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for GameAttributes - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into GameAttributes - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameInput { + #[serde(rename = "incarnation_id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub incarnation_id: Option, +} + +impl GameInput { + #[allow(clippy::new_without_default)] + pub fn new() -> GameInput { + GameInput { + incarnation_id: None, + } + } +} + +/// Converts the GameInput value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GameInput { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping incarnation_id in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameInput value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameInput { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub incarnation_id: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GameInput".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "incarnation_id" => intermediate_rep.incarnation_id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GameInput".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameInput { + incarnation_id: intermediate_rep.incarnation_id.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> for hyper::header::HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for GameInput - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into GameInput - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationships { + #[serde(rename = "incarnation")] + #[serde(skip_serializing_if = "Option::is_none")] + pub incarnation: Option, + + #[serde(rename = "winner_answer")] + #[serde(skip_serializing_if = "Option::is_none")] + pub winner_answer: Option, +} + +impl GameRelationships { + #[allow(clippy::new_without_default)] + pub fn new() -> GameRelationships { + GameRelationships { + incarnation: None, + winner_answer: None, + } + } +} + +/// Converts the GameRelationships value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GameRelationships { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping incarnation in query parameter serialization + + // Skipping winner_answer in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationships value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationships { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub incarnation: Vec, + pub winner_answer: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GameRelationships".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "incarnation" => intermediate_rep.incarnation.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "winner_answer" => intermediate_rep.winner_answer.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GameRelationships".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationships { + incarnation: intermediate_rep.incarnation.into_iter().next(), + winner_answer: intermediate_rep.winner_answer.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for GameRelationships - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into GameRelationships - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationshipsIncarnation { + #[serde(rename = "data")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, +} + +impl GameRelationshipsIncarnation { + #[allow(clippy::new_without_default)] + pub fn new() -> GameRelationshipsIncarnation { + GameRelationshipsIncarnation { data: None } + } +} + +/// Converts the GameRelationshipsIncarnation value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GameRelationshipsIncarnation { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationshipsIncarnation value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationshipsIncarnation { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GameRelationshipsIncarnation".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push( + ::from_str( + val, + ) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GameRelationshipsIncarnation".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationshipsIncarnation { + data: intermediate_rep.data.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for GameRelationshipsIncarnation - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationshipsIncarnation - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationshipsIncarnationData { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, +} + +impl GameRelationshipsIncarnationData { + #[allow(clippy::new_without_default)] + pub fn new() -> GameRelationshipsIncarnationData { + GameRelationshipsIncarnationData { id: None } + } +} + +/// Converts the GameRelationshipsIncarnationData value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GameRelationshipsIncarnationData { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationshipsIncarnationData value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationshipsIncarnationData { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GameRelationshipsIncarnationData".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GameRelationshipsIncarnationData" + .to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationshipsIncarnationData { + id: intermediate_rep.id.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameRelationshipsIncarnationData - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationshipsIncarnationData - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationshipsWinnerAnswer { + #[serde(rename = "data")] + #[serde(deserialize_with = "swagger::nullable_format::deserialize_optional_nullable")] + #[serde(default = "swagger::nullable_format::default_optional_nullable")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option>, +} + +impl GameRelationshipsWinnerAnswer { + #[allow(clippy::new_without_default)] + pub fn new() -> GameRelationshipsWinnerAnswer { + GameRelationshipsWinnerAnswer { data: None } + } +} + +/// Converts the GameRelationshipsWinnerAnswer value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GameRelationshipsWinnerAnswer { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationshipsWinnerAnswer value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationshipsWinnerAnswer { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GameRelationshipsWinnerAnswer".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + "data" => return std::result::Result::Err("Parsing a nullable type in this style is not supported in GameRelationshipsWinnerAnswer".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing GameRelationshipsWinnerAnswer".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationshipsWinnerAnswer { + data: std::result::Result::Err( + "Nullable types not supported in GameRelationshipsWinnerAnswer".to_string(), + )?, + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for GameRelationshipsWinnerAnswer - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationshipsWinnerAnswer - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GameRelationshipsWinnerAnswerData { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, +} + +impl GameRelationshipsWinnerAnswerData { + #[allow(clippy::new_without_default)] + pub fn new() -> GameRelationshipsWinnerAnswerData { + GameRelationshipsWinnerAnswerData { id: None } + } +} + +/// Converts the GameRelationshipsWinnerAnswerData value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GameRelationshipsWinnerAnswerData { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GameRelationshipsWinnerAnswerData value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GameRelationshipsWinnerAnswerData { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GameRelationshipsWinnerAnswerData".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GameRelationshipsWinnerAnswerData" + .to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GameRelationshipsWinnerAnswerData { + id: intermediate_rep.id.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GameRelationshipsWinnerAnswerData - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GameRelationshipsWinnerAnswerData - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GamesIdGet200Response { + #[serde(rename = "data")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + + #[serde(rename = "included")] + #[serde(skip_serializing_if = "Option::is_none")] + pub included: Option>, +} + +impl GamesIdGet200Response { + #[allow(clippy::new_without_default)] + pub fn new() -> GamesIdGet200Response { + GamesIdGet200Response { + data: None, + included: None, + } + } +} + +/// Converts the GamesIdGet200Response value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GamesIdGet200Response { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + // Skipping included in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GamesIdGet200Response value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GamesIdGet200Response { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + pub included: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GamesIdGet200Response".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push(::from_str(val).map_err(|x| x.to_string())?), + "included" => return std::result::Result::Err("Parsing a container in this style is not supported in GamesIdGet200Response".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing GamesIdGet200Response".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GamesIdGet200Response { + data: intermediate_rep.data.into_iter().next(), + included: intermediate_rep.included.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for GamesIdGet200Response - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into GamesIdGet200Response - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GamesIdGet200ResponseIncludedInner { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if = "Option::is_none")] + pub relationships: Option, +} + +impl GamesIdGet200ResponseIncludedInner { + #[allow(clippy::new_without_default)] + pub fn new() -> GamesIdGet200ResponseIncludedInner { + GamesIdGet200ResponseIncludedInner { + id: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the GamesIdGet200ResponseIncludedInner value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GamesIdGet200ResponseIncludedInner { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GamesIdGet200ResponseIncludedInner value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GamesIdGet200ResponseIncludedInner { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GamesIdGet200ResponseIncludedInner" + .to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GamesIdGet200ResponseIncludedInner" + .to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GamesIdGet200ResponseIncludedInner { + id: intermediate_rep.id.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GamesIdGet200ResponseIncludedInner - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GamesIdGet200ResponseIncludedInner - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GamesPost201Response { + #[serde(rename = "data")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + + #[serde(rename = "included")] + #[serde(skip_serializing_if = "Option::is_none")] + pub included: Option>, +} + +impl GamesPost201Response { + #[allow(clippy::new_without_default)] + pub fn new() -> GamesPost201Response { + GamesPost201Response { + data: None, + included: None, + } + } +} + +/// Converts the GamesPost201Response value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GamesPost201Response { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping data in query parameter serialization + + // Skipping included in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GamesPost201Response value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GamesPost201Response { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub data: Vec, + pub included: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GamesPost201Response".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "data" => intermediate_rep.data.push(::from_str(val).map_err(|x| x.to_string())?), + "included" => return std::result::Result::Err("Parsing a container in this style is not supported in GamesPost201Response".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing GamesPost201Response".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GamesPost201Response { + data: intermediate_rep.data.into_iter().next(), + included: intermediate_rep.included.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for GamesPost201Response - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into GamesPost201Response - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct GamesPost201ResponseIncludedInner { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if = "Option::is_none")] + pub relationships: Option, +} + +impl GamesPost201ResponseIncludedInner { + #[allow(clippy::new_without_default)] + pub fn new() -> GamesPost201ResponseIncludedInner { + GamesPost201ResponseIncludedInner { + id: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the GamesPost201ResponseIncludedInner value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for GamesPost201ResponseIncludedInner { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a GamesPost201ResponseIncludedInner value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for GamesPost201ResponseIncludedInner { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing GamesPost201ResponseIncludedInner".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing GamesPost201ResponseIncludedInner" + .to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(GamesPost201ResponseIncludedInner { + id: intermediate_rep.id.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for GamesPost201ResponseIncludedInner - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into GamesPost201ResponseIncludedInner - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Incarnation { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if = "Option::is_none")] + pub relationships: Option, +} + +impl Incarnation { + #[allow(clippy::new_without_default)] + pub fn new() -> Incarnation { + Incarnation { + id: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the Incarnation value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Incarnation { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Incarnation value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Incarnation { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Incarnation".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Incarnation".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Incarnation { + id: intermediate_rep.id.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> for hyper::header::HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Incarnation - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Incarnation - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct IncarnationAttributes { + #[serde(rename = "concept")] + #[serde(skip_serializing_if = "Option::is_none")] + pub concept: Option, + + #[serde(rename = "mask")] + #[serde(skip_serializing_if = "Option::is_none")] + pub mask: Option, + + #[serde(rename = "answer")] + #[serde(skip_serializing_if = "Option::is_none")] + pub answer: Option, + + #[serde(rename = "answers")] + #[serde(skip_serializing_if = "Option::is_none")] + pub answers: Option>, +} + +impl IncarnationAttributes { + #[allow(clippy::new_without_default)] + pub fn new() -> IncarnationAttributes { + IncarnationAttributes { + concept: None, + mask: None, + answer: None, + answers: None, + } + } +} + +/// Converts the IncarnationAttributes value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for IncarnationAttributes { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.concept + .as_ref() + .map(|concept| ["concept".to_string(), concept.to_string()].join(",")), + self.mask + .as_ref() + .map(|mask| ["mask".to_string(), mask.to_string()].join(",")), + self.answer + .as_ref() + .map(|answer| ["answer".to_string(), answer.to_string()].join(",")), + self.answers.as_ref().map(|answers| { + [ + "answers".to_string(), + answers + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + ] + .join(",") + }), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a IncarnationAttributes value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for IncarnationAttributes { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub concept: Vec, + pub mask: Vec, + pub answer: Vec, + pub answers: Vec>, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing IncarnationAttributes".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "concept" => intermediate_rep.concept.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "mask" => intermediate_rep.mask.push(::from_str(val).map_err(|x| x.to_string())?), + #[allow(clippy::redundant_clone)] + "answer" => intermediate_rep.answer.push(::from_str(val).map_err(|x| x.to_string())?), + "answers" => return std::result::Result::Err("Parsing a container in this style is not supported in IncarnationAttributes".to_string()), + _ => return std::result::Result::Err("Unexpected key while parsing IncarnationAttributes".to_string()) + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(IncarnationAttributes { + concept: intermediate_rep.concept.into_iter().next(), + mask: intermediate_rep.mask.into_iter().next(), + answer: intermediate_rep.answer.into_iter().next(), + answers: intermediate_rep.answers.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for IncarnationAttributes - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into IncarnationAttributes - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct IncarnationRelationships { + #[serde(rename = "region")] + #[serde(skip_serializing_if = "Option::is_none")] + pub region: Option, +} + +impl IncarnationRelationships { + #[allow(clippy::new_without_default)] + pub fn new() -> IncarnationRelationships { + IncarnationRelationships { region: None } + } +} + +/// Converts the IncarnationRelationships value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for IncarnationRelationships { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping region in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a IncarnationRelationships value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for IncarnationRelationships { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub region: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing IncarnationRelationships".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "region" => intermediate_rep.region.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing IncarnationRelationships".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(IncarnationRelationships { + region: intermediate_rep.region.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for IncarnationRelationships - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into IncarnationRelationships - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct IncarnationRelationshipsRegion { + #[serde(rename = "data")] + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, +} + +impl IncarnationRelationshipsRegion { + #[allow(clippy::new_without_default)] + pub fn new() -> IncarnationRelationshipsRegion { + IncarnationRelationshipsRegion { data: None } + } +} + +impl FromStr for IncarnationRelationshipsRegion { + type Err = String; + + fn from_str(s: &str) -> Result { + serde_json::from_str(s).map_err(|e| e.to_string()) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct IncarnationRelationshipsRegionData { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, +} + +impl IncarnationRelationshipsRegionData { + #[allow(clippy::new_without_default)] + pub fn new() -> IncarnationRelationshipsRegionData { + IncarnationRelationshipsRegionData { id: None } + } +} + +impl FromStr for IncarnationRelationshipsRegionData { + type Err = String; + + fn from_str(s: &str) -> Result { + serde_json::from_str(s).map_err(|e| e.to_string()) + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct Region { + #[serde(rename = "id")] + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + + #[serde(rename = "attributes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub attributes: Option, + + #[serde(rename = "relationships")] + #[serde(skip_serializing_if = "Option::is_none")] + pub relationships: Option, +} + +impl Region { + #[allow(clippy::new_without_default)] + pub fn new() -> Region { + Region { + id: None, + attributes: None, + relationships: None, + } + } +} + +/// Converts the Region value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for Region { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping id in query parameter serialization + + // Skipping attributes in query parameter serialization + + // Skipping relationships in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a Region value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for Region { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub id: Vec, + pub attributes: Vec, + pub relationships: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing Region".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "id" => intermediate_rep.id.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "attributes" => intermediate_rep.attributes.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "relationships" => intermediate_rep.relationships.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing Region".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(Region { + id: intermediate_rep.id.into_iter().next(), + attributes: intermediate_rep.attributes.into_iter().next(), + relationships: intermediate_rep.relationships.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> for hyper::header::HeaderValue { + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for Region - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into Region - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct RegionAttributes { + #[serde(rename = "name")] + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + #[serde(rename = "description")] + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, +} + +impl RegionAttributes { + #[allow(clippy::new_without_default)] + pub fn new() -> RegionAttributes { + RegionAttributes { + name: None, + description: None, + } + } +} + +/// Converts the RegionAttributes value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for RegionAttributes { + fn to_string(&self) -> String { + let params: Vec> = vec![ + self.name + .as_ref() + .map(|name| ["name".to_string(), name.to_string()].join(",")), + self.description + .as_ref() + .map(|description| ["description".to_string(), description.to_string()].join(",")), + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a RegionAttributes value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for RegionAttributes { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub name: Vec, + pub description: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing RegionAttributes".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "name" => intermediate_rep.name.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + #[allow(clippy::redundant_clone)] + "description" => intermediate_rep.description.push( + ::from_str(val).map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing RegionAttributes".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(RegionAttributes { + name: intermediate_rep.name.into_iter().next(), + description: intermediate_rep.description.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for RegionAttributes - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into RegionAttributes - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct RegionRelationships { + #[serde(rename = "parent")] + #[serde(skip_serializing_if = "Option::is_none")] + pub parent: Option, +} + +impl RegionRelationships { + #[allow(clippy::new_without_default)] + pub fn new() -> RegionRelationships { + RegionRelationships { parent: None } + } +} + +/// Converts the RegionRelationships value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl std::string::ToString for RegionRelationships { + fn to_string(&self) -> String { + let params: Vec> = vec![ + // Skipping parent in query parameter serialization + + ]; + + params.into_iter().flatten().collect::>().join(",") + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a RegionRelationships value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl std::str::FromStr for RegionRelationships { + type Err = String; + + fn from_str(s: &str) -> std::result::Result { + /// An intermediate representation of the struct to use for parsing. + #[derive(Default)] + #[allow(dead_code)] + struct IntermediateRep { + pub parent: Vec, + } + + let mut intermediate_rep = IntermediateRep::default(); + + // Parse into intermediate representation + let mut string_iter = s.split(','); + let mut key_result = string_iter.next(); + + while key_result.is_some() { + let val = match string_iter.next() { + Some(x) => x, + None => { + return std::result::Result::Err( + "Missing value while parsing RegionRelationships".to_string(), + ) + } + }; + + if let Some(key) = key_result { + #[allow(clippy::match_single_binding)] + match key { + #[allow(clippy::redundant_clone)] + "parent" => intermediate_rep.parent.push( + ::from_str(val) + .map_err(|x| x.to_string())?, + ), + _ => { + return std::result::Result::Err( + "Unexpected key while parsing RegionRelationships".to_string(), + ) + } + } + } + + // Get the next key + key_result = string_iter.next(); + } + + // Use the intermediate representation to return the struct + std::result::Result::Ok(RegionRelationships { + parent: intermediate_rep.parent.into_iter().next(), + }) + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> + for hyper::header::HeaderValue +{ + type Error = String; + + fn try_from( + hdr_value: header::IntoHeaderValue, + ) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Invalid header value for RegionRelationships - value: {} is invalid {}", + hdr_value, e + )), + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom + for header::IntoHeaderValue +{ + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(header::IntoHeaderValue(value)) + } + std::result::Result::Err(err) => std::result::Result::Err(format!( + "Unable to convert header value '{}' into RegionRelationships - {}", + value, err + )), + } + } + std::result::Result::Err(e) => std::result::Result::Err(format!( + "Unable to convert header: {:?} to string: {}", + hdr_value, e + )), + } + } +} diff --git a/waydowntown/waydowntown_rust_server/src/server/mod.rs b/waydowntown/waydowntown_rust_server/src/server/mod.rs new file mode 100644 index 00000000..b811fb53 --- /dev/null +++ b/waydowntown/waydowntown_rust_server/src/server/mod.rs @@ -0,0 +1,441 @@ +use futures::{future, future::BoxFuture, Stream, stream, future::FutureExt, stream::TryStreamExt}; +use hyper::{Request, Response, StatusCode, Body, HeaderMap}; +use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; +use log::warn; +#[allow(unused_imports)] +use std::convert::{TryFrom, TryInto}; +use std::error::Error; +use std::future::Future; +use std::marker::PhantomData; +use std::task::{Context, Poll}; +use swagger::{ApiError, BodyExt, Has, RequestParser, XSpanIdString}; +pub use swagger::auth::Authorization; +use swagger::auth::Scopes; +use url::form_urlencoded; + +#[allow(unused_imports)] +use crate::{models, header, AuthenticationApi}; + +pub use crate::context; + +type ServiceFuture = BoxFuture<'static, Result, crate::ServiceError>>; + +use crate::{Api, + AnswersPostResponse, + GamesIdGetResponse, + GamesPostResponse +}; + +mod server_auth; + +mod paths { + use lazy_static::lazy_static; + + lazy_static! { + pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(vec![ + r"^/answers$", + r"^/games$", + r"^/games/(?P[^/?#]*)$" + ]) + .expect("Unable to create global regex set"); + } + pub(crate) static ID_ANSWERS: usize = 0; + pub(crate) static ID_GAMES: usize = 1; + pub(crate) static ID_GAMES_ID: usize = 2; + lazy_static! { + pub static ref REGEX_GAMES_ID: regex::Regex = + #[allow(clippy::invalid_regex)] + regex::Regex::new(r"^/games/(?P[^/?#]*)$") + .expect("Unable to create regex for GAMES_ID"); + } +} + + +pub struct MakeService where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static +{ + api_impl: T, + marker: PhantomData, +} + +impl MakeService where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static +{ + pub fn new(api_impl: T) -> Self { + MakeService { + api_impl, + marker: PhantomData + } + } +} + + +impl hyper::service::Service for MakeService where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static +{ + type Response = Service; + type Error = crate::ServiceError; + type Future = future::Ready>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, target: Target) -> Self::Future { + future::ok(Service::new( + self.api_impl.clone(), + )) + } +} + +fn method_not_allowed() -> Result, crate::ServiceError> { + Ok( + Response::builder().status(StatusCode::METHOD_NOT_ALLOWED) + .body(Body::empty()) + .expect("Unable to create Method Not Allowed response") + ) +} + +pub struct Service where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static +{ + api_impl: T, + marker: PhantomData, +} + +impl Service where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static +{ + pub fn new(api_impl: T) -> Self { + Service { + api_impl, + marker: PhantomData + } + } +} + +impl Clone for Service where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static +{ + fn clone(&self) -> Self { + Service { + api_impl: self.api_impl.clone(), + marker: self.marker, + } + } +} + +impl hyper::service::Service<(Request, C)> for Service where + T: Api + Clone + Send + Sync + 'static, + C: Has + Send + Sync + 'static +{ + type Response = Response; + type Error = crate::ServiceError; + type Future = ServiceFuture; + + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.api_impl.poll_ready(cx) + } + + fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + + match method { + + // AnswersPost - POST /answers + hyper::Method::POST if path.matched(paths::ID_ANSWERS) => { + // Body parameters (note that non-required body parameters will ignore garbage + // values, rather than causing a 400 response). Produce warning header and logs for + // any unused fields. + let result = body.into_raw().await; + match result { + Ok(body) => { + let mut unused_elements = Vec::new(); + let param_answer_input: Option = if !body.is_empty() { + let deserializer = &mut serde_json::Deserializer::from_slice(&body); + match serde_ignored::deserialize(deserializer, |path| { + warn!("Ignoring unknown field in body: {}", path); + unused_elements.push(path.to_string()); + }) { + Ok(param_answer_input) => param_answer_input, + Err(e) => return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Couldn't parse body parameter AnswerInput - doesn't match schema: {}", e))) + .expect("Unable to create Bad Request response for invalid body parameter AnswerInput due to schema")), + } + } else { + None + }; + let param_answer_input = match param_answer_input { + Some(param_answer_input) => param_answer_input, + None => return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from("Missing required body parameter AnswerInput")) + .expect("Unable to create Bad Request response for missing body parameter AnswerInput")), + }; + + let result = api_impl.answers_post( + param_answer_input, + &context + ).await; + let mut response = Response::new(Body::empty()); + response.headers_mut().insert( + HeaderName::from_static("x-span-id"), + HeaderValue::from_str((&context as &dyn Has).get().0.clone().as_str()) + .expect("Unable to create X-Span-ID header value")); + + if !unused_elements.is_empty() { + response.headers_mut().insert( + HeaderName::from_static("warning"), + HeaderValue::from_str(format!("Ignoring unknown fields in body: {:?}", unused_elements).as_str()) + .expect("Unable to create Warning header value")); + } + + match result { + Ok(rsp) => match rsp { + AnswersPostResponse::SuccessfullyCreatedAnswer + (body) + => { + *response.status_mut() = StatusCode::from_u16(201).expect("Unable to turn 201 into a StatusCode"); + response.headers_mut().insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json") + .expect("Unable to create Content-Type header for ANSWERS_POST_SUCCESSFULLY_CREATED_ANSWER")); + let body_content = serde_json::to_string(&body).expect("impossible to fail to serialize"); + *response.body_mut() = Body::from(body_content); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + *response.body_mut() = Body::from("An internal error occurred"); + }, + } + + Ok(response) + }, + Err(e) => Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Couldn't read body parameter AnswerInput: {}", e))) + .expect("Unable to create Bad Request response due to unable to read body parameter AnswerInput")), + } + }, + + // GamesIdGet - GET /games/{id} + hyper::Method::GET if path.matched(paths::ID_GAMES_ID) => { + // Path parameters + let path: &str = uri.path(); + let path_params = + paths::REGEX_GAMES_ID + .captures(path) + .unwrap_or_else(|| + panic!("Path {} matched RE GAMES_ID in set but failed match against \"{}\"", path, paths::REGEX_GAMES_ID.as_str()) + ); + + let param_id = match percent_encoding::percent_decode(path_params["id"].as_bytes()).decode_utf8() { + Ok(param_id) => match param_id.parse::() { + Ok(param_id) => param_id, + Err(e) => return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Couldn't parse path parameter id: {}", e))) + .expect("Unable to create Bad Request response for invalid path parameter")), + }, + Err(_) => return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["id"]))) + .expect("Unable to create Bad Request response for invalid percent decode")) + }; + + let result = api_impl.games_id_get( + param_id, + &context + ).await; + let mut response = Response::new(Body::empty()); + response.headers_mut().insert( + HeaderName::from_static("x-span-id"), + HeaderValue::from_str((&context as &dyn Has).get().0.clone().as_str()) + .expect("Unable to create X-Span-ID header value")); + + match result { + Ok(rsp) => match rsp { + GamesIdGetResponse::SuccessfullyFetchedGame + (body) + => { + *response.status_mut() = StatusCode::from_u16(200).expect("Unable to turn 200 into a StatusCode"); + response.headers_mut().insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json") + .expect("Unable to create Content-Type header for GAMES_ID_GET_SUCCESSFULLY_FETCHED_GAME")); + let body_content = serde_json::to_string(&body).expect("impossible to fail to serialize"); + *response.body_mut() = Body::from(body_content); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + *response.body_mut() = Body::from("An internal error occurred"); + }, + } + + Ok(response) + }, + + // GamesPost - POST /games + hyper::Method::POST if path.matched(paths::ID_GAMES) => { + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = form_urlencoded::parse(uri.query().unwrap_or_default().as_bytes()).collect::>(); + let param_concept = query_params.iter().filter(|e| e.0 == "concept").map(|e| e.1.clone()) + .next(); + let param_concept = match param_concept { + Some(param_concept) => { + let param_concept = + ::from_str + (¶m_concept); + match param_concept { + Ok(param_concept) => Some(param_concept), + Err(e) => return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Couldn't parse query parameter concept - doesn't match schema: {}", e))) + .expect("Unable to create Bad Request response for invalid query parameter concept")), + } + }, + None => None, + }; + let param_incarnation_filter_left_square_bracket_concept_right_square_bracket = query_params.iter().filter(|e| e.0 == "incarnation_filter[concept]").map(|e| e.1.clone()) + .next(); + let param_incarnation_filter_left_square_bracket_concept_right_square_bracket = match param_incarnation_filter_left_square_bracket_concept_right_square_bracket { + Some(param_incarnation_filter_left_square_bracket_concept_right_square_bracket) => { + let param_incarnation_filter_left_square_bracket_concept_right_square_bracket = + ::from_str + (¶m_incarnation_filter_left_square_bracket_concept_right_square_bracket); + match param_incarnation_filter_left_square_bracket_concept_right_square_bracket { + Ok(param_incarnation_filter_left_square_bracket_concept_right_square_bracket) => Some(param_incarnation_filter_left_square_bracket_concept_right_square_bracket), + Err(e) => return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Couldn't parse query parameter incarnation_filter[concept] - doesn't match schema: {}", e))) + .expect("Unable to create Bad Request response for invalid query parameter incarnation_filter[concept]")), + } + }, + None => None, + }; + + // Body parameters (note that non-required body parameters will ignore garbage + // values, rather than causing a 400 response). Produce warning header and logs for + // any unused fields. + let result = body.into_raw().await; + match result { + Ok(body) => { + let mut unused_elements = Vec::new(); + let param_game_input: Option = if !body.is_empty() { + let deserializer = &mut serde_json::Deserializer::from_slice(&body); + match serde_ignored::deserialize(deserializer, |path| { + warn!("Ignoring unknown field in body: {}", path); + unused_elements.push(path.to_string()); + }) { + Ok(param_game_input) => param_game_input, + Err(e) => return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Couldn't parse body parameter GameInput - doesn't match schema: {}", e))) + .expect("Unable to create Bad Request response for invalid body parameter GameInput due to schema")), + } + } else { + None + }; + let param_game_input = match param_game_input { + Some(param_game_input) => param_game_input, + None => return Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from("Missing required body parameter GameInput")) + .expect("Unable to create Bad Request response for missing body parameter GameInput")), + }; + + let result = api_impl.games_post( + param_game_input, + param_concept, + param_incarnation_filter_left_square_bracket_concept_right_square_bracket, + &context + ).await; + let mut response = Response::new(Body::empty()); + response.headers_mut().insert( + HeaderName::from_static("x-span-id"), + HeaderValue::from_str((&context as &dyn Has).get().0.clone().as_str()) + .expect("Unable to create X-Span-ID header value")); + + if !unused_elements.is_empty() { + response.headers_mut().insert( + HeaderName::from_static("warning"), + HeaderValue::from_str(format!("Ignoring unknown fields in body: {:?}", unused_elements).as_str()) + .expect("Unable to create Warning header value")); + } + + match result { + Ok(rsp) => match rsp { + GamesPostResponse::SuccessfullyCreatedGame + (body) + => { + *response.status_mut() = StatusCode::from_u16(201).expect("Unable to turn 201 into a StatusCode"); + response.headers_mut().insert( + CONTENT_TYPE, + HeaderValue::from_str("application/json") + .expect("Unable to create Content-Type header for GAMES_POST_SUCCESSFULLY_CREATED_GAME")); + let body_content = serde_json::to_string(&body).expect("impossible to fail to serialize"); + *response.body_mut() = Body::from(body_content); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + *response.body_mut() = Body::from("An internal error occurred"); + }, + } + + Ok(response) + }, + Err(e) => Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::from(format!("Couldn't read body parameter GameInput: {}", e))) + .expect("Unable to create Bad Request response due to unable to read body parameter GameInput")), + } + }, + + _ if path.matched(paths::ID_ANSWERS) => method_not_allowed(), + _ if path.matched(paths::ID_GAMES) => method_not_allowed(), + _ if path.matched(paths::ID_GAMES_ID) => method_not_allowed(), + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .expect("Unable to create Not Found response")) + } + } Box::pin(run(self.api_impl.clone(), req)) } +} + +/// Request parser for `Api`. +pub struct ApiRequestParser; +impl RequestParser for ApiRequestParser { + fn parse_operation_id(request: &Request) -> Option<&'static str> { + let path = paths::GLOBAL_REGEX_SET.matches(request.uri().path()); + match *request.method() { + // AnswersPost - POST /answers + hyper::Method::POST if path.matched(paths::ID_ANSWERS) => Some("AnswersPost"), + // GamesIdGet - GET /games/{id} + hyper::Method::GET if path.matched(paths::ID_GAMES_ID) => Some("GamesIdGet"), + // GamesPost - POST /games + hyper::Method::POST if path.matched(paths::ID_GAMES) => Some("GamesPost"), + _ => None, + } + } +} diff --git a/waydowntown/waydowntown_rust_server/src/server/server_auth.rs b/waydowntown/waydowntown_rust_server/src/server/server_auth.rs new file mode 100644 index 00000000..ba78eb2f --- /dev/null +++ b/waydowntown/waydowntown_rust_server/src/server/server_auth.rs @@ -0,0 +1,28 @@ +use super::Service; +use crate::{Api, AuthenticationApi}; +use swagger::{ + ApiError, + Authorization, + auth::{Basic, Bearer}, + Has, + XSpanIdString}; + +impl AuthenticationApi for Service where +T: Api + Clone + Send + 'static + AuthenticationApi, +C: Has + Has> + Send + Sync + 'static { + + /// Passthrough of the task to the api-implementation + fn bearer_authorization(&self, token: &Bearer) -> Result { + self.api_impl.bearer_authorization(token) + } + + /// Passthrough of the task to the api-implementation + fn apikey_authorization(&self, token: &str) -> Result { + self.api_impl.apikey_authorization(token) + } + + /// Passthrough of the task to the api-implementation + fn basic_authorization(&self, basic: &Basic) -> Result { + self.api_impl.basic_authorization(basic) + } +}