remove coupling with reqwest
This commit is contained in:
parent
94be793df7
commit
dde4762a05
9 changed files with 209 additions and 132 deletions
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -1,6 +1,6 @@
|
||||||
[submodule "crates/subresource_integrity"]
|
[submodule "crates/subresource_integrity"]
|
||||||
path = crates/subresource_integrity
|
path = crates/subresource_integrity
|
||||||
url = https://fem.mint.lgbt/kitsunecafe/subresource-integrity.git
|
url = https://forgejo.archandle.net/rowan/subresource-integrity.git
|
||||||
[submodule "crates/maybe_owned"]
|
[submodule "crates/maybe_owned"]
|
||||||
path = crates/maybe_owned
|
path = crates/maybe_owned
|
||||||
url = https://fem.mint.lgbt/kitsunecafe/maybe-owned.git
|
url = https://forgejo.archandle.net/rowan/maybe-owned.git
|
||||||
|
|
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -988,9 +988,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.79"
|
version = "2.0.87"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1040,6 +1040,26 @@ dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.36"
|
version = "0.3.36"
|
||||||
|
@ -1216,6 +1236,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"subresource_integrity",
|
"subresource_integrity",
|
||||||
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
|
|
|
@ -12,6 +12,7 @@ reqwest = "0.12.8"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
subresource_integrity = { version = "0.1.0", path = "crates/subresource_integrity", features = ["serde", "md5"] }
|
subresource_integrity = { version = "0.1.0", path = "crates/subresource_integrity", features = ["serde", "md5"] }
|
||||||
|
thiserror = "2.0.12"
|
||||||
time = { version = "0.3.36", features = ["serde"] }
|
time = { version = "0.3.36", features = ["serde"] }
|
||||||
url = "2.5.2"
|
url = "2.5.2"
|
||||||
|
|
||||||
|
|
35
src/client.rs
Normal file
35
src/client.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use crate::{error::Error, response::PagedResponse};
|
||||||
|
|
||||||
|
pub trait Client {
|
||||||
|
type Request;
|
||||||
|
|
||||||
|
fn get(&self, url: url::Url) -> Result<Self::Request, Error>;
|
||||||
|
fn fetch(
|
||||||
|
&self,
|
||||||
|
req: Self::Request,
|
||||||
|
) -> impl std::future::Future<Output = Result<PagedResponse, Error>> + Send;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client for reqwest::Client {
|
||||||
|
type Request = reqwest::Request;
|
||||||
|
|
||||||
|
fn get(&self, url: url::Url) -> Result<Self::Request, Error> {
|
||||||
|
reqwest::Client::get(self, url)
|
||||||
|
.build()
|
||||||
|
.map_err(|e| Error::Request(Box::new(e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch(&self, req: Self::Request) -> Result<PagedResponse, Error> {
|
||||||
|
let response = self
|
||||||
|
.execute(req)
|
||||||
|
.await
|
||||||
|
.map_err(|e| Error::Response(Box::new(e)))?;
|
||||||
|
|
||||||
|
let response = response
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.map_err(|e| Error::Response(Box::new(e)))?;
|
||||||
|
|
||||||
|
str::parse(&response)
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,8 +33,9 @@ impl Display for Order {
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
pub enum Stream {
|
pub enum Stream {
|
||||||
|
Supported,
|
||||||
#[serde(rename = "LTS")]
|
#[serde(rename = "LTS")]
|
||||||
LTS,
|
Lts,
|
||||||
Beta,
|
Beta,
|
||||||
Alpha,
|
Alpha,
|
||||||
Tech,
|
Tech,
|
||||||
|
@ -43,7 +44,8 @@ pub enum Stream {
|
||||||
impl Display for Stream {
|
impl Display for Stream {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::LTS => write!(f, "LTS"),
|
Self::Supported => write!(f, "SUPPORTED"),
|
||||||
|
Self::Lts => write!(f, "LTS"),
|
||||||
Self::Beta => write!(f, "BETA"),
|
Self::Beta => write!(f, "BETA"),
|
||||||
Self::Alpha => write!(f, "ALPHA"),
|
Self::Alpha => write!(f, "ALPHA"),
|
||||||
Self::Tech => write!(f, "TECH"),
|
Self::Tech => write!(f, "TECH"),
|
||||||
|
@ -57,7 +59,7 @@ pub enum Platform {
|
||||||
Windows,
|
Windows,
|
||||||
Linux,
|
Linux,
|
||||||
#[serde(rename = "MAC_OS")]
|
#[serde(rename = "MAC_OS")]
|
||||||
MacOS,
|
MacOs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Platform {
|
impl Display for Platform {
|
||||||
|
@ -65,7 +67,7 @@ impl Display for Platform {
|
||||||
match self {
|
match self {
|
||||||
Self::Windows => write!(f, "WINDOWS"),
|
Self::Windows => write!(f, "WINDOWS"),
|
||||||
Self::Linux => write!(f, "LINUX"),
|
Self::Linux => write!(f, "LINUX"),
|
||||||
Self::MacOS => write!(f, "MAC_OS"),
|
Self::MacOs => write!(f, "MAC_OS"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
21
src/error.rs
Normal file
21
src/error.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::response::APIError;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("error parsing result: {0}")]
|
||||||
|
Parse(serde_json::Error),
|
||||||
|
#[error("invalid request: {0}")]
|
||||||
|
Request(Box<dyn core::error::Error>),
|
||||||
|
#[error("connection error: {0}")]
|
||||||
|
Response(Box<dyn core::error::Error>),
|
||||||
|
#[error("api responded with error: {0}")]
|
||||||
|
Api(APIError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Error> for Error {
|
||||||
|
fn from(value: serde_json::Error) -> Self {
|
||||||
|
Self::Parse(value)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#![feature(result_flattening)]
|
pub mod client;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
pub mod error;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
|
156
src/request.rs
156
src/request.rs
|
@ -1,17 +1,17 @@
|
||||||
use std::borrow::Cow;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
use maybe_owned::MaybeOwned;
|
use maybe_owned::MaybeOwnedMut;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{client::Client, common::*, error, response::PagedResponse};
|
||||||
common::*,
|
|
||||||
response::{self, OffsetConnection},
|
|
||||||
};
|
|
||||||
|
|
||||||
type Parameter<'a> = (&'a str, &'a str);
|
type Parameter<'a> = (&'a str, &'a str);
|
||||||
|
|
||||||
pub struct RequestBuilder<'a> {
|
const RELEASE_API_URL: &'static str = "https://services.api.unity.com/unity/editor/release/";
|
||||||
client: Option<&'a reqwest::Client>,
|
|
||||||
|
pub struct RequestBuilder<'a, T> {
|
||||||
|
client: Option<MaybeOwnedMut<'a, T>>,
|
||||||
|
url: Option<Url>,
|
||||||
api_version: APIVersion,
|
api_version: APIVersion,
|
||||||
limit: u8,
|
limit: u8,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
@ -22,11 +22,12 @@ pub struct RequestBuilder<'a> {
|
||||||
version: Option<Version>,
|
version: Option<Version>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RequestBuilder<'_> {
|
impl<T: Default> Default for RequestBuilder<'_, T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
limit: 10,
|
limit: 10,
|
||||||
client: Default::default(),
|
client: Default::default(),
|
||||||
|
url: Some(Self::default_api_url()),
|
||||||
api_version: Default::default(),
|
api_version: Default::default(),
|
||||||
offset: Default::default(),
|
offset: Default::default(),
|
||||||
order: Default::default(),
|
order: Default::default(),
|
||||||
|
@ -38,11 +39,39 @@ impl Default for RequestBuilder<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RequestBuilder<'a> {
|
impl<'a, T: 'a> RequestBuilder<'a, T> {
|
||||||
pub fn new(client: &'a reqwest::Client) -> Self {
|
fn default_api_url() -> Url {
|
||||||
|
Url::parse(RELEASE_API_URL).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: 'a + Client> RequestBuilder<'a, T> {
|
||||||
|
pub fn new<I: Into<MaybeOwnedMut<'a, T>>>(client: I) -> RequestBuilder<'a, T> {
|
||||||
Self {
|
Self {
|
||||||
client: Some(client),
|
client: Some(client.into()),
|
||||||
..Default::default()
|
url: Some(Self::default_api_url()),
|
||||||
|
limit: 10,
|
||||||
|
api_version: Default::default(),
|
||||||
|
offset: Default::default(),
|
||||||
|
order: Default::default(),
|
||||||
|
stream: Default::default(),
|
||||||
|
platform: Default::default(),
|
||||||
|
architecture: Default::default(),
|
||||||
|
version: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_client<I: Into<MaybeOwnedMut<'a, T>>>(self, client: I) -> RequestBuilder<'a, T> {
|
||||||
|
Self {
|
||||||
|
client: Some(client.into()),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_url(self, url: Url) -> Self {
|
||||||
|
Self {
|
||||||
|
url: Some(url),
|
||||||
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,24 +115,23 @@ impl<'a> RequestBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(self) -> Result<OffsetConnection, response::Error> {
|
pub async fn send(self) -> Result<PagedResponse, error::Error> {
|
||||||
Into::<Request>::into(self).send().await
|
Into::<Request<'a, T>>::into(self).send().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<RequestBuilder<'a>> for Request<'a> {
|
impl<'a, T: 'a + Client> From<RequestBuilder<'a, T>> for Request<'a, T> {
|
||||||
fn from(val: RequestBuilder<'a>) -> Self {
|
fn from(val: RequestBuilder<'a, T>) -> Self {
|
||||||
Request {
|
Request {
|
||||||
client: Cow::Borrowed(val.client.unwrap()),
|
client: val.client.unwrap(),
|
||||||
|
api_url: val.url.unwrap_or_else(RequestBuilder::<T>::default_api_url),
|
||||||
api_version: val.api_version.to_string(),
|
api_version: val.api_version.to_string(),
|
||||||
limit: val.limit.to_string(),
|
limit: val.limit.to_string(),
|
||||||
offset: val.offset.to_string(),
|
offset: val.offset.to_string(),
|
||||||
order: val.order.to_string(),
|
order: val.order.to_string(),
|
||||||
stream: val.stream.map_or_else(String::default, |s| s.to_string()),
|
stream: val.stream.map_or_else(String::default, |s| s.to_string()),
|
||||||
|
|
||||||
platform: val
|
platform: val.platform.map_or_else(String::default, |p| p.to_string()),
|
||||||
.platform
|
|
||||||
.map_or_else(String::default, |p| p.to_string()),
|
|
||||||
|
|
||||||
architecture: val
|
architecture: val
|
||||||
.architecture
|
.architecture
|
||||||
|
@ -116,8 +144,9 @@ impl<'a> From<RequestBuilder<'a>> for Request<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Request<'a> {
|
pub struct Request<'a, T> {
|
||||||
client: Cow<'a, reqwest::Client>,
|
api_url: Url,
|
||||||
|
client: MaybeOwnedMut<'a, T>,
|
||||||
api_version: String,
|
api_version: String,
|
||||||
limit: String,
|
limit: String,
|
||||||
offset: String,
|
offset: String,
|
||||||
|
@ -128,10 +157,8 @@ pub struct Request<'a> {
|
||||||
version: String,
|
version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request<'_> {
|
impl<T: Client> Request<'_, T> {
|
||||||
const API_URI: &'static str = "https://services.api.unity.com/unity/editor/release/";
|
fn as_parameters(&self) -> impl Iterator<Item = Parameter<'_>> {
|
||||||
|
|
||||||
fn as_parameters(&self) -> impl Iterator<Item = Parameter> {
|
|
||||||
[
|
[
|
||||||
("limit", self.limit.as_str()),
|
("limit", self.limit.as_str()),
|
||||||
("offset", self.offset.as_str()),
|
("offset", self.offset.as_str()),
|
||||||
|
@ -145,50 +172,45 @@ impl Request<'_> {
|
||||||
.filter(|p| !p.1.is_empty())
|
.filter(|p| !p.1.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_dir(value: &str) -> String {
|
fn get_url(&self) -> Result<Url, url::ParseError> {
|
||||||
format!("{value}/")
|
let version = format!("{}/", self.api_version);
|
||||||
}
|
self.api_url.join(&version)?.join("releases")
|
||||||
|
|
||||||
fn api_root(&self) -> Result<Url, url::ParseError> {
|
|
||||||
Url::parse(Self::API_URI)
|
|
||||||
.and_then(|url| url.join(&Self::as_dir(&self.api_version)))
|
|
||||||
.and_then(|url| url.join("releases"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_uri(&self) -> Result<Url, url::ParseError> {
|
fn build_uri(&self) -> Result<Url, url::ParseError> {
|
||||||
self.api_root()
|
self.get_url()
|
||||||
.and_then(|url| Url::parse_with_params(url.as_str(), self.as_parameters()))
|
.and_then(|url| Url::parse_with_params(url.as_str(), self.as_parameters()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(&self) -> Result<OffsetConnection, response::Error> {
|
pub async fn send(&mut self) -> Result<PagedResponse, error::Error> {
|
||||||
let response = self.client.get(self.build_uri().unwrap()).send().await?;
|
let url = self.build_uri().unwrap();
|
||||||
response::parse(response).await
|
let req = self.client.get(url)?;
|
||||||
|
self.client.fetch(req).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct UnityReleaseClient<'a> {
|
pub struct UnityReleaseClient<'a, T> {
|
||||||
client: MaybeOwned<'a, reqwest::Client>,
|
client: MaybeOwnedMut<'a, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> UnityReleaseClient<'a> {
|
impl<'a, T: 'a> UnityReleaseClient<'a, T> {
|
||||||
pub fn new(client: impl Into<MaybeOwned<'a, reqwest::Client>>) -> Self {
|
pub fn new(client: impl Into<MaybeOwnedMut<'a, T>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
client: client.into(),
|
client: client.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn request(&self) -> RequestBuilder {
|
impl<'a, T: 'a + Client> UnityReleaseClient<'a, T> {
|
||||||
match &self.client {
|
pub fn request(&mut self) -> RequestBuilder<'_, T> {
|
||||||
MaybeOwned::Owned(x) => RequestBuilder::new(x),
|
RequestBuilder::new(MaybeOwnedMut::Borrowed(self.client.deref_mut()))
|
||||||
MaybeOwned::Borrowed(x) => RequestBuilder::new(x),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
&self,
|
&self,
|
||||||
request: impl Into<Request<'a>>,
|
request: impl Into<Request<'a, T>>,
|
||||||
) -> Result<OffsetConnection, response::Error> {
|
) -> Result<PagedResponse, error::Error> {
|
||||||
request.into().send().await
|
request.into().send().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,12 +218,13 @@ impl<'a> UnityReleaseClient<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::request::*;
|
use crate::request::*;
|
||||||
|
use reqwest;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn defaut_request_uri() {
|
fn defaut_request_uri() {
|
||||||
let expected = "https://services.api.unity.com/unity/editor/release/v1/releases?limit=10&offset=0&order=RELEASE_DATE_DESC";
|
let expected = "https://services.api.unity.com/unity/editor/release/v1/releases?limit=10&offset=0&order=RELEASE_DATE_DESC";
|
||||||
let client = UnityReleaseClient::default();
|
let mut client = UnityReleaseClient::new(reqwest::Client::default());
|
||||||
let request: Request = client.request().into();
|
let request: Request<reqwest::Client> = client.request().into();
|
||||||
assert_eq!(request.build_uri().unwrap().as_str(), expected);
|
assert_eq!(request.build_uri().unwrap().as_str(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,13 +232,13 @@ mod tests {
|
||||||
fn custom_request_uri() {
|
fn custom_request_uri() {
|
||||||
let expected = "https://services.api.unity.com/unity/editor/release/v1/releases?limit=5&offset=20&order=RELEASE_DATE_ASC&stream=LTS&platform=LINUX&architecture=ARM64&version=2020.3.44f1";
|
let expected = "https://services.api.unity.com/unity/editor/release/v1/releases?limit=5&offset=20&order=RELEASE_DATE_ASC&stream=LTS&platform=LINUX&architecture=ARM64&version=2020.3.44f1";
|
||||||
|
|
||||||
let client = UnityReleaseClient::default();
|
let mut client = UnityReleaseClient::default();
|
||||||
let request: Request = client
|
let request: Request<reqwest::Client> = client
|
||||||
.request()
|
.request()
|
||||||
.with_order(Order::Ascending)
|
.with_order(Order::Ascending)
|
||||||
.with_limit(5)
|
.with_limit(5)
|
||||||
.with_offset(20)
|
.with_offset(20)
|
||||||
.with_stream(Stream::LTS)
|
.with_stream(Stream::Lts)
|
||||||
.with_platform(Platform::Linux)
|
.with_platform(Platform::Linux)
|
||||||
.with_architecture(Architecture::Arm64)
|
.with_architecture(Architecture::Arm64)
|
||||||
.with_version("2020.3.44f1".to_string())
|
.with_version("2020.3.44f1".to_string())
|
||||||
|
@ -228,19 +251,20 @@ mod tests {
|
||||||
fn borrowed_client() {
|
fn borrowed_client() {
|
||||||
let expected = "https://services.api.unity.com/unity/editor/release/v1/releases?limit=10&offset=0&order=RELEASE_DATE_ASC";
|
let expected = "https://services.api.unity.com/unity/editor/release/v1/releases?limit=10&offset=0&order=RELEASE_DATE_ASC";
|
||||||
let request_client = reqwest::Client::default();
|
let request_client = reqwest::Client::default();
|
||||||
let client = UnityReleaseClient::new(request_client);
|
let mut client = UnityReleaseClient::new(request_client);
|
||||||
let request: Request = client.request().with_order(Order::Ascending).into();
|
let request: Request<reqwest::Client> =
|
||||||
|
client.request().with_order(Order::Ascending).into();
|
||||||
|
|
||||||
assert_eq!(request.build_uri().unwrap().as_str(), expected);
|
assert_eq!(request.build_uri().unwrap().as_str(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[tokio::test]
|
//#[tokio::test]
|
||||||
// async fn test_connection() {
|
//async fn test_connection() {
|
||||||
// let client = UnityReleaseClient::default();
|
// let mut client = UnityReleaseClient::default();
|
||||||
// let request = client.request().with_offset(25).with_limit(25);
|
// let request = client.request().with_offset(25).with_limit(25);
|
||||||
// let req: Request = request.into();
|
// let mut req: Request<reqwest::Client> = request.into();
|
||||||
// // println!("{}", req.build_uri().unwrap());
|
// // println!("{}", req.build_uri().unwrap());
|
||||||
// let response = req.send().await;
|
// let response = req.send().await;
|
||||||
// print!("{response:?}");
|
// print!("{response:?}");
|
||||||
// }
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use std::path::PathBuf;
|
use std::{fmt::Display, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
use reqwest::Response;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use subresource_integrity::Integrity;
|
use subresource_integrity::Integrity;
|
||||||
use time::Date;
|
use time::Date;
|
||||||
|
|
||||||
use crate::common::*;
|
use crate::{common::*, error::Error};
|
||||||
|
|
||||||
fn handle_fractional_numbers<'de, D>(deserializer: D) -> Result<usize, D::Error>
|
fn handle_fractional_numbers<'de, D>(deserializer: D) -> Result<usize, D::Error>
|
||||||
where
|
where
|
||||||
|
@ -56,18 +55,18 @@ pub struct ThirdPartyNotice {
|
||||||
pub enum FileKind {
|
pub enum FileKind {
|
||||||
Text,
|
Text,
|
||||||
#[serde(rename = "TAR_GZ")]
|
#[serde(rename = "TAR_GZ")]
|
||||||
TarGZ,
|
TarGz,
|
||||||
#[serde(rename = "TAR_XZ")]
|
#[serde(rename = "TAR_XZ")]
|
||||||
TarXZ,
|
TarXz,
|
||||||
ZIP,
|
Zip,
|
||||||
PKG,
|
Pkg,
|
||||||
EXE,
|
Exe,
|
||||||
PO,
|
Po,
|
||||||
DMG,
|
Dmg,
|
||||||
LZMA,
|
Lzma,
|
||||||
LZ4,
|
Lz4,
|
||||||
MD,
|
Md,
|
||||||
PDF,
|
Pdf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
@ -84,7 +83,7 @@ pub enum ModuleCategory {
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
pub enum SKUFamily {
|
pub enum SKUFamily {
|
||||||
DOTS,
|
Dots,
|
||||||
Classic,
|
Classic,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,60 +156,25 @@ pub struct Release {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
pub struct OffsetConnection {
|
pub struct PagedResponse {
|
||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
pub limit: u8,
|
pub limit: u8,
|
||||||
pub total: usize,
|
pub total: usize,
|
||||||
pub results: Vec<Release>,
|
pub results: Vec<Release>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn parse(response: Response) -> Result<OffsetConnection, Error> {
|
|
||||||
let body = response.text().await?;
|
|
||||||
println!("{body}");
|
|
||||||
serde_json::from_str::<ResponseKind>(body.as_str())
|
|
||||||
.map(Result::from)
|
|
||||||
.map_err(Error::from)
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum ResponseKind {
|
pub enum APIResponse {
|
||||||
Ok(OffsetConnection),
|
Ok(PagedResponse),
|
||||||
Err(APIError),
|
Err(APIError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl FromStr for PagedResponse {
|
||||||
pub enum Error {
|
type Err = Error;
|
||||||
API(APIError),
|
|
||||||
Parse(serde_json::Error),
|
|
||||||
Request(reqwest::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ResponseKind> for Result<OffsetConnection, Error> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
fn from(value: ResponseKind) -> Self {
|
serde_json::from_str(s).map_err(Error::from)
|
||||||
match value {
|
|
||||||
ResponseKind::Ok(v) => Ok(v),
|
|
||||||
ResponseKind::Err(e) => Err(Error::from(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<APIError> for Error {
|
|
||||||
fn from(value: APIError) -> Self {
|
|
||||||
Self::API(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<serde_json::Error> for Error {
|
|
||||||
fn from(value: serde_json::Error) -> Self {
|
|
||||||
Self::Parse(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<reqwest::Error> for Error {
|
|
||||||
fn from(value: reqwest::Error) -> Self {
|
|
||||||
Self::Request(value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,3 +184,11 @@ pub struct APIError {
|
||||||
pub status: u16,
|
pub status: u16,
|
||||||
pub detail: String,
|
pub detail: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for APIError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{} ({}): {}", self.title, self.status, self.detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::error::Error for APIError {}
|
||||||
|
|
Loading…
Add table
Reference in a new issue