mirror of
https://github.com/Drop-OSS/dropbreak.git
synced 2026-07-18 09:57:53 -04:00
+3
-13
@@ -4,21 +4,11 @@ rust:
|
||||
- nightly
|
||||
- beta
|
||||
- stable
|
||||
before_script:
|
||||
- |
|
||||
pip install 'travis-cargo<0.2' --user &&
|
||||
export PATH=$HOME/.local/bin:$PATH
|
||||
script:
|
||||
- |
|
||||
travis-cargo build &&
|
||||
travis-cargo test &&
|
||||
rm /tmp/*
|
||||
travis-cargo test -- --no-default-features --features yaml &&
|
||||
travis-cargo bench &&
|
||||
travis-cargo --only stable doc
|
||||
after_success:
|
||||
- travis-cargo --only stable doc-upload
|
||||
cargo build --features yaml_enc,bin_enc,ron_enc &&
|
||||
cargo test --features yaml_enc,bin_enc,ron_enc &&
|
||||
cargo doc --features yaml_enc,bin_enc,ron_enc
|
||||
env:
|
||||
global:
|
||||
- TRAVIS_CARGO_NIGHTLY_FEATURE=" "
|
||||
- secure: RFX1Ke3uk8ZdrAGwKPP7FXqQfC6bkGMDzV5ut9s5da6ClvoafosJF4RBvXi5j8KhQP9Kyow7Z8IwtTExNHMIjlE0MguHsYdFBdkj9HdjrqBQHThx5vMOq3VY3bNq9ah83Q6idoTTpQvoZ380BSHznHz8E3rmKGbEA0Z1NigWlT8DFfgqI5nTa64/bvD8IzMTjkLax4pq1SjsG8v+UgwNxkuYE6Yl/mpQJ/xTCGYiFHLKk4EW8z2I5DcK/XqY+w42uFDP2BRvXdzuS937A0lGZtAiK3sKJCBDq/2Ulkz4+onkAo9QT0k9Eea9dSoxXDk3RJHzDb5wLANtubxAWyTKiyTlO3I81ogPmHevSv3xDXXwLBjPxwwIRI1qt/01Nod4yIT4fWfIxcW+rY/W5P4v5QbvAJ079v8be1lnwCEFP55WXfTFEypvKUmNpCTeY9nAKiLywpQuibi/TuHmVH+R9xWlJaXrJ8+fM+dkl57V7AnIKs3nnYmE/c5guUakJ46QVZZtSQRcFfD0QeZPM5WaqAjiZ52eP4y/Mi5Rjg4/noLjXoR3YWYxWZsT5b7U2hv/wEMTzERgHev3aPZttO7gr3ug/FDv0TP6PKPCLd/i+g0w4RKRGArcAXIBWHdeEhfOYuC8Q93gl3d/qutvgGdWF4sdBpaPRcwmaWD+FoEWjWQ=
|
||||
|
||||
+31
-14
@@ -1,29 +1,46 @@
|
||||
[package]
|
||||
authors = ["Marcel Müller <neikos@neikos.email>"]
|
||||
description = "A single file database"
|
||||
documentation = "http://neikos.me/rustbreak/rustbreak/index.html"
|
||||
description = "A modular and configurable database"
|
||||
documentation = "https://docs.rs/rustbreak"
|
||||
homepage = "https://github.com/TheNeikos/rustbreak"
|
||||
keywords = ["database", "simple", "fast", "daybreak", "rustbreak"]
|
||||
keywords = ["database", "simple", "fast", "rustbreak"]
|
||||
license = "MPL-2.0"
|
||||
name = "rustbreak"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/TheNeikos/rustbreak"
|
||||
version = "1.4.0"
|
||||
version = "2.0.0-rc2"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[dependencies]
|
||||
bincode = { version = "0.8", optional = true }
|
||||
serde_yaml = { version = "0.7", optional = true }
|
||||
ron = { version = "0.1.3", optional = true }
|
||||
fs2 = "0.4"
|
||||
quick-error = "1.1.0"
|
||||
failure = "0.1.1"
|
||||
serde = "1"
|
||||
|
||||
[dependencies.ron]
|
||||
optional = true
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies.base64]
|
||||
optional = true
|
||||
version = "0.9.0"
|
||||
|
||||
[dependencies.bincode]
|
||||
optional = true
|
||||
version = "1"
|
||||
|
||||
[dependencies.serde_yaml]
|
||||
optional = true
|
||||
version = "0.7"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "2.1"
|
||||
lazy_static = "0.2.1"
|
||||
lazy_static = "1"
|
||||
serde_derive = "1"
|
||||
tempfile = "3"
|
||||
|
||||
[features]
|
||||
default = ["bin"]
|
||||
bin = ["bincode"]
|
||||
yaml = ["serde_yaml"]
|
||||
default = []
|
||||
ron_enc = ["ron"]
|
||||
bin_enc = ["bincode", "base64"]
|
||||
yaml_enc = ["serde_yaml"]
|
||||
|
||||
|
||||
@@ -26,73 +26,120 @@ Features
|
||||
|
||||
- Simple To Use, Fast, Secure
|
||||
- Threadsafe
|
||||
- Key/Value Storage
|
||||
- bincode or yaml storage
|
||||
- Serde compatible storage (ron, bincode, or yaml included)
|
||||
|
||||
Quickstart
|
||||
----------
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies.rustbreak]
|
||||
version = "2"
|
||||
features = ["ron_enc"] # You can also use "yaml_enc" or "bin_enc"
|
||||
# Check the documentation to add your own!
|
||||
```
|
||||
|
||||
```rust
|
||||
# extern crate failure;
|
||||
# extern crate rustbreak;
|
||||
# use std::collections::HashMap;
|
||||
use rustbreak::{MemoryDatabase, deser::Ron};
|
||||
|
||||
# fn main() {
|
||||
# let func = || -> Result<(), failure::Error> {
|
||||
let db = MemoryDatabase::<HashMap<u32, String>, Ron>::memory(HashMap::new())?;
|
||||
|
||||
println!("Writing to Database");
|
||||
db.write(|db| {
|
||||
db.insert(0, String::from("world"));
|
||||
db.insert(1, String::from("bar"));
|
||||
});
|
||||
|
||||
db.read(|db| {
|
||||
// db.insert("foo".into(), String::from("bar"));
|
||||
// The above line will not compile since we are only reading
|
||||
println!("Hello: {:?}", db.get(&0));
|
||||
})?;
|
||||
# return Ok(()); };
|
||||
# func().unwrap();
|
||||
# }
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Usage is quite simple:
|
||||
|
||||
- Create/open a database using `Database::open`
|
||||
- You can specify the kind of Key you want using this Syntax:
|
||||
`Database::<Key>::open`
|
||||
- `Insert`/`Retrieve` data from the Database
|
||||
- Don't forget to run `flush` periodically
|
||||
- Create/open a database using one of the Database constructors:
|
||||
- Create a `FileDatabase` with `FileDatabase::from_path`
|
||||
- Create a `MemoryDatabase` with `MemoryDatabase::memory`
|
||||
- Create a `Database` with `Database::from_parts`
|
||||
- `Write`/`Read` data from the Database
|
||||
- Don't forget to run `save` periodically, or whenever it makes sense.
|
||||
- You can save in parallel to using the Database. However you will lock
|
||||
write acess while it is being written to storage.
|
||||
|
||||
```rust
|
||||
use rustbreak::Database;
|
||||
# use std::collections::HashMap;
|
||||
use rustbreak::{MemoryDatabase, deser::Ron};
|
||||
|
||||
fn main() {
|
||||
let db = Database::open("my_contacts").unwrap();
|
||||
let db = MemoryDatabase::<HashMap<String, String>, Ron>::memory(HashMap::new(), Ron);
|
||||
|
||||
db.insert("Lapfox", "lapfoxtrax.com").unwrap();
|
||||
db.insert("Rust", "github.com/rust-lang/rust").unwrap();
|
||||
println!("Writing to Database");
|
||||
db.write(|db| {
|
||||
db.insert("hello".into(), String::from("world"));
|
||||
db.insert("foo".into(), String::from("bar"));
|
||||
});
|
||||
|
||||
// we need to be explicit about the kind of type we want as println! is
|
||||
// generic
|
||||
let rust : String = db.retrieve("Rust").unwrap();
|
||||
println!("You can find Rust at: {}", rust);
|
||||
db.flush().unwrap();
|
||||
}
|
||||
db.read(|db| {
|
||||
// db.insert("foo".into(), String::from("bar"));
|
||||
// The above line will not compile since we are only reading
|
||||
println!("Hello: {:?}", db.get("hello"));
|
||||
});
|
||||
```
|
||||
|
||||
## Encodings
|
||||
|
||||
The following parts explain how to enable the respective features. You can also
|
||||
enable several at the same time.
|
||||
|
||||
### Yaml
|
||||
|
||||
If you would like to use yaml instead of bincode to perhaps read or modify the
|
||||
database in an editor you can use it like this:
|
||||
|
||||
- Disable default features
|
||||
- Specify yaml as a feature
|
||||
If you would like to use yaml you need to specify `yaml_enc` as a feature:
|
||||
|
||||
```toml
|
||||
[dependencies.rustbreak]
|
||||
version = "1"
|
||||
default-features = false
|
||||
features = ["yaml"]
|
||||
features = ["yaml_enc"]
|
||||
```
|
||||
|
||||
You can now use `rustbreak::deser::Yaml` as deserialization struct.
|
||||
|
||||
### Ron
|
||||
|
||||
If you would like to use [`ron`](https://github.com/ron-rs/ron) instead of bincode:
|
||||
|
||||
- Disable default features
|
||||
- Specify ron_enc as a feature
|
||||
If you would like to use [`ron`](https://github.com/ron-rs/ron) you need to
|
||||
specify `ron_enc` as a feature:
|
||||
|
||||
```toml
|
||||
[dependencies.rustbreak]
|
||||
version = "1"
|
||||
default-features = false
|
||||
features = ["ron_enc"]
|
||||
```
|
||||
|
||||
How it works
|
||||
------------
|
||||
You can now use `rustbreak::deser::Ron` as deserialization struct.
|
||||
|
||||
Internally the Database holds a Hashmap behind a RwLock.
|
||||
This Hashmap is then written to/read from and safely casted to the requested
|
||||
type. This works thanks to encoding/decoding traits.
|
||||
### Bincode
|
||||
|
||||
If you would like to use bincode you need to specify `bin_enc` as a feature:
|
||||
|
||||
```toml
|
||||
[dependencies.rustbreak]
|
||||
version = "1"
|
||||
features = ["bin_enc"]
|
||||
```
|
||||
|
||||
You can now use `rustbreak::deser::Bincode` as deserialization struct.
|
||||
|
||||
|
||||
[doc]:http://neikos.me/rustbreak/rustbreak/index.html
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// This just reads an example configuration.
|
||||
// If it doesn't find one, it uses your default configuration
|
||||
//
|
||||
// You can create one by writing this file to `/tmp/config.ron`:
|
||||
// ```
|
||||
// ---
|
||||
// user_path: /tmp/nope
|
||||
// allow_overwrite: true
|
||||
// ```
|
||||
//
|
||||
|
||||
extern crate rustbreak;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::default::Default;
|
||||
use rustbreak::FileDatabase;
|
||||
use rustbreak::deser::Ron;
|
||||
|
||||
type DB = FileDatabase<Config, Ron>;
|
||||
|
||||
lazy_static! {
|
||||
static ref CONFIG: DB = {
|
||||
let db = FileDatabase::from_path("/tmp/config.ron", Config::default())
|
||||
.expect("Create database from path");
|
||||
db.load().expect("Config to load");
|
||||
db
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
struct Config {
|
||||
user_path: PathBuf,
|
||||
allow_overwrite: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
user_path: PathBuf::from("/tmp"),
|
||||
allow_overwrite: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _conf : Config = CONFIG.read(|conf| {
|
||||
conf.clone()
|
||||
}).expect("Reading configuration");
|
||||
|
||||
let (user_path, allow_overwrite) =
|
||||
CONFIG.read(|conf| (conf.user_path.clone(), conf.allow_overwrite.clone())).expect("Read config");
|
||||
|
||||
println!("The current configuration is: {:?} and {}", user_path, allow_overwrite);
|
||||
}
|
||||
+45
-34
@@ -1,46 +1,57 @@
|
||||
extern crate rustbreak;
|
||||
#[macro_use] extern crate lazy_static;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
extern crate failure;
|
||||
|
||||
use rustbreak::{Database, Result as BreakResult};
|
||||
use rustbreak::FileDatabase;
|
||||
use rustbreak::deser::Ron;
|
||||
|
||||
lazy_static! {
|
||||
static ref DB: Database<String> = {
|
||||
Database::open("music").unwrap()
|
||||
};
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
enum Country {
|
||||
Italy, UnitedKingdom
|
||||
}
|
||||
|
||||
type Artist = String;
|
||||
type Album = String;
|
||||
type AlbumArtist = (Album, Artist);
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
struct Person {
|
||||
name: String,
|
||||
country: Country,
|
||||
}
|
||||
|
||||
fn do_main() -> Result<(), failure::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
let db = FileDatabase::<HashMap<String, Person>, Ron>::from_path("test.ron", HashMap::new())?;
|
||||
|
||||
println!("Writing to Database");
|
||||
db.write(|db| {
|
||||
db.insert("john".into(), Person {
|
||||
name: String::from("John Andersson"),
|
||||
country: Country::Italy
|
||||
});
|
||||
db.insert("fred".into(), Person {
|
||||
name: String::from("Fred Johnson"),
|
||||
country: Country::UnitedKingdom
|
||||
});
|
||||
println!("Entries: \n{:#?}", db);
|
||||
})?;
|
||||
|
||||
println!("Syncing Database");
|
||||
db.save()?;
|
||||
|
||||
println!("Loading Database");
|
||||
db.load()?;
|
||||
|
||||
println!("Reading from Database");
|
||||
db.read(|db| {
|
||||
println!("Results:");
|
||||
println!("{:#?}", db);
|
||||
})?;
|
||||
|
||||
fn add_album_to_artist(name: Artist, album: Album) -> BreakResult<()> {
|
||||
let mut lock = try!(DB.lock());
|
||||
let mut map: Vec<Album> = lock.retrieve(&name).unwrap_or_else(|_| vec![]);
|
||||
map.push(album);
|
||||
try!(lock.insert(&name, map));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_albums(name: &str) -> BreakResult<Vec<Album>> {
|
||||
DB.retrieve(name)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let albums = [
|
||||
("The Queenstons", "What you do EP"),
|
||||
("The Queenstons", "Figurehead"),
|
||||
("The Queenstons", "Undertones"),
|
||||
("System of a Down", "Toxicity II"),
|
||||
("System of a Down", "Mezmerize"),
|
||||
];
|
||||
|
||||
for &(artist, album) in albums.iter() {
|
||||
add_album_to_artist(artist.to_owned(), album.to_owned()).unwrap();
|
||||
if let Err(e) = do_main() {
|
||||
eprintln!("An error has occurred at: \n{}", e.backtrace());
|
||||
::std::process::exit(1);
|
||||
}
|
||||
|
||||
for al in get_albums("The Queenstons").unwrap() {
|
||||
println!("{}", al);
|
||||
}
|
||||
|
||||
DB.flush().unwrap();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/* This file is a complete work in progress! And is meant to illustrate a possible way of
|
||||
* migrating databases with Rustbreak.
|
||||
*
|
||||
* Currently the major challenge is to statically define the upgrade process.
|
||||
*
|
||||
* This is currently done by using the `upgrading_macro`.
|
||||
*
|
||||
* The idea is to read the 'version' tag in the file, load it into a simple tag struct and
|
||||
* read the version from there. This version is then used to dynamically get the corresponding
|
||||
* struct type. This then gets converted to the latest version.
|
||||
*/
|
||||
|
||||
|
||||
extern crate rustbreak;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
||||
use rustbreak::FileDatabase;
|
||||
use rustbreak::deser::Ron;
|
||||
|
||||
mod data {
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum Version {
|
||||
First, Second, Third,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Versioning {
|
||||
pub version: Version
|
||||
}
|
||||
|
||||
pub mod v1 {
|
||||
use data::Version;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Players {
|
||||
version: Version,
|
||||
pub players: HashMap<i32, String>,
|
||||
}
|
||||
|
||||
impl Players {
|
||||
pub fn new() -> Players {
|
||||
Players {
|
||||
version: Version::First,
|
||||
players: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v2 {
|
||||
use data::Version;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Player {
|
||||
pub name: String,
|
||||
pub level: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Players {
|
||||
version: Version,
|
||||
pub players: HashMap<i32, Player>,
|
||||
}
|
||||
|
||||
impl Players {
|
||||
pub fn new() -> Players {
|
||||
Players {
|
||||
version: Version::Second,
|
||||
players: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod v3 {
|
||||
use data::Version;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Player {
|
||||
pub name: String,
|
||||
pub score: i64
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Players {
|
||||
version: Version,
|
||||
pub players: HashMap<i32, Player>,
|
||||
}
|
||||
|
||||
impl Players {
|
||||
pub fn new() -> Players {
|
||||
Players {
|
||||
version: Version::Third,
|
||||
players: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_v1_v2(input: v1::Players) -> v2::Players {
|
||||
let mut new = v2::Players::new();
|
||||
for (key, player) in input.players {
|
||||
new.players.insert(key,
|
||||
v2::Player {
|
||||
name: player,
|
||||
level: 1
|
||||
});
|
||||
}
|
||||
|
||||
new
|
||||
}
|
||||
|
||||
pub fn convert_v2_v3(input: v2::Players) -> v3::Players {
|
||||
let mut new = v3::Players::new();
|
||||
for (key, player) in input.players {
|
||||
new.players.insert(key,
|
||||
v3::Player {
|
||||
name: player.name,
|
||||
score: 0
|
||||
});
|
||||
}
|
||||
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! migration_rules {
|
||||
( $name:ident -> $res:ty { $( $t:ty => $conv:path ),* $(,)* } ) => {
|
||||
fn $name<T: 'static>(input: T) -> Option<$res> {
|
||||
fn inner(mut input: Box<std::any::Any>) -> Option<$res> {
|
||||
println!("We have: {:#?}", input);
|
||||
$(
|
||||
{
|
||||
let mut maybe_in = None;
|
||||
if let Some(out) = input.downcast_ref::<$t>() {
|
||||
maybe_in = Some(Box::new($conv(out.clone())));
|
||||
}
|
||||
|
||||
if let Some(inp) = maybe_in {
|
||||
input = inp;
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
if let Ok(out) = input.downcast::<$res>() {
|
||||
return Some(*out);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
inner(Box::new(input))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
migration_rules! {
|
||||
update_database -> data::v3::Players {
|
||||
data::v1::Players => data::convert_v1_v2,
|
||||
data::v2::Players => data::convert_v2_v3,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_version() -> data::Version {
|
||||
let db = FileDatabase::<data::Versioning, Ron>::from_path("migration.ron", data::Versioning { version: data::Version::First }).unwrap();
|
||||
db.load().unwrap();
|
||||
db.read(|ver| ver.version).unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use data::v3::Players;
|
||||
|
||||
let version = get_version();
|
||||
|
||||
|
||||
// Let's check if there are any updates
|
||||
let updated_data = update_database(database.get_data(false).unwrap()).unwrap();
|
||||
let database = FileDatabase::<Players, Ron>::from_path("migration.ron", updated_data).unwrap();
|
||||
|
||||
println!("{:#?}", database);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
authors = ["Marcel Müller <neikos@neikos.email>"]
|
||||
name = "server"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
rocket = "0.3.3"
|
||||
rocket_codegen = "0.3.3"
|
||||
serde = "1.0.23"
|
||||
serde_derive = "1.0.23"
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
default-features = false
|
||||
features = ["handlebars_templates"]
|
||||
version = "0.3.3"
|
||||
|
||||
[dependencies.rustbreak]
|
||||
path = "../.."
|
||||
features = ["ron_enc"]
|
||||
@@ -0,0 +1,26 @@
|
||||
(
|
||||
pastes: [
|
||||
(
|
||||
user: "neikos@neikos.email",
|
||||
body: "Hello :)",
|
||||
),
|
||||
(
|
||||
user: "neikos@neikos.email",
|
||||
body: "This works great!",
|
||||
),
|
||||
(
|
||||
user: "neikos@neikos.email",
|
||||
body: "Hehe",
|
||||
),
|
||||
(
|
||||
user: "neikos@neikos.email",
|
||||
body: "Hello World :)",
|
||||
),
|
||||
],
|
||||
users: {
|
||||
"neikos@neikos.email": (
|
||||
username: "neikos@neikos.email",
|
||||
password: "asdf",
|
||||
),
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,159 @@
|
||||
#![feature(plugin, decl_macro, custom_derive)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rustbreak;
|
||||
extern crate rocket;
|
||||
extern crate rocket_contrib;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rocket::{State, Outcome};
|
||||
use rocket::http::{Cookies, Cookie};
|
||||
use rocket::request::{self, Request, FromRequest, Form};
|
||||
use rocket::response::Redirect;
|
||||
use rocket_contrib::Template;
|
||||
use rustbreak::FileDatabase;
|
||||
use rustbreak::deser::Ron;
|
||||
|
||||
// We create a type alias so that we always associate the same types to it
|
||||
type DB = FileDatabase<ServerData, Ron>;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
struct Paste {
|
||||
user: String,
|
||||
body: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, FromForm)]
|
||||
struct NewPaste {
|
||||
body: String
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, FromForm)]
|
||||
struct User {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
impl <'a, 'r> FromRequest<'a, 'r> for User {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<User, ()> {
|
||||
let mut cookies = request.cookies();
|
||||
let db = request.guard::<State<DB>>()?;
|
||||
match cookies.get_private("user_id") {
|
||||
Some(cookie) => {
|
||||
let mut outcome = Outcome::Forward(());
|
||||
let _ = db.read(|db| {
|
||||
if db.users.contains_key(cookie.value()) {
|
||||
outcome = Outcome::Success(db.users.get(cookie.value()).unwrap().clone());
|
||||
}
|
||||
});
|
||||
return outcome;
|
||||
}
|
||||
None => return Outcome::Forward(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
struct ServerData {
|
||||
pastes: Vec<Paste>,
|
||||
users: HashMap<String, User>
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct TemplateData {
|
||||
pastes: Vec<Paste>,
|
||||
logged_in: bool,
|
||||
user: String
|
||||
}
|
||||
|
||||
|
||||
// Routing
|
||||
|
||||
#[get("/")]
|
||||
fn index(db: State<DB>, user: Option<User>) -> Template {
|
||||
let mut data = TemplateData {
|
||||
logged_in: user.is_some(),
|
||||
user: user.map(|u| u.username).unwrap_or_else(|| String::new()),
|
||||
pastes: vec![],
|
||||
};
|
||||
let _ = db.read(|db| {
|
||||
data.pastes = db.pastes.clone();
|
||||
});
|
||||
|
||||
return Template::render("index", &data);
|
||||
}
|
||||
|
||||
#[post("/register", data = "<req_user>")]
|
||||
fn post_register(db: State<DB>, req_user: Form<User>, mut cookies: Cookies) -> Redirect {
|
||||
let user = req_user.into_inner();
|
||||
let _ = db.write(|db| {
|
||||
if db.users.contains_key(&user.username) {
|
||||
return;
|
||||
}
|
||||
db.users.insert(user.username.clone(), user.clone());
|
||||
cookies.add_private(
|
||||
Cookie::build("user_id", user.username.clone()).http_only(true).finish()
|
||||
);
|
||||
});
|
||||
let _ = db.save();
|
||||
|
||||
Redirect::to("/")
|
||||
}
|
||||
|
||||
#[post("/login", data = "<req_user>")]
|
||||
fn post_login(db: State<DB>, req_user: Form<User>, mut cookies: Cookies) -> Redirect {
|
||||
let user = req_user.into_inner();
|
||||
let _ = db.read(|db| {
|
||||
match db.users.get(&user.username) {
|
||||
Some(u) => {
|
||||
if u.password == user.password {
|
||||
cookies.add_private(
|
||||
Cookie::build("user_id", user.username.clone()).http_only(true).finish()
|
||||
);
|
||||
}
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
});
|
||||
|
||||
Redirect::to("/")
|
||||
}
|
||||
|
||||
#[post("/paste", data = "<paste>")]
|
||||
fn post_paste(db: State<DB>, user: User, paste: Form<NewPaste>) -> Redirect {
|
||||
let body : String = paste.into_inner().body.clone();
|
||||
let _ = db.write(|db| {
|
||||
let paste = Paste {
|
||||
body: body,
|
||||
user: user.username.clone(),
|
||||
};
|
||||
db.pastes.push(paste);
|
||||
});
|
||||
let _ = db.save();
|
||||
|
||||
Redirect::to("/")
|
||||
}
|
||||
|
||||
#[get("/dummy")]
|
||||
fn get_dummy() -> &'static str {
|
||||
"Hello World"
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let db : DB = FileDatabase::from_path("server.ron", ServerData {
|
||||
pastes: vec![],
|
||||
users: HashMap::new(),
|
||||
}).unwrap();
|
||||
let _ = db.load();
|
||||
|
||||
|
||||
rocket::ignite()
|
||||
.mount("/", routes![index, post_login, post_paste, post_register, get_dummy])
|
||||
.attach(Template::fairing())
|
||||
.manage(db)
|
||||
.launch();
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Rustbreak Pastes</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.1/css/bulma.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
{{#if logged_in }}
|
||||
<div class="container">
|
||||
<div>
|
||||
You are logged in as: <strong>{{user}}</strong>
|
||||
</div>
|
||||
<form action="/paste" method="post">
|
||||
<div class="field">
|
||||
<label class="label is-small">Name</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" name="body" row="4"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control is-small">
|
||||
<button class="button is-link is-small">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<form action="/login" method="post" class="column">
|
||||
<div class="field">
|
||||
<label class="label is-small">Name</label>
|
||||
<div class="control is-small">
|
||||
<input class="input is-small" type="text" placeholder="Username" name="username">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label is-small">Password</label>
|
||||
<div class="control is-small">
|
||||
<input class="input is-small" type="password" placeholder="Username" name="password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control is-small">
|
||||
<button class="button is-link is-small">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form action="/register" method="post" class="column">
|
||||
<div class="field">
|
||||
<label class="label is-small">Name</label>
|
||||
<div class="control is-small">
|
||||
<input class="input is-small" type="text" placeholder="Username" name="username">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label is-small">Password</label>
|
||||
<div class="control is-small">
|
||||
<input class="input is-small" type="password" placeholder="Username" name="password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control is-small">
|
||||
<button class="button is-small is-link">Register</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="container">
|
||||
<h1 class="title">Pastes</h1>
|
||||
|
||||
{{#each pastes as |p| ~}}
|
||||
<div class="box">
|
||||
<article class="media">
|
||||
<div class="media-content">
|
||||
<p>
|
||||
<strong>{{p.user}}</strong><br>
|
||||
{{p.body}}
|
||||
<p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
{{/each~}}
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
Made with love in Germany — Created using Rocket, Handlebars and Rustbreak
|
||||
</p>
|
||||
<a href="https://bulma.io">
|
||||
<img src="https://bulma.io/images/made-with-bulma.png" alt="Made with Bulma" width="128" height="24">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
extern crate rustbreak;
|
||||
|
||||
use rustbreak::Database;
|
||||
|
||||
fn main() {
|
||||
let db = Database::open("my_contacts").unwrap();
|
||||
|
||||
db.insert("Lapfox", "lapfoxtrax.com").unwrap();
|
||||
db.insert("Rust", "github.com/rust-lang/rust").unwrap();
|
||||
|
||||
// we need to be explicit about the kind of type we want as println! is generic
|
||||
let rust : String = db.retrieve("Rust").unwrap();
|
||||
println!("You can find Rust at: {}", rust);
|
||||
db.flush().unwrap();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
extern crate rustbreak;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
extern crate failure;
|
||||
|
||||
use rustbreak::{FileDatabase, backend::FileBackend};
|
||||
use rustbreak::deser::{Ron, Yaml};
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
enum Country {
|
||||
Italy, UnitedKingdom
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
struct Person {
|
||||
name: String,
|
||||
country: Country,
|
||||
}
|
||||
|
||||
fn do_main() -> Result<(), failure::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
let db = FileDatabase::<HashMap<String, Person>, Ron>::from_path("test.ron", HashMap::new())?;
|
||||
|
||||
println!("Writing to Database");
|
||||
db.write(|db| {
|
||||
db.insert("john".into(), Person {
|
||||
name: String::from("John Andersson"),
|
||||
country: Country::Italy
|
||||
});
|
||||
db.insert("fred".into(), Person {
|
||||
name: String::from("Fred Johnson"),
|
||||
country: Country::UnitedKingdom
|
||||
});
|
||||
println!("Entries: \n{:#?}", db);
|
||||
})?;
|
||||
|
||||
println!("Syncing Database");
|
||||
db.save()?;
|
||||
|
||||
// Now lets switch it
|
||||
|
||||
let db = db.with_deser(Yaml).with_backend(FileBackend::open("test.yml")?);
|
||||
db.save()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = do_main() {
|
||||
eprintln!("An error has occurred at: \n{}", e.backtrace());
|
||||
::std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! The persistence backends of the Database
|
||||
//!
|
||||
//! A file is a `Backend` through the `FileBackend`, so is a `Vec<u8>` with a `MemoryBackend`.
|
||||
//!
|
||||
//! Implementing your own Backend should be straightforward. Check the `Backend` documentation for
|
||||
//! details.
|
||||
|
||||
use failure::ResultExt;
|
||||
|
||||
use error;
|
||||
|
||||
/// The Backend Trait
|
||||
///
|
||||
/// It should always read and save in full the data that it is passed. This means that a write to
|
||||
/// the backend followed by a read __must__ return the same dataset.
|
||||
pub trait Backend {
|
||||
/// Read the all data from the backend
|
||||
fn get_data(&mut self) -> error::Result<Vec<u8>>;
|
||||
|
||||
/// Write the whole slice to the backend
|
||||
fn put_data(&mut self, data: &[u8]) -> error::Result<()>;
|
||||
}
|
||||
|
||||
/// A backend using a file
|
||||
#[derive(Debug)]
|
||||
pub struct FileBackend(::std::fs::File);
|
||||
|
||||
impl Backend for FileBackend {
|
||||
fn get_data(&mut self) -> error::Result<Vec<u8>> {
|
||||
use ::std::io::{Seek, SeekFrom, Read};
|
||||
|
||||
let mut buffer = vec![];
|
||||
self.0.seek(SeekFrom::Start(0)).context(error::RustbreakErrorKind::Backend)?;
|
||||
self.0.read_to_end(&mut buffer).context(error::RustbreakErrorKind::Backend)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
fn put_data(&mut self, data: &[u8]) -> error::Result<()> {
|
||||
use ::std::io::{Seek, SeekFrom, Write};
|
||||
|
||||
self.0.seek(SeekFrom::Start(0)).context(error::RustbreakErrorKind::Backend)?;
|
||||
self.0.set_len(0).context(error::RustbreakErrorKind::Backend)?;
|
||||
self.0.write_all(data).context(error::RustbreakErrorKind::Backend)?;
|
||||
self.0.sync_all().context(error::RustbreakErrorKind::Backend)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileBackend {
|
||||
/// Opens a new FileBackend for a given path, will create it if the file doesn't exist.
|
||||
pub fn open<P: AsRef<::std::path::Path>>(path: P) -> error::Result<FileBackend> {
|
||||
use ::std::fs::OpenOptions;
|
||||
|
||||
Ok(FileBackend(
|
||||
OpenOptions::new().read(true).write(true).create(true).open(path).context(error::RustbreakErrorKind::Backend)?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Use an already open File as the backend
|
||||
pub fn from_file(file: ::std::fs::File) -> FileBackend {
|
||||
FileBackend(file)
|
||||
}
|
||||
|
||||
/// Return the inner File
|
||||
pub fn into_inner(self) -> ::std::fs::File {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An in memory backend
|
||||
///
|
||||
/// It is backed by a `Vec<u8>`
|
||||
#[derive(Debug)]
|
||||
pub struct MemoryBackend(Vec<u8>);
|
||||
|
||||
impl MemoryBackend {
|
||||
/// Construct a new Memory Database
|
||||
pub fn new() -> MemoryBackend {
|
||||
MemoryBackend(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for MemoryBackend {
|
||||
fn get_data(&mut self) -> error::Result<Vec<u8>> {
|
||||
Ok(self.0.clone())
|
||||
}
|
||||
|
||||
fn put_data(&mut self, data: &[u8]) -> error::Result<()> {
|
||||
self.0 = data.to_owned();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tempfile;
|
||||
use super::{Backend, MemoryBackend, FileBackend};
|
||||
|
||||
#[test]
|
||||
fn test_memory_backend() {
|
||||
let mut backend = MemoryBackend::new();
|
||||
let data = [4, 5, 1, 6, 8, 1];
|
||||
|
||||
backend.put_data(&data).unwrap();
|
||||
assert_eq!(backend.get_data().unwrap(), data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_backend() {
|
||||
let file = tempfile::tempfile().unwrap();
|
||||
let mut backend = FileBackend::from_file(file);
|
||||
let data = [4, 5, 1, 6, 8, 1];
|
||||
|
||||
backend.put_data(&data).unwrap();
|
||||
assert_eq!(backend.get_data().unwrap(), data);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use bincode::{self, serialize as bin_serialize, deserialize as bin_deserialize};
|
||||
|
||||
pub type Repr = Vec<u8>;
|
||||
pub type SerializeError = bincode::Error;
|
||||
pub type DeserializeError = ();
|
||||
|
||||
pub fn serialize<T>(value: &T) -> bincode::Result<Vec<u8>>
|
||||
where T: Serialize
|
||||
{
|
||||
bin_serialize(value, bincode::Infinite)
|
||||
}
|
||||
|
||||
pub fn deserialize<'a, T>(bytes: &'a [u8]) -> Result<T, bincode::Error>
|
||||
where T: Deserialize<'a>
|
||||
{
|
||||
bin_deserialize(bytes)
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
use std::io::Read;
|
||||
|
||||
use failure;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
#[cfg(feature = "ron_enc")]
|
||||
pub use self::ron::Ron;
|
||||
|
||||
#[cfg(feature = "yaml_enc")]
|
||||
pub use self::yaml::Yaml;
|
||||
|
||||
#[cfg(feature = "bin_enc")]
|
||||
pub use self::bincode::Bincode;
|
||||
|
||||
/// A trait to bundle serializer and deserializer in a simple struct
|
||||
///
|
||||
/// It should preferably be an struct: one that does not have any members.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// For an imaginary serde compatible encoding scheme 'Frobnar', an example implementation can look
|
||||
/// like this:
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate rustbreak;
|
||||
/// extern crate serde;
|
||||
/// #[macro_use] extern crate failure;
|
||||
///
|
||||
/// use std::io::Read;
|
||||
/// use serde::Serialize;
|
||||
/// use serde::de::Deserialize;
|
||||
/// use failure::{Context, Fail, Backtrace};
|
||||
///
|
||||
/// use rustbreak::error;
|
||||
/// use rustbreak::deser::DeSerializer;
|
||||
///
|
||||
/// #[derive(Fail, Debug)]
|
||||
/// #[fail(display = "A FrobnarError ocurred")]
|
||||
/// struct FrobnarError;
|
||||
///
|
||||
/// fn to_frobnar<T: Serialize>(input: &T) -> Vec<u8> {
|
||||
/// unimplemented!(); // implementation not specified
|
||||
/// }
|
||||
///
|
||||
/// fn from_frobnar<'r, T: Deserialize<'r> + 'r, R: Read>(input: &R) -> Result<T, FrobnarError> {
|
||||
/// unimplemented!(); // implementation not specified
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Debug, Default, Clone)]
|
||||
/// struct Frobnar;
|
||||
///
|
||||
/// impl<T: Serialize> DeSerializer<T> for Frobnar
|
||||
/// where for<'de> T: Deserialize<'de>
|
||||
/// {
|
||||
/// fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error> {
|
||||
/// Ok(to_frobnar(val))
|
||||
/// }
|
||||
///
|
||||
/// fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error> {
|
||||
/// Ok(from_frobnar(&s)?)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {}
|
||||
/// ```
|
||||
pub trait DeSerializer<T: Serialize + DeserializeOwned> : ::std::default::Default + Send + Sync + Clone {
|
||||
/// Serializes a given value to a String
|
||||
fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error>;
|
||||
/// Deserializes a String to a value
|
||||
fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error>;
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "ron_enc")]
|
||||
mod ron {
|
||||
use std::io::Read;
|
||||
|
||||
use failure;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use failure::ResultExt;
|
||||
|
||||
use ron::ser::to_string_pretty as to_ron_string;
|
||||
use ron::ser::PrettyConfig;
|
||||
use ron::de::from_reader as from_ron_string;
|
||||
|
||||
use error;
|
||||
use deser::DeSerializer;
|
||||
|
||||
/// The Struct that allows you to use `ron` the Rusty Object Notation
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Ron;
|
||||
|
||||
impl<T: Serialize + DeserializeOwned> DeSerializer<T> for Ron {
|
||||
fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error> {
|
||||
Ok(to_ron_string(val, PrettyConfig::default()).map(String::into_bytes)
|
||||
.context(error::RustbreakErrorKind::Serialization)?)
|
||||
}
|
||||
fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error> {
|
||||
Ok(from_ron_string(s).context(error::RustbreakErrorKind::Deserialization)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "yaml_enc")]
|
||||
mod yaml {
|
||||
use std::io::Read;
|
||||
|
||||
use failure;
|
||||
use serde_yaml::{to_string as to_yaml_string, from_reader as from_yaml_string};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use failure::ResultExt;
|
||||
|
||||
use error;
|
||||
use deser::DeSerializer;
|
||||
|
||||
/// The struct that allows you to use yaml
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Yaml;
|
||||
|
||||
impl<T: Serialize + DeserializeOwned> DeSerializer<T> for Yaml {
|
||||
fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error> {
|
||||
Ok(to_yaml_string(val)
|
||||
.map(String::into_bytes)
|
||||
.context(error::RustbreakErrorKind::Serialization)?)
|
||||
}
|
||||
fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error> {
|
||||
Ok(from_yaml_string(s).context(error::RustbreakErrorKind::Deserialization)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "bin_enc")]
|
||||
mod bincode {
|
||||
use std::io::Read;
|
||||
|
||||
use failure;
|
||||
use bincode::{deserialize_from, serialize};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use failure::ResultExt;
|
||||
|
||||
use error;
|
||||
use deser::DeSerializer;
|
||||
|
||||
/// The struct that allows you to use bincode
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Bincode;
|
||||
|
||||
impl<T: Serialize + DeserializeOwned> DeSerializer<T> for Bincode {
|
||||
fn serialize(&self, val: &T) -> Result<Vec<u8>, failure::Error> {
|
||||
Ok(serialize(val).context(error::RustbreakErrorKind::Serialization)?)
|
||||
}
|
||||
fn deserialize<R: Read>(&self, s: R) -> Result<T, failure::Error> {
|
||||
Ok(deserialize_from(s).context(error::RustbreakErrorKind::Deserialization)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
-31
@@ -1,37 +1,77 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
quick_error! {
|
||||
/// The Error type exported by BreakError, usually you only need to check against NotFound,
|
||||
/// however it might be useful sometimes to get other errors.
|
||||
#[derive(Debug)]
|
||||
pub enum BreakError {
|
||||
/// An error returned when doing file operations, this might happen by opening, closing,
|
||||
/// locking or flushing
|
||||
Io(err: ::std::io::Error) {
|
||||
from()
|
||||
}
|
||||
/// This error happens if Bincode cannot deserialize a given file. If you get this error
|
||||
/// check your database is not corrupt. (This includes non-empty files **not** created by
|
||||
/// RustBreak!
|
||||
Deserialize(err: ::enc::DeserializeError) {
|
||||
from()
|
||||
}
|
||||
/// This error happens if bincode cannot serialize the given type at runtime
|
||||
Serialize(err: ::enc::SerializeError) {
|
||||
from()
|
||||
}
|
||||
/// Error when reading a formatted String
|
||||
Format(err: ::std::string::FromUtf8Error) {
|
||||
from()
|
||||
}
|
||||
/// Poisoned, you can recover from this by running `recover_poison` on the database
|
||||
Poison {}
|
||||
/// This simply means your key could not be found in the database
|
||||
NotFound {}
|
||||
use std::fmt::{self, Display};
|
||||
use failure::{Context, Fail, Backtrace};
|
||||
|
||||
/// The different kinds of errors that can be returned
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
|
||||
pub enum RustbreakErrorKind {
|
||||
/// A context error when a serialization failed
|
||||
#[fail(display = "Could not serialize the value")]
|
||||
Serialization,
|
||||
/// A context error when a deserialization failed
|
||||
#[fail(display = "Could not deserialize the value")]
|
||||
Deserialization,
|
||||
/// This error is returned if the `Database` is poisoned. See `Database::write` for details
|
||||
#[fail(display = "The database has been poisoned")]
|
||||
Poison,
|
||||
/// An error in the backend happened
|
||||
#[fail(display = "The backend has encountered an error")]
|
||||
Backend,
|
||||
/// If `Database::write_safe` is used and the closure panics, this error is returned
|
||||
#[fail(display = "The write operation paniced but got caught")]
|
||||
WritePanic,
|
||||
/// This variant should never be used. It is meant to keep this enum forward compatible.
|
||||
#[doc(hidden)]
|
||||
#[fail(display = "You have found a secret message, please report it to the Rustbreak maintainer")]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// The main error type that gets returned for errors that happen while interacting with a
|
||||
/// `Database`.
|
||||
#[derive(Debug)]
|
||||
pub struct RustbreakError {
|
||||
inner: Context<RustbreakErrorKind>,
|
||||
}
|
||||
|
||||
impl Fail for RustbreakError {
|
||||
fn cause(&self) -> Option<&Fail> {
|
||||
self.inner.cause()
|
||||
}
|
||||
|
||||
fn backtrace(&self) -> Option<&Backtrace> {
|
||||
self.inner.backtrace()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<::std::sync::PoisonError<T>> for BreakError {
|
||||
fn from(_: ::std::sync::PoisonError<T>) -> BreakError {
|
||||
BreakError::Poison
|
||||
impl Display for RustbreakError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt(&self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl RustbreakError {
|
||||
/// Get the kind of this error
|
||||
pub fn kind(&self) -> RustbreakErrorKind {
|
||||
*self.inner.get_context()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RustbreakErrorKind> for RustbreakError {
|
||||
fn from(kind: RustbreakErrorKind) -> RustbreakError {
|
||||
RustbreakError { inner: Context::new(kind) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Context<RustbreakErrorKind>> for RustbreakError {
|
||||
fn from(inner: Context<RustbreakErrorKind>) -> RustbreakError {
|
||||
RustbreakError { inner: inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple type alias for errors
|
||||
pub type Result<T> = ::std::result::Result<T, RustbreakError>;
|
||||
|
||||
+541
-474
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use ron::{ser, de};
|
||||
|
||||
pub type Repr = String;
|
||||
pub type SerializeError = ser::Error;
|
||||
pub type DeserializeError = de::Error;
|
||||
|
||||
pub fn serialize<T>(value: &T) -> ser::Result<String>
|
||||
where T: Serialize
|
||||
{
|
||||
ser::pretty::to_string(value)
|
||||
}
|
||||
|
||||
pub fn deserialize<T, I>(bytes: &I) -> Result<T, ::error::BreakError>
|
||||
where T: DeserializeOwned,
|
||||
I: AsRef<[u8]>
|
||||
{
|
||||
let string = try!(String::from_utf8(bytes.as_ref().to_vec()));
|
||||
Ok(de::from_str(&string)?)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_yaml::Result as YamlResult;
|
||||
use serde_yaml::Error;
|
||||
|
||||
pub type Repr = String;
|
||||
pub type SerializeError = Error;
|
||||
pub type DeserializeError = ();
|
||||
|
||||
pub fn serialize<T>(value: &T) -> YamlResult<String>
|
||||
where T: Serialize
|
||||
{
|
||||
::serde_yaml::to_string(value)
|
||||
}
|
||||
|
||||
pub fn deserialize<T, I: AsRef<[u8]>>(bytes: &I) -> Result<T, ::error::BreakError>
|
||||
where T: DeserializeOwned
|
||||
{
|
||||
|
||||
let string = try!(String::from_utf8(bytes.as_ref().to_vec()));
|
||||
let des = try!(::serde_yaml::from_str(&string));
|
||||
Ok(des)
|
||||
}
|
||||
Reference in New Issue
Block a user