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
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;
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,
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() {
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() {
171163//! library. 😊
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
179170extern 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 } ;
186177use std:: collections:: HashMap ;
187178use std:: ffi:: OsString ;
179+ use std:: fmt;
188180use std:: fs:: { File , create_dir_all} ;
189181use std:: io:: { self , ErrorKind , Read , Write } ;
190182use 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 ) ]
213205pub 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
304318fn 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
319333impl < 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) ]
359367mod 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