| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::config::api::{ConfigLevel, ReadConfig, WriteConfig}, |
| crate::config::priority_config::Priority, |
| anyhow::{anyhow, Error}, |
| serde_json::Value, |
| std::io::{Read, Write}, |
| }; |
| |
| pub(crate) struct Persistent { |
| data: Priority, |
| } |
| |
| impl Persistent { |
| fn open<R: Read>(file: Option<R>) -> Result<Option<Value>, Error> { |
| if file.is_none() { |
| return Ok(None); |
| } |
| let config = serde_json::from_reader(file.unwrap()); |
| // If JSON is malformed, this will just overwrite if set is ever used. |
| if config.is_err() { |
| return Ok(None); |
| } |
| |
| Ok(Some(config.unwrap())) |
| } |
| |
| fn save_config<W: Write>(file: Option<W>, value: &Option<Value>) -> Result<(), Error> { |
| if value.is_none() { |
| // No reason to throw an error. |
| return Ok(()); |
| } |
| if file.is_none() { |
| // If no option is supplied, just move on to the next - assume user doesn't want to |
| // save this level. |
| return Ok(()); |
| } |
| match serde_json::to_writer_pretty(file.unwrap(), value.as_ref().unwrap()) { |
| Err(e) => Err(anyhow!("Could not write config file: {}", e)), |
| Ok(_) => Ok(()), |
| } |
| } |
| |
| pub(crate) fn load<R: Read>( |
| global: Option<R>, |
| build: Option<R>, |
| user: Option<R>, |
| ) -> Result<Self, Error> { |
| Ok(Self { |
| data: Priority::new( |
| Persistent::open(user)?, |
| Persistent::open(build)?, |
| Persistent::open(global)?, |
| ), |
| }) |
| } |
| |
| pub(crate) fn save<W: Write>( |
| &self, |
| global: Option<W>, |
| build: Option<W>, |
| user: Option<W>, |
| ) -> Result<(), Error> { |
| Persistent::save_config(user, &self.data.user)?; |
| Persistent::save_config(build, &self.data.build)?; |
| Persistent::save_config(global, &self.data.global)?; |
| Ok(()) |
| } |
| } |
| |
| impl ReadConfig for Persistent { |
| fn get(&self, key: &str) -> Option<Value> { |
| self.data.get(key) |
| } |
| } |
| |
| impl WriteConfig for Persistent { |
| fn set(&mut self, level: &ConfigLevel, key: &str, value: Value) -> Result<(), Error> { |
| match level { |
| ConfigLevel::Defaults => Err(anyhow!("Cannot override defaults")), |
| _ => self.data.set(&level, key, value), |
| } |
| } |
| |
| fn remove(&mut self, level: &ConfigLevel, key: &str) -> Result<(), Error> { |
| match level { |
| ConfigLevel::Defaults => Err(anyhow!("Cannot override defaults")), |
| _ => self.data.remove(&level, key), |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // tests |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use std::io::{BufReader, BufWriter}; |
| |
| const USER: &'static str = r#" |
| { |
| "name": "User" |
| }"#; |
| |
| const BUILD: &'static str = r#" |
| { |
| "name": "Build" |
| }"#; |
| |
| const GLOBAL: &'static str = r#" |
| { |
| "name": "Global" |
| }"#; |
| |
| #[test] |
| fn test_persistent_build() -> Result<(), Error> { |
| let mut user_file = String::from(USER); |
| let mut build_file = String::from(BUILD); |
| let mut global_file = String::from(GLOBAL); |
| |
| let persistent_config = Persistent::load( |
| Some(BufReader::new(global_file.as_bytes())), |
| Some(BufReader::new(build_file.as_bytes())), |
| Some(BufReader::new(user_file.as_bytes())), |
| )?; |
| |
| let value = persistent_config.get("name"); |
| assert!(value.is_some()); |
| assert_eq!(value.unwrap(), Value::String(String::from("User"))); |
| |
| let mut user_file_out = String::new(); |
| let mut build_file_out = String::new(); |
| let mut global_file_out = String::new(); |
| |
| unsafe { |
| persistent_config.save( |
| Some(BufWriter::new(global_file_out.as_mut_vec())), |
| Some(BufWriter::new(build_file_out.as_mut_vec())), |
| Some(BufWriter::new(user_file_out.as_mut_vec())), |
| )?; |
| } |
| |
| // Remove whitespace |
| user_file.retain(|c| !c.is_whitespace()); |
| build_file.retain(|c| !c.is_whitespace()); |
| global_file.retain(|c| !c.is_whitespace()); |
| user_file_out.retain(|c| !c.is_whitespace()); |
| build_file_out.retain(|c| !c.is_whitespace()); |
| global_file_out.retain(|c| !c.is_whitespace()); |
| |
| assert_eq!(user_file, user_file_out); |
| assert_eq!(build_file, build_file_out); |
| assert_eq!(global_file, global_file_out); |
| |
| Ok(()) |
| } |
| } |