ffsend_api/action/
info.rs1use std::cmp::max;
2
3use reqwest::Error as ReqwestError;
4use thiserror::Error;
5
6use crate::api::data::{Error as DataError, OwnedData};
7use crate::api::nonce::{request_nonce, NonceError};
8use crate::api::request::{ensure_success, ResponseError};
9use crate::api::url::UrlBuilder;
10use crate::client::Client;
11use crate::file::remote_file::RemoteFile;
12
13pub struct Info<'a> {
17 file: &'a RemoteFile,
19
20 nonce: Vec<u8>,
23}
24
25impl<'a> Info<'a> {
26 pub fn new(file: &'a RemoteFile, nonce: Option<Vec<u8>>) -> Self {
28 Self {
29 file,
30 nonce: nonce.unwrap_or_default(),
31 }
32 }
33
34 pub fn invoke(mut self, client: &Client) -> Result<InfoResponse, Error> {
36 if self.nonce.is_empty() {
38 self.nonce = self.fetch_auth_nonce(client)?;
39 }
40
41 let data = OwnedData::from(InfoData::new(), &self.file)
43 .map_err(|err| -> PrepareError { err.into() })?;
44
45 self.fetch_info(client, &data)
47 }
48
49 fn fetch_auth_nonce(&self, client: &Client) -> Result<Vec<u8>, Error> {
51 request_nonce(client, UrlBuilder::download(self.file, false)).map_err(|err| err.into())
52 }
53
54 fn fetch_info(
56 &self,
57 client: &Client,
58 data: &OwnedData<InfoData>,
59 ) -> Result<InfoResponse, Error> {
60 let url = UrlBuilder::api_info(self.file);
62 let response = client
63 .post(url)
64 .json(&data)
65 .send()
66 .map_err(|_| InfoError::Request)?;
67
68 ensure_success(&response)?;
70
71 let response: InfoResponse = match response.json() {
73 Ok(response) => response,
74 Err(err) => return Err(InfoError::Decode(err).into()),
75 };
76
77 Ok(response)
78 }
79}
80
81#[derive(Debug, Serialize, Default)]
85pub struct InfoData {}
86
87impl InfoData {
88 pub fn new() -> Self {
90 InfoData::default()
91 }
92}
93
94#[derive(Debug, Deserialize)]
96pub struct InfoResponse {
97 #[serde(rename = "dlimit")]
99 download_limit: usize,
100
101 #[serde(rename = "dtotal")]
103 download_count: usize,
104
105 #[serde(rename = "ttl")]
107 ttl: u64,
108}
109
110impl InfoResponse {
111 pub fn download_count(&self) -> usize {
113 self.download_count
114 }
115
116 pub fn download_limit(&self) -> usize {
118 self.download_limit
119 }
120
121 pub fn download_left(&self) -> usize {
123 max(self.download_limit() - self.download_count(), 0)
124 }
125
126 pub fn ttl_millis(&self) -> u64 {
129 self.ttl
130 }
131}
132
133#[derive(Error, Debug)]
134pub enum Error {
135 #[error("failed to prepare the action")]
137 Prepare(#[from] PrepareError),
138
139 #[error("the file has expired or did never exist")]
142 Expired,
143
144 #[error("failed to send the file info request")]
146 Info(#[from] InfoError),
147}
148
149impl From<NonceError> for Error {
150 fn from(err: NonceError) -> Error {
151 match err {
152 NonceError::Expired => Error::Expired,
153 err => Error::Prepare(PrepareError::Auth(err)),
154 }
155 }
156}
157
158impl From<ResponseError> for Error {
159 fn from(err: ResponseError) -> Error {
160 match err {
161 ResponseError::Expired => Error::Expired,
162 err => Error::Info(InfoError::Response(err)),
163 }
164 }
165}
166
167#[derive(Debug, Error)]
168pub enum InfoDataError {
169 #[error("")]
173 Owned(#[from] DataError),
174}
175
176#[derive(Error, Debug)]
177pub enum PrepareError {
178 #[error("failed to authenticate")]
180 Auth(#[from] NonceError),
181
182 #[error("invalid parameters")]
185 InfoData(#[from] InfoDataError),
186}
187
188impl From<DataError> for PrepareError {
189 fn from(err: DataError) -> PrepareError {
190 PrepareError::InfoData(InfoDataError::Owned(err))
191 }
192}
193
194#[derive(Error, Debug)]
195pub enum InfoError {
196 #[error("failed to send file info request")]
198 Request,
199
200 #[error("bad response from server while fetching file info")]
202 Response(#[from] ResponseError),
203
204 #[error("failed to decode info response")]
207 Decode(#[from] ReqwestError),
208}