// Copyright 2015-2017 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . use std::fs::File; use std::io::Write; use std::path::Path; use std::time::Duration; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::de::from_reader; use serde_json::ser::to_string; use journaldb::Algorithm; use ethcore::client::{Mode as ClientMode}; #[derive(Clone)] pub struct Seconds(Duration); impl Seconds { pub fn value(&self) -> u64 { self.0.as_secs() } } impl From for Seconds { fn from(s: u64) -> Seconds { Seconds(Duration::from_secs(s)) } } impl From for Seconds { fn from(d: Duration) -> Seconds { Seconds(d) } } impl Into for Seconds { fn into(self) -> Duration { self.0 } } impl Serialize for Seconds { fn serialize(&self, serializer: S) -> Result { serializer.serialize_u64(self.value()) } } impl<'de> Deserialize<'de> for Seconds { fn deserialize>(deserializer: D) -> Result { let secs = u64::deserialize(deserializer)?; Ok(Seconds::from(secs)) } } #[derive(Clone, Serialize, Deserialize)] #[serde(rename_all = "lowercase", tag = "mode")] pub enum Mode { Active, Passive { #[serde(rename = "mode.timeout")] timeout: Seconds, #[serde(rename = "mode.alarm")] alarm: Seconds, }, Dark { #[serde(rename = "mode.timeout")] timeout: Seconds, }, Offline, } impl Into for Mode { fn into(self) -> ClientMode { match self { Mode::Active => ClientMode::Active, Mode::Passive { timeout, alarm } => ClientMode::Passive(timeout.into(), alarm.into()), Mode::Dark { timeout } => ClientMode::Dark(timeout.into()), Mode::Offline => ClientMode::Off, } } } impl From for Mode { fn from(mode: ClientMode) -> Mode { match mode { ClientMode::Active => Mode::Active, ClientMode::Passive(timeout, alarm) => Mode::Passive { timeout: timeout.into(), alarm: alarm.into() }, ClientMode::Dark(timeout) => Mode::Dark { timeout: timeout.into() }, ClientMode::Off => Mode::Offline, } } } #[derive(Serialize, Deserialize)] pub struct UserDefaults { pub is_first_launch: bool, #[serde(with = "algorithm_serde")] pub pruning: Algorithm, pub tracing: bool, pub fat_db: bool, #[serde(flatten)] mode: Mode, } impl UserDefaults { pub fn mode(&self) -> ClientMode { self.mode.clone().into() } pub fn set_mode(&mut self, mode: ClientMode) { self.mode = mode.into(); } } mod algorithm_serde { use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::de::Error; use journaldb::Algorithm; pub fn serialize(algorithm: &Algorithm, serializer: S) -> Result where S: Serializer { algorithm.as_str().serialize(serializer) } pub fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de> { let pruning = String::deserialize(deserializer)?; pruning.parse().map_err(|_| Error::custom("invalid pruning method")) } } impl Default for UserDefaults { fn default() -> Self { UserDefaults { is_first_launch: true, pruning: Algorithm::OverlayRecent, tracing: false, fat_db: false, mode: Mode::Active, } } } impl UserDefaults { pub fn load

(path: P) -> Result where P: AsRef { match File::open(path) { Ok(file) => match from_reader(file) { Ok(defaults) => Ok(defaults), Err(e) => { warn!("Error loading user defaults file: {:?}", e); Ok(UserDefaults::default()) }, }, _ => Ok(UserDefaults::default()), } } pub fn save

(&self, path: P) -> Result<(), String> where P: AsRef { let mut file: File = File::create(path).map_err(|_| "Cannot create user defaults file".to_owned())?; file.write_all(to_string(&self).unwrap().as_bytes()).map_err(|_| "Failed to save user defaults".to_owned()) } }