blob: 86464309c2cbcf31e1c4367b8522970d0a05ec0f [file] [log] [blame]
// 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(())
}
}