Skip to main content

bunnycdn/
storage.rs

1#![allow(unused)]
2// #![deny(missing_docs)]
3use crate::serde_types::*;
4use anyhow::{Context, Result};
5// use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7// use chrono::NativeDateTime;
8use std::env;
9// use std::error::Error;
10use std::fs;
11
12const SERVER_URL: &str = "https://storage.bunnycdn.com";
13
14#[derive(Debug)]
15pub enum ResponseData {
16    StorageInfo(Vec<Option<StorageObject>>),
17    BunnyStatus(BunnyResponse),
18    HttpStatus(reqwest::StatusCode),
19}
20
21impl ResponseData {
22    pub fn print(&self) {
23        match self {
24            ResponseData::StorageInfo(storage) => {
25                let json = serde_json::to_string_pretty(&storage).unwrap();
26                println!("{}", json);
27            }
28            ResponseData::HttpStatus(status) => {
29                if !status.is_success() {
30                    error!("{}", status);
31                } else {
32                    println!("{}", status);
33                }
34            }
35            ResponseData::BunnyStatus(status) => {
36                println!("{:?}", status);
37            }
38        }
39    }
40}
41
42#[derive(Debug, Serialize, Deserialize)]
43pub struct StorageZone {
44    api_endpoint: String,
45    name: String,
46    api_key: String,
47}
48
49impl StorageZone {
50    pub fn new(name: String, api_key: String) -> Self {
51        StorageZone {
52            name,
53            api_key,
54            api_endpoint: SERVER_URL.to_string(),
55        }
56    }
57
58    pub fn set_api_endpoint(&mut self, api_endpoint: &str) -> &Self {
59        self.api_endpoint = api_endpoint.to_string();
60        self
61    }
62
63    pub fn name(&self) -> String {
64        self.name.clone()
65    }
66
67    pub async fn download_file(&self, file_path: &str, object_url: &str) -> Result<ResponseData> {
68        let request_url = format!("{}/{}/{}", self.api_endpoint, self.name, object_url);
69        trace!("{}", request_url);
70        // todo do this in chunks/ don't put whole file into memory
71        let response = reqwest::Client::new()
72            .get(&request_url)
73            .header("AccessKey", &self.api_key)
74            .header("Accept", "application/json")
75            .header("Accept-Encoding", "gzip, br")
76            .send()
77            .await?;
78
79        let http_status = response.status();
80        let response_data = ResponseData::HttpStatus(http_status);
81        if http_status.as_u16() == 200 {
82            let data = response.text().await?;
83            fs::write(file_path, data)?;
84        }
85        // Rely on http status codes than to phrase the json response. codes are the same
86        // } else {
87        //     info!("{:?}", http_status);
88        //     let json_response: BunnyResponse = response.json().await?;
89        //     response_data = ResponseData::BunnyStatus(json_response);
90        // }
91        Ok(response_data)
92    }
93
94    pub async fn upload_file(&self, file_path: &str, object_url: &str) -> Result<ResponseData> {
95        let request_url = format!("{}/{}/{}", self.api_endpoint, self.name, object_url);
96        let pwd = env::current_dir().unwrap();
97        trace!("request_url:{}, file_path:{}/{}", request_url, pwd.display(), file_path);
98        let file_contents = fs::read(file_path)?;
99        // todo do this in chunks/ don't put whole file into memory
100        let response = reqwest::Client::new()
101            .put(&request_url)
102            .header("AccessKey", &self.api_key)
103            .header("Accept-Encoding", "gzip, br")
104            .body(file_contents)
105            .send()
106            .await?;
107
108        let http_status = response.status();
109        let response_data = ResponseData::HttpStatus(http_status);
110        if http_status.as_u16() == 201 {
111            info!("{:?}", "upload successful");
112        }
113        Ok(response_data)
114    }
115
116    pub async fn delete(&self, object_url: &str) -> Result<ResponseData> {
117        let request_url = format!("{}/{}/{}", self.api_endpoint, self.name, object_url);
118        trace!("{}", request_url);
119
120        let response = reqwest::Client::new()
121            .delete(&request_url)
122            .header("AccessKey", &self.api_key)
123            .header("Accept-Encoding", "gzip, br")
124            .send()
125            .await?;
126
127        let response_data = ResponseData::HttpStatus(response.status());
128        // response_data.canonical_reason()
129        // let json_response = BunnyResponse {http_code:http_status.as_u16(), Some(message:http_status.canonical_reason()).to_string()};
130
131        // info!("{:?}", response_data.HttpStatus.as_u16());
132        Ok(response_data)
133    }
134
135    pub async fn get_objects(&self, directory_url: &str) -> Result<ResponseData> {
136        let request_url = format!("{}/{}/{}", self.api_endpoint, self.name, directory_url);
137        trace!("{:?}", request_url);
138
139        let response = reqwest::Client::new()
140            .get(&request_url)
141            .header("AccessKey", &self.api_key)
142            .header("Accept", "application/json")
143            .header("Accept-Encoding", "gzip, br")
144            .send()
145            .await?;
146
147        let http_status = response.status();
148        trace!("{}", http_status);
149        // println!("{}", http_status);
150
151        // let mut data = ResponseData::BunnyStatus(BunnyResponse {http_code:http_status.as_u16(), ..Default::default()});
152        let mut response_data = ResponseData::HttpStatus(http_status);
153        // let json_response = BunnyResponse {http_code:http_status.as_u16(), message:"".to_string()};
154        if http_status.as_u16() == 200 {
155            // let data: Vec<Option<StorageObject>> =
156            //     response.json().await.expect("Can't parse JSON! Make sure to select a directory not a file!");
157            let data = response.text().await?;
158            trace!("{:?}", data);
159            // println!("{:?}", data);
160            let data = serde_json::from_str::<Vec<Option<StorageObject>>>(&data)
161                .context("Can't parse JSON! Make sure to select a directory not a file")?;
162            trace!("{:?}", data);
163            // println!("{:?}", data);
164            response_data = ResponseData::StorageInfo(data);
165            trace!("{:?}", response_data);
166        } else if http_status.as_u16() == 404 {
167            response_data = match response.json().await {
168                Ok(data) => ResponseData::BunnyStatus(data), // return json if there is any
169                Err(_) => response_data,                     // return HTTP status code
170            };
171            trace!("{:?}", response_data);
172        } else {
173            let data = response.text().await?;
174            trace!("{} - {:?}", http_status, data);
175        }
176        Ok(response_data)
177    }
178}