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
|
// Copyright 2022 Oxide Computer Company
|
||||||
|
|
||||||
//! Support code for generated clients.
|
mod progenitor_client;
|
||||||
|
|
||||||
use std::{
|
pub use crate::progenitor_client::*;
|
||||||
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)]
|
#[doc(hidden)]
|
||||||
pub fn encode_path(pc: &str) -> String {
|
pub fn code() -> &'static str {
|
||||||
percent_encoding::utf8_percent_encode(pc, PATH_SET).to_string()
|
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:
|
* Create the Rust source file containing the support code:
|
||||||
*/
|
*/
|
||||||
let progenitor_client_code =
|
let progenitor_client_code = progenitor_client::code();
|
||||||
include_str!("../../progenitor-client/src/lib.rs");
|
|
||||||
let mut clientrs = src;
|
let mut clientrs = src;
|
||||||
clientrs.push("progenitor_client.rs");
|
clientrs.push("progenitor_client.rs");
|
||||||
save(clientrs, progenitor_client_code)?;
|
save(clientrs, progenitor_client_code)?;
|
||||||
|
|
Loading…
Reference in New Issue