blob: 2d2b2ff46390f513d990a6abbd1ce65a5927a9be [file] [log] [blame]
// Copyright 2019 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::common::SettingCodec;
use failure::{format_err, Error};
use fidl_fuchsia_setui::*;
use serde_json::json;
use serde_json::Value;
pub struct JsonCodec {}
impl JsonCodec {
pub fn new() -> JsonCodec {
return JsonCodec {};
}
}
const KEY_TYPE: &str = "type";
const KEY_DATA: &str = "data";
impl SettingCodec<Value> for JsonCodec {
fn encode(&self, data: SettingData) -> Result<Value, Error> {
if let Some(setting_type) = get_type(&data) {
let mut data_value;
match setting_type {
TYPE_STRING_VALUE => {
data_value = Some(encode_string_value(&data)?);
}
TYPE_ACCOUNT_VALUE => {
data_value = Some(encode_account_settings(&data)?);
}
_ => {
return Err(format_err!("type encoding not available"));
}
}
return Ok(json!({KEY_TYPE: setting_type, KEY_DATA: data_value.unwrap()}));
} else {
return Err(format_err!("unhandled data type"));
}
}
fn decode(&self, encoded: Value) -> Result<SettingData, Error> {
if let Value::Object(mapping) = encoded {
// Get value mapped to the type key
if let (Some(type_value), Some(data_value)) =
(mapping.get(KEY_TYPE), mapping.get(KEY_DATA))
{
// Retrieve setting type from value.
if let Some(setting_type) = type_value.as_u64() {
match setting_type {
TYPE_STRING_VALUE => return decode_string_value(data_value),
TYPE_ACCOUNT_VALUE => return decode_account(data_value),
_ => return Err(format_err!("unsupported encoded type")),
}
} else {
return Err(format_err!("type is not a number"));
}
} else {
return Err(format_err!("type or data not present"));
}
} else {
return Err(format_err!("root node should be an object"));
}
}
}
const TYPE_STRING_VALUE: u64 = 1;
const TYPE_ACCOUNT_VALUE: u64 = 2;
fn get_type(setting_data: &SettingData) -> Option<u64> {
match setting_data {
SettingData::StringValue(_val) => Some(TYPE_STRING_VALUE),
SettingData::Account(_val) => Some(TYPE_ACCOUNT_VALUE),
_ => None,
}
}
fn encode_string_value(data: &SettingData) -> Result<Value, Error> {
if let SettingData::StringValue(string_val) = data {
return Ok(json!(string_val));
} else {
return Err(format_err!("not a string value"));
}
}
fn decode_string_value(encoded: &Value) -> Result<SettingData, Error> {
if let Value::String(string_val) = encoded {
return Ok(SettingData::StringValue(string_val.clone()));
}
return Err(format_err!("not a string type"));
}
const ACCOUNT_SETTINGS_MODE_KEY: &str = "mode";
fn encode_account_settings(data: &SettingData) -> Result<Value, Error> {
if let SettingData::Account(account_settings) = data {
if account_settings.mode == None {
return Ok(json!({}));
}
let encoded_mode = encode_login_mode(account_settings.mode.unwrap())?;
return Ok(json!({ ACCOUNT_SETTINGS_MODE_KEY: encoded_mode }));
} else {
return Err(format_err!("not an account setting val"));
}
}
fn decode_account(encoded: &Value) -> Result<SettingData, Error> {
if let Value::Object(mapping) = encoded {
match mapping.get(ACCOUNT_SETTINGS_MODE_KEY) {
Some(val) => {
Ok(SettingData::Account(AccountSettings { mode: Some(decode_login_mode(val)?) }))
}
_ => Ok(SettingData::Account(AccountSettings { mode: None })),
}
} else {
return Err(format_err!("malformed account json"));
}
}
const LOGIN_MODE_NONE: u64 = 0;
const LOGIN_MODE_GUEST_OVERRIDE: u64 = 1;
const LOGIN_MODE_AUTH_PROVIDER: u64 = 2;
fn encode_login_mode(mode: LoginOverride) -> Result<Value, Error> {
match mode {
LoginOverride::None => {
return Ok(json!(LOGIN_MODE_NONE));
}
LoginOverride::AutologinGuest => {
return Ok(json!(LOGIN_MODE_GUEST_OVERRIDE));
}
LoginOverride::AuthProvider => {
return Ok(json!(LOGIN_MODE_AUTH_PROVIDER));
}
}
}
fn decode_login_mode(value: &Value) -> Result<LoginOverride, Error> {
if let Value::Number(number) = value {
if let Some(val) = number.as_u64() {
match val {
LOGIN_MODE_NONE => {
return Ok(LoginOverride::None);
}
LOGIN_MODE_GUEST_OVERRIDE => {
return Ok(LoginOverride::AutologinGuest);
}
LOGIN_MODE_AUTH_PROVIDER => {
return Ok(LoginOverride::AuthProvider);
}
_ => {
return Err(format_err!("not a decodable type"));
}
}
} else {
return Err(format_err!("incorrect number format"));
}
} else {
return Err(format_err!("not a number"));
}
}
#[cfg(test)]
mod tests {
use super::*;
/// A basic test to exercise that basic functionality works. In this case, we
/// mutate the unknown type, reserved for testing. We should always immediately
/// receive back an Ok response.
#[test]
fn test_encode_string() {
let test_val = "foo bar";
let codec = JsonCodec::new();
let encode_result = codec.encode(SettingData::StringValue(test_val.to_string()));
assert!(encode_result.is_ok());
let encoded_value = encode_result.unwrap();
let decode_result = codec.decode(encoded_value);
assert!(decode_result.is_ok());
match decode_result.unwrap() {
SettingData::StringValue(val) => {
assert_eq!(val, test_val);
}
_ => {
panic!("wrong type!");
}
}
}
#[test]
fn test_encode_account() {
let codec = JsonCodec::new();
let test_override = LoginOverride::AutologinGuest;
let encode_result =
codec.encode(SettingData::Account(AccountSettings { mode: Some(test_override) }));
assert!(encode_result.is_ok());
let encoded_value = encode_result.unwrap();
let decode_result = codec.decode(encoded_value);
assert!(decode_result.is_ok());
match decode_result.unwrap() {
SettingData::Account(account_settings) => {
if let Some(login_override) = account_settings.mode {
assert_eq!(test_override, login_override);
} else {
panic!("override should have been set");
}
}
_ => {
panic!("wrong setting data type");
}
}
}
}