remove cross-package file dependency by adding progenitor_client::code() (#52)
This commit is contained in:
parent
fc15414bcd
commit
30d64120af
|
@ -1,240 +1,10 @@
|
|||
// Copyright 2022 Oxide Computer Company
|
||||
|
||||
//! Support code for generated clients.
|
||||
mod progenitor_client;
|
||||
|
||||
use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures_core::Stream;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
/// Represents a streaming, untyped byte stream for both success and error
|
||||
/// responses.
|
||||
pub type ByteStream =
|
||||
Pin<Box<dyn Stream<Item = reqwest::Result<Bytes>> + Send>>;
|
||||
|
||||
/// Success value returned by generated client methods.
|
||||
pub struct ResponseValue<T> {
|
||||
inner: T,
|
||||
status: reqwest::StatusCode,
|
||||
headers: reqwest::header::HeaderMap,
|
||||
// TODO cookies?
|
||||
}
|
||||
|
||||
impl<T: DeserializeOwned> ResponseValue<T> {
|
||||
#[doc(hidden)]
|
||||
pub async fn from_response<E: std::fmt::Debug>(
|
||||
response: reqwest::Response,
|
||||
) -> Result<Self, Error<E>> {
|
||||
let status = response.status();
|
||||
let headers = response.headers().clone();
|
||||
let inner = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(Error::InvalidResponsePayload)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
status,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseValue<ByteStream> {
|
||||
#[doc(hidden)]
|
||||
pub fn stream(response: reqwest::Response) -> Self {
|
||||
let status = response.status();
|
||||
let headers = response.headers().clone();
|
||||
Self {
|
||||
inner: Box::pin(response.bytes_stream()),
|
||||
status,
|
||||
headers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseValue<()> {
|
||||
#[doc(hidden)]
|
||||
pub fn empty(response: reqwest::Response) -> Self {
|
||||
let status = response.status();
|
||||
let headers = response.headers().clone();
|
||||
// TODO is there anything we want to do to confirm that there is no
|
||||
// content?
|
||||
Self {
|
||||
inner: (),
|
||||
status,
|
||||
headers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ResponseValue<T> {
|
||||
/// Consumes the ResponseValue, returning the wrapped value.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Get the status from this response.
|
||||
pub fn status(&self) -> reqwest::StatusCode {
|
||||
self.status
|
||||
}
|
||||
|
||||
/// Get the headers from this response.
|
||||
pub fn headers(&self) -> &reqwest::header::HeaderMap {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn map<U: std::fmt::Debug, F, E>(
|
||||
self,
|
||||
f: F,
|
||||
) -> Result<ResponseValue<U>, E>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
let Self {
|
||||
inner,
|
||||
status,
|
||||
headers,
|
||||
} = self;
|
||||
|
||||
Ok(ResponseValue {
|
||||
inner: f(inner),
|
||||
status,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for ResponseValue<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for ResponseValue<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug> std::fmt::Debug for ResponseValue<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error produced by generated client methods.
|
||||
///
|
||||
/// The type parameter may be a struct if there's a single expected error type
|
||||
/// or an enum if there are multiple valid error types. It can be the unit type
|
||||
/// if there are no structured returns expected.
|
||||
pub enum Error<E = ()> {
|
||||
/// A server error either with the data, or with the connection.
|
||||
CommunicationError(reqwest::Error),
|
||||
|
||||
/// A documented, expected error response.
|
||||
ErrorResponse(ResponseValue<E>),
|
||||
|
||||
/// An expected response code whose deserialization failed.
|
||||
// TODO we have stuff from the response; should we include it?
|
||||
InvalidResponsePayload(reqwest::Error),
|
||||
|
||||
/// A response not listed in the API description. This may represent a
|
||||
/// success or failure response; check `status().is_success()`.
|
||||
UnexpectedResponse(reqwest::Response),
|
||||
}
|
||||
|
||||
impl<E> Error<E> {
|
||||
/// Returns the status code, if the error was generated from a response.
|
||||
pub fn status(&self) -> Option<reqwest::StatusCode> {
|
||||
match self {
|
||||
Error::CommunicationError(e) => e.status(),
|
||||
Error::ErrorResponse(rv) => Some(rv.status()),
|
||||
Error::InvalidResponsePayload(e) => e.status(),
|
||||
Error::UnexpectedResponse(r) => Some(r.status()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this error into one without a typed body for unified error
|
||||
/// handling with APIs that distinguish various error response bodies.
|
||||
pub fn into_untyped(self) -> Error {
|
||||
match self {
|
||||
Error::CommunicationError(e) => Error::CommunicationError(e),
|
||||
Error::ErrorResponse(ResponseValue {
|
||||
inner: _,
|
||||
status,
|
||||
headers,
|
||||
}) => Error::ErrorResponse(ResponseValue {
|
||||
inner: (),
|
||||
status,
|
||||
headers,
|
||||
}),
|
||||
Error::InvalidResponsePayload(e) => {
|
||||
Error::InvalidResponsePayload(e)
|
||||
}
|
||||
Error::UnexpectedResponse(r) => Error::UnexpectedResponse(r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<reqwest::Error> for Error<E> {
|
||||
fn from(e: reqwest::Error) -> Self {
|
||||
Self::CommunicationError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> std::fmt::Display for Error<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::CommunicationError(e) => {
|
||||
write!(f, "Communication Error {}", e)
|
||||
}
|
||||
Error::ErrorResponse(_) => {
|
||||
write!(f, "Error Response")
|
||||
}
|
||||
Error::InvalidResponsePayload(e) => {
|
||||
write!(f, "Invalid Response Payload {}", e)
|
||||
}
|
||||
Error::UnexpectedResponse(r) => {
|
||||
write!(f, "Unexpected Response {:?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<E> std::fmt::Debug for Error<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
impl<E> std::error::Error for Error<E> {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::CommunicationError(e) => Some(e),
|
||||
Error::InvalidResponsePayload(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PATH_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
|
||||
.add(b' ')
|
||||
.add(b'"')
|
||||
.add(b'#')
|
||||
.add(b'<')
|
||||
.add(b'>')
|
||||
.add(b'?')
|
||||
.add(b'`')
|
||||
.add(b'{')
|
||||
.add(b'}');
|
||||
pub use crate::progenitor_client::*;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn encode_path(pc: &str) -> String {
|
||||
percent_encoding::utf8_percent_encode(pc, PATH_SET).to_string()
|
||||
pub fn code() -> &'static str {
|
||||
include_str!("progenitor_client.rs")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
// Copyright 2022 Oxide Computer Company
|
||||
|
||||
//! Support code for generated clients.
|
||||
|
||||
use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures_core::Stream;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
/// Represents a streaming, untyped byte stream for both success and error
|
||||
/// responses.
|
||||
pub type ByteStream =
|
||||
Pin<Box<dyn Stream<Item = reqwest::Result<Bytes>> + Send>>;
|
||||
|
||||
/// Success value returned by generated client methods.
|
||||
pub struct ResponseValue<T> {
|
||||
inner: T,
|
||||
status: reqwest::StatusCode,
|
||||
headers: reqwest::header::HeaderMap,
|
||||
// TODO cookies?
|
||||
}
|
||||
|
||||
impl<T: DeserializeOwned> ResponseValue<T> {
|
||||
#[doc(hidden)]
|
||||
pub async fn from_response<E: std::fmt::Debug>(
|
||||
response: reqwest::Response,
|
||||
) -> Result<Self, Error<E>> {
|
||||
let status = response.status();
|
||||
let headers = response.headers().clone();
|
||||
let inner = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(Error::InvalidResponsePayload)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
status,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseValue<ByteStream> {
|
||||
#[doc(hidden)]
|
||||
pub fn stream(response: reqwest::Response) -> Self {
|
||||
let status = response.status();
|
||||
let headers = response.headers().clone();
|
||||
Self {
|
||||
inner: Box::pin(response.bytes_stream()),
|
||||
status,
|
||||
headers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResponseValue<()> {
|
||||
#[doc(hidden)]
|
||||
pub fn empty(response: reqwest::Response) -> Self {
|
||||
let status = response.status();
|
||||
let headers = response.headers().clone();
|
||||
// TODO is there anything we want to do to confirm that there is no
|
||||
// content?
|
||||
Self {
|
||||
inner: (),
|
||||
status,
|
||||
headers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ResponseValue<T> {
|
||||
/// Consumes the ResponseValue, returning the wrapped value.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Get the status from this response.
|
||||
pub fn status(&self) -> reqwest::StatusCode {
|
||||
self.status
|
||||
}
|
||||
|
||||
/// Get the headers from this response.
|
||||
pub fn headers(&self) -> &reqwest::header::HeaderMap {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn map<U: std::fmt::Debug, F, E>(
|
||||
self,
|
||||
f: F,
|
||||
) -> Result<ResponseValue<U>, E>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
let Self {
|
||||
inner,
|
||||
status,
|
||||
headers,
|
||||
} = self;
|
||||
|
||||
Ok(ResponseValue {
|
||||
inner: f(inner),
|
||||
status,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for ResponseValue<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for ResponseValue<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug> std::fmt::Debug for ResponseValue<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error produced by generated client methods.
|
||||
///
|
||||
/// The type parameter may be a struct if there's a single expected error type
|
||||
/// or an enum if there are multiple valid error types. It can be the unit type
|
||||
/// if there are no structured returns expected.
|
||||
pub enum Error<E = ()> {
|
||||
/// A server error either with the data, or with the connection.
|
||||
CommunicationError(reqwest::Error),
|
||||
|
||||
/// A documented, expected error response.
|
||||
ErrorResponse(ResponseValue<E>),
|
||||
|
||||
/// An expected response code whose deserialization failed.
|
||||
// TODO we have stuff from the response; should we include it?
|
||||
InvalidResponsePayload(reqwest::Error),
|
||||
|
||||
/// A response not listed in the API description. This may represent a
|
||||
/// success or failure response; check `status().is_success()`.
|
||||
UnexpectedResponse(reqwest::Response),
|
||||
}
|
||||
|
||||
impl<E> Error<E> {
|
||||
/// Returns the status code, if the error was generated from a response.
|
||||
pub fn status(&self) -> Option<reqwest::StatusCode> {
|
||||
match self {
|
||||
Error::CommunicationError(e) => e.status(),
|
||||
Error::ErrorResponse(rv) => Some(rv.status()),
|
||||
Error::InvalidResponsePayload(e) => e.status(),
|
||||
Error::UnexpectedResponse(r) => Some(r.status()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this error into one without a typed body for unified error
|
||||
/// handling with APIs that distinguish various error response bodies.
|
||||
pub fn into_untyped(self) -> Error {
|
||||
match self {
|
||||
Error::CommunicationError(e) => Error::CommunicationError(e),
|
||||
Error::ErrorResponse(ResponseValue {
|
||||
inner: _,
|
||||
status,
|
||||
headers,
|
||||
}) => Error::ErrorResponse(ResponseValue {
|
||||
inner: (),
|
||||
status,
|
||||
headers,
|
||||
}),
|
||||
Error::InvalidResponsePayload(e) => {
|
||||
Error::InvalidResponsePayload(e)
|
||||
}
|
||||
Error::UnexpectedResponse(r) => Error::UnexpectedResponse(r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<reqwest::Error> for Error<E> {
|
||||
fn from(e: reqwest::Error) -> Self {
|
||||
Self::CommunicationError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> std::fmt::Display for Error<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::CommunicationError(e) => {
|
||||
write!(f, "Communication Error {}", e)
|
||||
}
|
||||
Error::ErrorResponse(_) => {
|
||||
write!(f, "Error Response")
|
||||
}
|
||||
Error::InvalidResponsePayload(e) => {
|
||||
write!(f, "Invalid Response Payload {}", e)
|
||||
}
|
||||
Error::UnexpectedResponse(r) => {
|
||||
write!(f, "Unexpected Response {:?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<E> std::fmt::Debug for Error<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
impl<E> std::error::Error for Error<E> {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::CommunicationError(e) => Some(e),
|
||||
Error::InvalidResponsePayload(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PATH_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
|
||||
.add(b' ')
|
||||
.add(b'"')
|
||||
.add(b'#')
|
||||
.add(b'<')
|
||||
.add(b'>')
|
||||
.add(b'?')
|
||||
.add(b'`')
|
||||
.add(b'{')
|
||||
.add(b'}');
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn encode_path(pc: &str) -> String {
|
||||
percent_encoding::utf8_percent_encode(pc, PATH_SET).to_string()
|
||||
}
|
|
@ -117,8 +117,7 @@ fn main() -> Result<()> {
|
|||
/*
|
||||
* Create the Rust source file containing the support code:
|
||||
*/
|
||||
let progenitor_client_code =
|
||||
include_str!("../../progenitor-client/src/lib.rs");
|
||||
let progenitor_client_code = progenitor_client::code();
|
||||
let mut clientrs = src;
|
||||
clientrs.push("progenitor_client.rs");
|
||||
save(clientrs, progenitor_client_code)?;
|
||||
|
|
Loading…
Reference in New Issue