Skip to main content

cloud_storage_rs/
lib.rs

1//! # MOVED TO [cloud-storage](https://docs.rs/cloud-storage)
2//! 
3//! This crate aims to simplify interacting with the Google Cloud Storage JSON API. Use it until
4//! Google releases a Cloud Storage Client Library for Rust. Shoutout to
5//! [MyEmma](https://myemma.io/) for funding this free and open source project.
6//!
7//! Google Cloud Storage is a product by Google that allows for cheap file storage, with a
8//! relatively sophisticated API. The idea is that storage happens in `Bucket`s, which are
9//! filesystems with a globally unique name. You can make as many of these `Bucket`s as you like!
10//!
11//! This project talks to Google using a `Service Account`. A service account is an account that you
12//! must create in the [cloud storage console](https://console.cloud.google.com/). When the account
13//! is created, you can download the file `service-account-********.json`. Store this file somewhere
14//! on your machine, and place the path to this file in the environment parameter `SERVICE_ACCOUNT`.
15//! Environment parameters declared in the `.env` file are also registered. The service account can
16//! then be granted `Roles` in the cloud storage console. The roles required for this project to
17//! function are `Service Account Token Creator` and `Storage Object Admin`.
18//!
19//! # Quickstart
20//! Add the following line to your `Cargo.toml`
21//! ```toml
22//! [dependencies]
23//! cloud-storage = "0.4"
24//! ```
25//! The two most important concepts are [Buckets](bucket/struct.Bucket.html), which represent
26//! file systems, and [Objects](object/struct.Object.html), which represent files.
27//!
28//! ## Examples:
29//! Creating a new Bucket in Google Cloud Storage:
30//! ```rust
31//! # use cloud_storage::{Bucket, NewBucket};
32//! let bucket = Bucket::create(&NewBucket {
33//!     name: "doctest-bucket".to_string(),
34//!     ..Default::default()
35//! }).unwrap();
36//! # bucket.delete();
37//! ```
38//! Connecting to an existing Bucket in Google Cloud Storage:
39//! ```no_run
40//! # use cloud_storage::Bucket;
41//! let bucket = Bucket::read("mybucket").unwrap();
42//! ```
43//! Read a file from disk and store it on googles server:
44//! ```rust,no_run
45//! # use cloud_storage::Object;
46//! # use std::fs::File;
47//! # use std::io::Read;
48//! let mut bytes: Vec<u8> = Vec::new();
49//! for byte in File::open("myfile.txt").unwrap().bytes() {
50//!     bytes.push(byte.unwrap())
51//! }
52//! Object::create("mybucket", &bytes, "myfile.txt", "text/plain");
53//! ```
54//! Renaming/moving a file
55//! ```rust,no_run
56//! # use cloud_storage::Object;
57//! let mut object = Object::read("mybucket", "myfile").unwrap();
58//! object.name = "mybetterfile".to_string();
59//! object.update().unwrap();
60//! ```
61//! Removing a file
62//! ```rust,no_run
63//! # use cloud_storage::Object;
64//! Object::delete("mybucket", "myfile");
65//! ```
66#![forbid(unsafe_code, missing_docs)]
67
68/// Contains objects as represented by Google, to be used for serialization and deserialization.
69mod error;
70mod resources;
71mod token;
72
73pub use crate::error::*;
74use crate::resources::service_account::ServiceAccount;
75pub use crate::resources::{
76    bucket::{Bucket, NewBucket},
77    object::Object,
78    *,
79};
80use crate::token::Token;
81use std::sync::Mutex;
82
83lazy_static::lazy_static! {
84    /// Static `Token` struct that caches
85    static ref TOKEN_CACHE: Mutex<Token> = Mutex::new(Token::new(
86        "https://www.googleapis.com/auth/devstorage.full_control",
87    ));
88
89    static ref IAM_TOKEN_CACHE: Mutex<Token> = Mutex::new(Token::new(
90        "https://www.googleapis.com/auth/iam"
91    ));
92
93    /// The struct is the parsed service account json file. It is publicly exported to enable easier
94    /// debugging of which service account is currently used. It is of the type
95    /// [ServiceAccount](service_account/struct.ServiceAccount.html).
96    pub static ref SERVICE_ACCOUNT: ServiceAccount = ServiceAccount::get();
97}
98
99const BASE_URL: &'static str = "https://www.googleapis.com/storage/v1";
100
101fn get_headers() -> Result<reqwest::header::HeaderMap, Error> {
102    let mut result = reqwest::header::HeaderMap::new();
103    let mut guard = TOKEN_CACHE.lock().unwrap();
104    let token = guard.get()?;
105    result.insert(
106        reqwest::header::AUTHORIZATION,
107        format!("Bearer {}", token).parse().unwrap(),
108    );
109    Ok(result)
110}
111
112fn from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
113where
114    T: std::str::FromStr,
115    T::Err: std::fmt::Display,
116    D: serde::Deserializer<'de>,
117{
118    use serde::de::Deserialize;
119    let s = String::deserialize(deserializer)?;
120    T::from_str(&s).map_err(serde::de::Error::custom)
121}
122
123fn from_str_opt<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
124where
125    T: std::str::FromStr,
126    T::Err: std::fmt::Display,
127    D: serde::Deserializer<'de>,
128{
129    let s: Result<serde_json::Value, _> = serde::Deserialize::deserialize(deserializer);
130    println!("{:?}", s);
131    match s {
132        Ok(serde_json::Value::String(s)) => T::from_str(&s)
133            .map_err(serde::de::Error::custom)
134            .map(Option::from),
135        Ok(serde_json::Value::Number(num)) => T::from_str(&num.to_string())
136            .map_err(serde::de::Error::custom)
137            .map(Option::from),
138        Ok(_value) => Err(serde::de::Error::custom("Incorrect type")),
139        Err(_) => Ok(None),
140    }
141}
142
143#[cfg(test)]
144fn read_test_bucket() -> Bucket {
145    dotenv::dotenv().ok();
146    let name = std::env::var("TEST_BUCKET").unwrap();
147    match Bucket::read(&name) {
148        Ok(bucket) => bucket,
149        Err(_not_found) => Bucket::create(&NewBucket {
150            name,
151            ..Default::default()
152        })
153        .unwrap(),
154    }
155}
156
157// since all tests run in parallel, we need to make sure we do not create multiple buckets with
158// the same name in each test.
159#[cfg(test)]
160fn create_test_bucket(name: &str) -> Bucket {
161    dotenv::dotenv().ok();
162    let base_name = std::env::var("TEST_BUCKET").unwrap();
163    let name = format!("{}-{}", base_name, name);
164    let new_bucket = NewBucket {
165        name,
166        ..Default::default()
167    };
168    match Bucket::create(&new_bucket) {
169        Ok(bucket) => bucket,
170        Err(_alread_exists) => Bucket::read(&new_bucket.name).unwrap(),
171    }
172}