Skip to content

Commit 8f045ed

Browse files
committed
1.0.0: serde (closes #15), std::error::Error (closes #12), re-export AppDirsError (closes #11)
1 parent 1b2c002 commit 8f045ed

File tree

2 files changed

+73
-58
lines changed

2 files changed

+73
-58
lines changed

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "preferences"
3-
version = "0.7.3"
3+
version = "1.0.0"
44
authors = ["Andy Barron <AndrewLBarron@gmail.com>"]
55

66
description = "Read and write user-specific application data (in stable Rust)"
@@ -12,4 +12,8 @@ license = "MIT"
1212

1313
[dependencies]
1414
app_dirs = "^1.1.1"
15-
rustc-serialize = "^0.3.19"
15+
serde = "^0.9.6"
16+
serde_json = "^0.9.5"
17+
18+
[dev-dependencies]
19+
serde_derive = "^0.9.6"

src/lib.rs

Lines changed: 67 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
//! Though it was originally inspired by Java's convenient
77
//! [Preferences API](https://docs.oracle.com/javase/8/docs/api/java/util/prefs/Preferences.html),
88
//! this crate is more flexible. *Any* struct or enum that implements
9-
//! [`rustc-serialize`][rustc-serialize-api]'s `Encodable` and `Decodable`
9+
//! [`serde`][serde-api]'s `Serialize` and `Deserialize`
1010
//! traits can be stored and retrieved as user data. Implementing those traits is
11-
//! trivial; just use `#[derive(RustcEncodable, RustcDecodable)`.
11+
//! trivial; just include the crate `serde_derive` (don't forget `#[macro_use]`!) and add
12+
//! `#[derive(Serialize, Deserialize)` to your struct definition. (See examples below.)
1213
//!
1314
//! # Usage
1415
//! For convenience, the type [`PreferencesMap<T>`](type.PreferencesMap.html) is provided. (It's
@@ -18,17 +19,6 @@
1819
//! will be implemented for your map instance. This allows you to seamlessly save and load
1920
//! user data with the `save(..)` and `load(..)` trait methods from `Preferences`.
2021
//!
21-
//! # Roadmap
22-
//! This crate aims to provide a convenient API for both stable and nightly Rust, which is why
23-
//! it currently uses [`rustc-serialize`][rustc-serialize-api] instead of the more recent
24-
//! [`serde`][serde-api] library. In the distant future, when custom derives are stabilized
25-
//! and `serde` is available in stable Rust, this library will migrate to `serde`. This will be
26-
//! a breaking change (**and will update the semantic version number accordingly so that your
27-
//! builds don't break**).
28-
//!
29-
//! At that point, updating should be dead simple; you'll just have to
30-
//! replace `#[derive(RustcEncodable, RustcDecodable)` with `#[derive(Serialize, Deserialize)`.
31-
//!
3222
//! # Basic example
3323
//! ```
3424
//! extern crate preferences;
@@ -63,16 +53,16 @@
6353
//!
6454
//! # Using custom data types
6555
//! ```
66-
//! // `rustc_serialize` will be replaced with `serde` when custom derive is stabilized
67-
//! extern crate rustc_serialize;
56+
//! #[macro_use]
57+
//! extern crate serde_derive;
6858
//! extern crate preferences;
6959
//! use preferences::{AppInfo, Preferences};
7060
//!
7161
//! const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"};
7262
//!
73-
//! // Deriving `RustcEncodable` and `RustcDecodable` on a struct/enum automatically implements
63+
//! // Deriving `Serialize` and `Deserialize` on a struct/enum automatically implements
7464
//! // the `Preferences` trait.
75-
//! #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
65+
//! #[derive(Serialize, Deserialize, PartialEq, Debug)]
7666
//! struct PlayerData {
7767
//! level: u32,
7868
//! health: f32,
@@ -96,13 +86,14 @@
9686
//!
9787
//! # Using custom data types with `PreferencesMap`
9888
//! ```
99-
//! extern crate rustc_serialize;
89+
//! #[macro_use]
90+
//! extern crate serde_derive;
10091
//! extern crate preferences;
10192
//! use preferences::{AppInfo, PreferencesMap, Preferences};
10293
//!
10394
//! const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"};
10495
//!
105-
//! #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
96+
//! #[derive(Serialize, Deserialize, PartialEq, Debug)]
10697
//! struct Point(f32, f32);
10798
//!
10899
//! fn main() {
@@ -124,13 +115,14 @@
124115
//!
125116
//! # Using custom data types with serializable containers
126117
//! ```
127-
//! extern crate rustc_serialize;
118+
//! #[macro_use]
119+
//! extern crate serde_derive;
128120
//! extern crate preferences;
129121
//! use preferences::{AppInfo, Preferences};
130122
//!
131123
//! const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"};
132124
//!
133-
//! #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
125+
//! #[derive(Serialize, Deserialize, PartialEq, Debug)]
134126
//! struct Point(usize, usize);
135127
//!
136128
//! fn main() {
@@ -171,20 +163,20 @@
171163
//! library. &#128522;
172164
//!
173165
//! [hashmap-api]: https://doc.rust-lang.org/nightly/std/collections/struct.HashMap.html
174-
//! [rustc-serialize-api]: https://crates.io/crates/rustc-serialize
175166
//! [serde-api]: https://crates.io/crates/serde
176167
177168
#![warn(missing_docs)]
178169

179170
extern crate app_dirs;
180-
extern crate rustc_serialize;
171+
extern crate serde;
172+
extern crate serde_json;
181173

182-
pub use app_dirs::AppInfo;
183-
use app_dirs::{AppDataType, AppDirsError, get_data_root, get_app_dir};
184-
use rustc_serialize::{Encodable, Decodable};
185-
use rustc_serialize::json::{self, EncoderError, DecoderError};
174+
pub use app_dirs::{AppDirsError, AppInfo};
175+
use app_dirs::{AppDataType, get_data_root, get_app_dir};
176+
use serde::{Serialize, Deserialize};
186177
use std::collections::HashMap;
187178
use std::ffi::OsString;
179+
use std::fmt;
188180
use std::fs::{File, create_dir_all};
189181
use std::io::{self, ErrorKind, Read, Write};
190182
use std::path::PathBuf;
@@ -211,25 +203,47 @@ pub type PreferencesMap<T = String> = HashMap<String, T>;
211203
/// Error type representing the errors that can occur when saving or loading user data.
212204
#[derive(Debug)]
213205
pub enum PreferencesError {
214-
/// An error occurred during JSON serialization.
215-
Serialize(EncoderError),
216-
/// An error occurred during JSON deserialization.
217-
Deserialize(DecoderError),
206+
/// An error occurred during JSON serialization or deserialization.
207+
Json(serde_json::Error),
218208
/// An error occurred during preferences file I/O.
219209
Io(io::Error),
220210
/// Couldn't figure out where to put or find the serialized data.
221211
Directory(AppDirsError),
222212
}
223213

224-
impl From<EncoderError> for PreferencesError {
225-
fn from(e: EncoderError) -> Self {
226-
PreferencesError::Serialize(e)
214+
impl fmt::Display for PreferencesError {
215+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216+
use PreferencesError::*;
217+
match *self {
218+
Json(ref e) => e.fmt(f),
219+
Io(ref e) => e.fmt(f),
220+
Directory(ref e) => e.fmt(f),
221+
}
227222
}
228223
}
229224

230-
impl From<DecoderError> for PreferencesError {
231-
fn from(e: DecoderError) -> Self {
232-
PreferencesError::Deserialize(e)
225+
impl std::error::Error for PreferencesError {
226+
fn description(&self) -> &str {
227+
use PreferencesError::*;
228+
match *self {
229+
Json(ref e) => e.description(),
230+
Io(ref e) => e.description(),
231+
Directory(ref e) => e.description(),
232+
}
233+
}
234+
fn cause(&self) -> Option<&std::error::Error> {
235+
use PreferencesError::*;
236+
Some(match *self {
237+
Json(ref e) => e,
238+
Io(ref e) => e,
239+
Directory(ref e) => e,
240+
})
241+
}
242+
}
243+
244+
impl From<serde_json::Error> for PreferencesError {
245+
fn from(e: serde_json::Error) -> Self {
246+
PreferencesError::Json(e)
233247
}
234248
}
235249

@@ -257,8 +271,8 @@ impl From<AppDirsError> for PreferencesError {
257271
/// Trait for types that can be saved & loaded as user data.
258272
///
259273
/// This type is automatically implemented for any struct/enum `T` which implements both
260-
/// `Encodable` and `Decodable` (from `rustc-serialize`). (Trivially, you can annotate the type
261-
/// with `#[derive(RustcEncodable, RustcDecodable)`). It is encouraged to use the provided
274+
/// `Serialize` and `Deserialize` (from `serde`). (Trivially, you can annotate the type
275+
/// with `#[derive(Serialize, Deserialize)`). It is encouraged to use the provided
262276
/// type, [`PreferencesMap`](type.PreferencesMap.html), to bundle related user preferences.
263277
///
264278
/// For the `app` parameter of `save(..)` and `load(..)`, it's recommended that you use a single
@@ -302,47 +316,41 @@ pub trait Preferences: Sized {
302316
}
303317

304318
fn compute_file_path<S: AsRef<str>>(app: &AppInfo, key: S) -> Result<PathBuf, PreferencesError> {
305-
let mut path = try!(get_app_dir(DATA_TYPE, app, key.as_ref()));
319+
let mut path = get_app_dir(DATA_TYPE, app, key.as_ref())?;
306320
let new_name = match path.file_name() {
307-
Some(name) if name.len() > 0 => {
321+
Some(name) if name.is_empty() => {
308322
let mut new_name = OsString::with_capacity(name.len() + PREFS_FILE_EXTENSION.len());
309323
new_name.push(name);
310324
new_name.push(PREFS_FILE_EXTENSION);
311325
new_name
312-
},
326+
}
313327
_ => DEFAULT_PREFS_FILENAME.into(),
314328
};
315329
path.set_file_name(new_name);
316330
Ok(path)
317331
}
318332

319333
impl<T> Preferences for T
320-
where T: Encodable + Decodable + Sized
334+
where T: Serialize + Deserialize + Sized
321335
{
322336
fn save<S>(&self, app: &AppInfo, key: S) -> Result<(), PreferencesError>
323337
where S: AsRef<str>
324338
{
325-
let path = try!(compute_file_path(app, key.as_ref()));
339+
let path = compute_file_path(app, key.as_ref())?;
326340
path.parent().map(create_dir_all);
327-
let mut file = try!(File::create(path));
341+
let mut file = File::create(path)?;
328342
self.save_to(&mut file)
329343
}
330344
fn load<S: AsRef<str>>(app: &AppInfo, key: S) -> Result<Self, PreferencesError> {
331-
let path = try!(compute_file_path(app, key.as_ref()));
332-
let mut file = try!(File::open(path));
345+
let path = compute_file_path(app, key.as_ref())?;
346+
let mut file = File::open(path)?;
333347
Self::load_from(&mut file)
334348
}
335349
fn save_to<W: Write>(&self, writer: &mut W) -> Result<(), PreferencesError> {
336-
let encoded = try!(json::encode(self));
337-
try!(writer.write_all(encoded.as_bytes()));
338-
try!(writer.flush());
339-
Ok(())
350+
serde_json::to_writer(writer, self).map_err(Into::into)
340351
}
341352
fn load_from<R: Read>(reader: &mut R) -> Result<Self, PreferencesError> {
342-
let mut bytes = Vec::new();
343-
try!(reader.read_to_end(&mut bytes));
344-
let encoded = try!(String::from_utf8(bytes));
345-
json::decode(&encoded).map_err(|e| e.into())
353+
serde_json::from_reader(reader).map_err(Into::into)
346354
}
347355
}
348356

@@ -358,7 +366,10 @@ pub fn prefs_base_dir() -> Option<PathBuf> {
358366
#[cfg(test)]
359367
mod tests {
360368
use {AppInfo, Preferences, PreferencesMap};
361-
const APP_INFO: AppInfo = AppInfo{name: "preferences", author: "Rust language community"};
369+
const APP_INFO: AppInfo = AppInfo {
370+
name: "preferences",
371+
author: "Rust language community",
372+
};
362373
const TEST_PREFIX: &'static str = "tests/module";
363374
fn gen_test_name(name: &str) -> String {
364375
TEST_PREFIX.to_owned() + "/" + name

0 commit comments

Comments
 (0)