1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Raze is a library for interfacing the [BackBlaze B2 API](https://www.backblaze.com/b2/cloud-storage.html)
//!
//! Raze provides raw API bindings via the [API][api] along with some useful functions via [util]. \
//! It is highly recommended to familiarize yourself with the [official B2 documentation](https://www.backblaze.com/b2/docs/) before using this crate. \
//!
//! This crate exposes an **async** API by the use of [tokio] and [reqwest].
//!
//! Disclaimer: This library is not associated with Backblaze - Be aware of the [B2 pricing](https://www.backblaze.com/b2/cloud-storage-pricing.html) - Refer to License.md for conditions
//!
//! ## Example:
//! ```rust
//! # use raze::api::*;
//! # use raze::utils::*;
//!
//! // Authenticate, upload and delete a file
//! #[tokio::main]
//! async fn main() {
//!     let client = reqwest::ClientBuilder::new().build().unwrap();
//!     let auth = b2_authorize_account(&client, std::env::var("B2_TEST_KEY_STRING").unwrap()).await.unwrap();
//!     let upauth = b2_get_upload_url(&client, &auth, std::env::var("B2_TEST_BUCKET_ID").unwrap()).await.unwrap();
//!     let file = tokio::fs::File::open("tests/resources/simple_text_file.txt").await.unwrap();
//!     let metadata = file.metadata().await.unwrap();
//!     let size = metadata.len();
//!     let modf = metadata.modified().unwrap().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs()*1000;
//!
//!     let param = FileParameters {
//!         file_path: "simple_text_file.txt",
//!         file_size: size,
//!         content_type: None,
//!         content_sha1: Sha1Variant::HexAtEnd,
//!         last_modified_millis: modf,
//!     };
//!
//!     let stream = reader_to_stream(file);
//!     let stream = BytesStreamHashAtEnd::wrap(stream);
//!     let stream = BytesStreamThrottled::wrap(stream, 5000);
//!     
//!     let body = reqwest::Body::wrap_stream(stream);
//!     let resp1 = b2_upload_file(&client, &upauth, body, param).await.unwrap();
//!
//!     let resp2 = b2_delete_file_version(&client, &auth, &resp1.file_name, &resp1.file_id.unwrap()).await.unwrap();
//! }
//! ```

/// Raw API bindings, mostly 1:1 with official API
pub mod api;
/// Various helper functions to assist with common tasks
pub mod utils;

use serde::{Deserialize, Serialize};
use std::fmt;

#[derive(Debug)]
/// The various kinds of errors this crate may return
pub enum Error {
    /// HTTP related errors
    ReqwestError(reqwest::Error),
    /// IO related errors
    IOError(std::io::Error),
    /// (De)Serialization related errors
    SerdeError(serde_json::Error),
    /// API related errors, returned by the B2 backend
    B2Error(B2ApiError),
}

impl Error {
    /// Constructs a B2Error from a json string
    ///
    /// When we get an API error, we get an error message as a string \
    /// This will create a B2Error containing that string
    ///
    /// In case the error message is invalid/unexpected JSON, this returns a SerdeError instead
    fn from_json(error: &str) -> Error {
        let deserialized: B2ApiError = match serde_json::from_str(error) {
            Ok(v) => v,
            Err(e) => return Error::SerdeError(e),
        };
        Error::B2Error(deserialized)
    }

    /// Same as from_string but works directly on a reqwest::Response
    async fn from_response(resp: reqwest::Response) -> Error {
        match resp.text().await {
            Ok(s) => Error::from_json(&s),
            Err(e) => Error::ReqwestError(e),
        }
    }
}

// Helper method for figuring out if an error was a Serde or API error
// Takes the json-str, return either a B2 API error or a Serde error
fn handle_b2error_kinds(n: &str) -> Error {
    let _b2err: B2ApiError = match serde_json::from_str(&n) {
        Ok(v) => return Error::B2Error(v),
        Err(e) => return Error::SerdeError(e),
    };
}

#[derive(Deserialize, Serialize, Clone, Eq, PartialEq, Ord, PartialOrd)]
/// An API error, returned by the B2 backend
///
/// You typically run into this in 2 cases:
/// 1. The backend ran into some issue, e.g. timed out, your authorization expired etc. \
/// 2. Wrong use of the API, e.g. malformed args, insufficient permissions
///
/// Official documentation: [Error Handling](https://www.backblaze.com/b2/docs/calling.html#error_handling)
pub struct B2ApiError {
    /// Contains the HTTP response code
    pub status: u16,
    /// A short string name for the error, eg. "invalid_bucket_name"
    pub code: String,
    /// A human-readable error message describing what went wrong
    pub message: String,
}

impl fmt::Debug for B2ApiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "B2ApiError: error code {} - {}. Message: {}",
            self.status, self.code, self.message
        )
    }
}

impl fmt::Display for B2ApiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(
            f,
            "A B2 API Error occurred. Error code {} - {}. Error message: {}",
            self.status, self.code, self.message
        )
    }
}