forklaunch 1.35.0

Launch faster with forklaunch
use std::fmt;
{{#has_operations}}use std::collections::BTreeMap;{{/has_operations}}
use forklaunch_universal_sdk as runtime;
pub use runtime::{SdkError, SdkAuth, HmacSigner, SdkResponseWithHeaders};
{{#has_multipart}}pub use runtime::{MultipartForm, MultipartPart};{{/has_multipart}}
{{#has_multipart_response}}pub use runtime::{MultipartResponse, MultipartResponsePart};{{/has_multipart_response}}
{{#has_sse}}pub use runtime::{SdkEventStream, sse};{{/has_sse}}
pub type SdkRequest = runtime::SdkRequest{{#has_multipart}}<MultipartForm>{{/has_multipart}};
type ClientRuntime = runtime::UniversalSdk{{#has_multipart}}<MultipartForm>{{/has_multipart}};

{{! forklaunch:models}}{{{model_source}}}
{{#operations}}
#[derive({{^json_request_type}}Default, {{/json_request_type}}{{^multipart_request}}Clone, PartialEq, {{^typed_fields}}Eq, {{/typed_fields}}::serde::Serialize, ::serde::Deserialize{{/multipart_request}})]
pub struct {{request_type}}{{#json_request_type}}<T = {{{json_request_type}}}>{{/json_request_type}} {
{{^no_input}}    pub params: {{#params_type}}{{{params_type}}}{{/params_type}}{{^params_type}}BTreeMap<String, String>{{/params_type}},
    pub query: {{#query_type}}{{{query_type}}}{{/query_type}}{{^query_type}}BTreeMap<String, String>{{/query_type}},
{{/no_input}}    pub headers: {{#headers_type}}{{{headers_type}}}{{/headers_type}}{{^headers_type}}BTreeMap<String, String>{{/headers_type}},
{{#has_body}}    pub body: Option<{{#multipart_request}}MultipartForm{{/multipart_request}}{{^multipart_request}}{{#binary_request}}Vec<u8>{{/binary_request}}{{^binary_request}}{{#json_request_type}}T{{/json_request_type}}{{^json_request_type}}String{{/json_request_type}}{{/binary_request}}{{/multipart_request}}>,{{/has_body}}
}

{{#json_request_type}}
impl<T> Default for {{request_type}}<T> {
    fn default() -> Self {
        Self { params: Default::default(), query: Default::default(), headers: Default::default(), body: None }
    }
}
{{/json_request_type}}
impl{{#json_request_type}}<T>{{/json_request_type}} fmt::Debug for {{request_type}}{{#json_request_type}}<T>{{/json_request_type}} {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.debug_struct("{{request_type}}")
            .field("params", &"<redacted>")
            .field("query", &"<redacted>")
            .field("headers", &"<redacted>")
{{#has_body}}            .field("has_body", &self.body.is_some()){{/has_body}}
            .finish_non_exhaustive()
    }
}

#[derive(Debug{{^streaming_response}}, Clone, PartialEq, ::serde::Serialize, ::serde::Deserialize{{/streaming_response}})]
pub enum {{responses_type}} {
{{#responses}}
    {{status_variant}}({{#headers_type}}SdkResponseWithHeaders<{{/headers_type}}{{{rust_response_type}}}{{#headers_type}}, {{{headers_type}}}>{{/headers_type}}),
{{/responses}}
{{^shared_response_decoders}}    Error { status: u16, message: String },{{/shared_response_decoders}}
}

{{/operations}}
{{! forklaunch:client}}#[derive(Clone)]
pub struct {{client_name}} {
    runtime: ClientRuntime,
}

impl fmt::Debug for {{client_name}} {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.debug_struct("{{client_name}}")
            .field("runtime", &self.runtime).finish()
    }
}

impl {{client_name}} {
    #[must_use]
    pub fn new(base_url: impl Into<String>) -> Self {
        Self { runtime: ClientRuntime::new(base_url) }
    }

    /// Custom transports must enforce TLS verification and disable credential-bearing redirects.
    #[must_use]
    pub fn with_http_client(base_url: impl Into<String>, http: reqwest::Client) -> Self {
        Self { runtime: ClientRuntime::with_http_client(base_url, http) }
    }

    /// Local development only: permits plaintext credentials outside loopback.
    #[must_use]
    pub fn with_plaintext_development_transport(mut self) -> Self {
        self.runtime = self.runtime.with_plaintext_development_transport();
        self
    }
    {{#has_multipart_response}}
    pub fn with_multipart_response_limit(mut self, max_bytes: usize) -> Result<Self, SdkError> {
        self.runtime = self.runtime.with_multipart_response_limit(max_bytes)?;
        Ok(self)
    }
    {{/has_multipart_response}}
    {{#has_sse}}
    pub fn with_sse_event_limit(mut self, max_event_bytes: usize) -> Result<Self, SdkError> {
        self.runtime = self.runtime.with_sse_event_limit(max_event_bytes)?;
        Ok(self)
    }
    {{/has_sse}}
    #[must_use]
    pub fn with_default_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
        self.runtime = self.runtime.with_default_header(name, value);
        self
    }
    #[must_use]
    pub fn with_bearer_token(mut self, token: impl Into<String>) -> Self {
        self.runtime = self.runtime.with_bearer_token(token);
        self
    }
    #[must_use]
    pub fn with_jwt(self, token: impl Into<String>) -> Self { self.with_bearer_token(token) }
    #[must_use]
    pub fn with_basic_auth(mut self, username: impl Into<String>, password: impl Into<String>) -> Self {
        self.runtime = self.runtime.with_basic_auth(username, password);
        self
    }
    /// One fixed `authorization` value; an HMAC credential verifies at most once.
    /// Prefer `with_hmac_secret` for service-to-service calls.
    #[must_use]
    pub fn with_hmac_authorization(mut self, value: impl Into<String>) -> Self {
        self.runtime = self.runtime.with_hmac_authorization(value);
        self
    }
    /// Sign every request with the ForkLaunch request HMAC (`keyId=default`): the final
    /// method, URL path, body bytes, a fresh timestamp and a random nonce.
    #[must_use]
    pub fn with_hmac_secret(mut self, secret: impl Into<Vec<u8>>) -> Self {
        self.runtime = self.runtime.with_hmac_secret(secret);
        self
    }
    /// Sign every request with a named server key.
    #[must_use]
    pub fn with_hmac_signer(mut self, signer: HmacSigner) -> Self {
        self.runtime = self.runtime.with_hmac_signer(signer);
        self
    }
    #[must_use]
    pub fn with_auth_hook<F>(mut self, hook: F) -> Self
    where F: Fn(&mut SdkRequest) + Send + Sync + 'static {
        self.runtime = self.runtime.with_auth_hook(hook);
        self
    }

{{#operations}}
    {{#json_request_type}}
    /// Serialize a schema-typed body before constructing the transport request.
    pub fn {{method_name}}(&self, input: {{request_type}}) -> Result<SdkRequest, serde_json::Error> {
        {{^typed_fields}}Ok({{/typed_fields}}self.{{execute_method_name}}_raw_request({{request_type}}::<String> {
            params: input.params, query: input.query, headers: input.headers,
            body: input.body.map(|body| {{#form_request}}runtime::encode_urlencoded(&body){{/form_request}}{{^form_request}}serde_json::to_string(&body){{/form_request}}).transpose()?,
        }){{^typed_fields}}){{/typed_fields}}
    }

    /// Execute a schema-typed request. Raw encoded bytes require the explicit `_raw` method.
    pub async fn {{execute_method_name}}(&self, input: {{request_type}}) -> Result<{{responses_type}}, SdkError> {
        let input = {{request_type}}::<String> {
            params: input.params, query: input.query, headers: input.headers,
            body: input.body.map(|body| {{#form_request}}runtime::encode_urlencoded(&body){{/form_request}}{{^form_request}}serde_json::to_string(&body){{/form_request}}).transpose().map_err(SdkError::Encode)?,
        };
        self.{{execute_method_name}}_raw(input).await
    }
    {{/json_request_type}}
    /// Construct a transport request{{#json_request_type}} from explicitly encoded wire data{{/json_request_type}}.
{{^typed_fields}}    #[must_use]
{{/typed_fields}}    pub fn {{#json_request_type}}{{execute_method_name}}_raw_request{{/json_request_type}}{{^json_request_type}}{{method_name}}{{/json_request_type}}(&self, input: {{request_type}}{{#json_request_type}}<String>{{/json_request_type}}) -> {{#typed_fields}}Result<SdkRequest, serde_json::Error>{{/typed_fields}}{{^typed_fields}}SdkRequest{{/typed_fields}} {
        let path = runtime::path_with_params("{{{path}}}", &{{#no_input}}::std::collections::BTreeMap::new(){{/no_input}}{{^no_input}}{{#params_type}}runtime::request_fields(&input.params)?{{/params_type}}{{^params_type}}input.params{{/params_type}}{{/no_input}});
        {{#typed_fields}}Ok({{/typed_fields}}SdkRequest {
            method: "{{http_method}}",
            url: format!("{}{}", self.runtime.base_url(), path),
            query: {{#no_input}}::std::collections::BTreeMap::new(){{/no_input}}{{^no_input}}{{#query_type}}runtime::request_fields(&input.query)?{{/query_type}}{{^query_type}}input.query{{/query_type}}{{/no_input}},
            headers: {{#headers_type}}runtime::request_fields(&input.headers)?{{/headers_type}}{{^headers_type}}input.headers{{/headers_type}},
            body: {{^has_body}}None{{/has_body}}{{#has_body}}{{#multipart_request}}None{{/multipart_request}}{{^multipart_request}}{{#binary_request}}None{{/binary_request}}{{^binary_request}}input.body{{/binary_request}}{{/multipart_request}}{{/has_body}},
            body_bytes: {{#binary_request}}input.body{{/binary_request}}{{^binary_request}}None{{/binary_request}},
            {{{multipart_field}}}
            {{^has_multipart}}multipart: None,{{/has_multipart}}
            content_type: "{{{request_content_type}}}",
        }{{#typed_fields}}){{/typed_fields}}
    }

    {{#no_input}}
    /// Invoke this operation without request parameters or a body.
    pub async fn {{execute_method_name}}(&self) -> Result<{{responses_type}}, SdkError> {
        self.{{execute_method_name}}_with_options({{request_type}}::default()).await
    }

    /// Invoke this operation with per-request transport headers.
    {{/no_input}}
    pub async fn {{execute_method_name}}{{#no_input}}_with_options{{/no_input}}{{#json_request_type}}_raw{{/json_request_type}}(&self, input: {{request_type}}{{#json_request_type}}<String>{{/json_request_type}}) -> Result<{{responses_type}}, SdkError> {
        let request = self.{{#json_request_type}}{{execute_method_name}}_raw_request{{/json_request_type}}{{^json_request_type}}{{method_name}}{{/json_request_type}}(input){{#typed_fields}}.map_err(SdkError::Encode)?{{/typed_fields}};
        {{#version_metadata}}let (response, matched_versions) = self.runtime.execute_versioned{{#request_objects}}_with_fields{{/request_objects}}(request, {{{version_metadata}}}{{#request_objects}}, {{{request_objects}}}{{/request_objects}}).await?;{{/version_metadata}}
        {{^version_metadata}}let response = self.runtime.execute{{#request_objects}}_with_fields{{/request_objects}}(request{{#request_objects}}, {{{request_objects}}}{{/request_objects}}).await?;{{/version_metadata}}
        let status = response.status().as_u16();
        let _content_type = runtime::response_content_type(&response)?;
        {{#version_metadata}}matched_versions.validate_headers(status, &_content_type, response.headers())?;{{/version_metadata}}
        match status {
{{#responses}}
            {{status_code}}{{^empty_response}} if _content_type == "{{{content_type}}}"{{/empty_response}} => {
                {{#headers_type}}let headers=runtime::decode_response_headers::<{{{headers_type}}}>(response.headers(), {{{rust_header_rules}}})?;{{/headers_type}}
                {{#empty_response}}let bytes=response.bytes().await?; {{#version_metadata}}if !bytes.is_empty(){return Err(SdkError::Contract("nonempty versioned empty response"));} matched_versions.validate_body(status,"",&serde_json::Value::Null)?;{{/version_metadata}}{{^version_metadata}}let _=bytes;{{/version_metadata}} let body=();{{/empty_response}}
                {{#json_response}}{{#shared_response_decoders}}let body = runtime::decode_json_response(response, {{{rust_body_schema}}}, {{#version_metadata}}Some(&matched_versions){{/version_metadata}}{{^version_metadata}}None{{/version_metadata}}).await?;{{/shared_response_decoders}}{{^shared_response_decoders}}let value:serde_json::Value=serde_json::from_slice(&response.bytes().await?).map_err(|_|SdkError::Contract("invalid response JSON"))?;
                {{#version_metadata}}matched_versions.validate_body(status, &_content_type, &value)?;{{/version_metadata}}
                runtime::ResponseValidator::new({{{rust_body_schema}}})?.validate(&value)?;
                let body=serde_json::from_value::<{{{rust_response_type}}}>(value).map_err(|_|SdkError::Contract("invalid typed response"))?;{{/shared_response_decoders}}{{/json_response}}
                {{#text_response}}{{#shared_response_decoders}}let body = runtime::decode_text_response(response, {{{rust_body_schema}}}, {{#version_metadata}}Some(&matched_versions){{/version_metadata}}{{^version_metadata}}None{{/version_metadata}}).await?;{{/shared_response_decoders}}{{^shared_response_decoders}}let body=response.text().await?; {{#version_metadata}}matched_versions.validate_body(status, &_content_type, &serde_json::Value::String(body.clone()))?;{{/version_metadata}} runtime::ResponseValidator::new({{{rust_body_schema}}})?.validate(&serde_json::Value::String(body.clone()))?;{{/shared_response_decoders}}{{/text_response}}
                {{#streaming_response}}let body=Box::new(SdkEventStream::from_response(response,self.runtime.sse_event_limit(), {{{rust_body_schema}}})?{{#version_metadata}}.with_matched_versions(matched_versions){{/version_metadata}});{{/streaming_response}}
                {{#multipart_response}}let body=runtime::decode_multipart_response(response,self.runtime.multipart_response_limit()).await?;{{/multipart_response}}
                {{#binary_response}}let body=response.bytes().await?.to_vec();{{/binary_response}}
                Ok({{responses_type}}::{{status_variant}}({{#headers_type}}SdkResponseWithHeaders {body,headers}{{/headers_type}}{{^headers_type}}body{{/headers_type}}))
            },
{{/responses}}
            _ => Err(SdkError::Contract("undeclared response status or content type")),
        }
    }

{{/operations}}
}