1use crate::{get_json, get_response, schema::Run, wrapper::ContainsRun, Client, Error};
6use reqwest::{
7 header::ACCEPT,
8 multipart::{Form, Part},
9 Url,
10};
11use std::{
12 io::{self, Write},
13 ops::Deref,
14};
15
16impl Run {
17 pub async fn download(&self, client: &Client) -> Result<impl Deref<Target = [u8]>, Error> {
19 self::download(
20 client,
21 self.id.as_deref().ok_or(Error::UnidentifiableResource)?,
22 )
23 .await
24 }
25
26 pub async fn get(client: &Client, id: &str, historic: bool) -> Result<Run, Error> {
28 self::get(client, id, historic).await
29 }
30
31 pub async fn upload(client: &Client, run: Vec<u8>) -> Result<UploadedRun, Error> {
33 self::upload(client, run).await
34 }
35
36 pub fn url(&self) -> Result<Url, Error> {
38 let mut url = Url::parse("https://splits.io").unwrap();
39 url.path_segments_mut()
40 .unwrap()
41 .push(self.id.as_deref().ok_or(Error::UnidentifiableResource)?);
42 Ok(url)
43 }
44}
45
46pub async fn download(client: &Client, id: &str) -> Result<impl Deref<Target = [u8]>, Error> {
48 let mut url = Url::parse("https://splits.io/api/v4/runs").unwrap();
49 url.path_segments_mut().unwrap().push(id);
50
51 get_response(
52 client,
53 client
54 .client
55 .get(url)
56 .header(ACCEPT, "application/original-timer"),
57 )
58 .await?
59 .bytes()
60 .await
61 .map_err(|source| Error::Download { source })
62}
63
64pub async fn get(client: &Client, id: &str, historic: bool) -> Result<Run, Error> {
66 let mut url = Url::parse("https://splits.io/api/v4/runs").unwrap();
67 url.path_segments_mut().unwrap().push(id);
68 if historic {
69 url.query_pairs_mut().append_pair("historic", "1");
70 }
71
72 let ContainsRun { run } = get_json(client, client.client.get(url)).await?;
73
74 Ok(run)
75}
76
77#[derive(Debug, serde_derive::Deserialize)]
78struct UploadResponse {
79 id: Box<str>,
80 claim_token: Box<str>,
81 presigned_request: PresignedRequest,
82}
83
84#[derive(Debug, serde_derive::Deserialize)]
85struct PresignedRequest {
86 uri: Box<str>,
87 fields: PresignedRequestFields,
88}
89
90#[derive(Debug, serde_derive::Deserialize, serde_derive::Serialize)]
91struct PresignedRequestFields {
92 key: Box<str>,
93 policy: Box<str>,
94 #[serde(rename = "x-amz-credential")]
95 credential: Box<str>,
96 #[serde(rename = "x-amz-algorithm")]
97 algorithm: Box<str>,
98 #[serde(rename = "x-amz-date")]
99 date: Box<str>,
100 #[serde(rename = "x-amz-signature")]
101 signature: Box<str>,
102}
103
104#[derive(Debug)]
106pub struct UploadedRun {
107 pub id: Box<str>,
109 pub claim_token: Box<str>,
111}
112
113impl UploadedRun {
114 pub async fn get(&self, client: &Client, historic: bool) -> Result<Run, Error> {
116 Run::get(client, &self.id, historic).await
117 }
118
119 pub fn public_url(&self) -> Url {
121 let mut url = Url::parse("https://splits.io").unwrap();
122 url.path_segments_mut().unwrap().push(&self.id);
123 url
124 }
125
126 pub fn claim_url(&self) -> Url {
128 let mut url = self.public_url();
129 url.query_pairs_mut()
130 .append_pair("claim_token", &self.claim_token);
131 url
132 }
133}
134
135pub struct RunWriter(Vec<u8>);
137
138impl Write for RunWriter {
139 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
140 Write::write(&mut self.0, buf)
141 }
142 fn flush(&mut self) -> io::Result<()> {
143 Ok(())
144 }
145}
146
147pub async fn upload(client: &Client, run: Vec<u8>) -> Result<UploadedRun, Error> {
149 let UploadResponse {
150 id,
151 claim_token,
152 presigned_request: PresignedRequest { uri, fields },
153 } = get_json(client, client.client.post("https://splits.io/api/v4/runs")).await?;
154
155 get_response(
156 client,
157 client.client.post(String::from(uri)).multipart(
158 Form::new()
159 .text("key", String::from(fields.key))
160 .text("policy", String::from(fields.policy))
161 .text("x-amz-credential", String::from(fields.credential))
162 .text("x-amz-algorithm", String::from(fields.algorithm))
163 .text("x-amz-date", String::from(fields.date))
164 .text("x-amz-signature", String::from(fields.signature))
165 .part("file", Part::bytes(run)),
166 ),
167 )
168 .await?;
169
170 Ok(UploadedRun { id, claim_token })
171}