use std::io::{Read, Write};
use std::str::FromStr;
use flate2::read::ZlibDecoder;
use flate2::write::ZlibEncoder;
use flate2::Compression;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use super::view_config::*;
use crate::utils::*;
pub enum ViewerConfigEncoding {
Json,
String,
ArrayBuffer,
JSONString,
}
impl FromStr for ViewerConfigEncoding {
type Err = JsValue;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"json" => Ok(ViewerConfigEncoding::Json),
"string" => Ok(ViewerConfigEncoding::String),
"arraybuffer" => Ok(ViewerConfigEncoding::ArrayBuffer),
x => Err(format!("Unknown format \"{}\"", x).into()),
}
}
}
#[derive(Serialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ViewerConfig {
pub plugin: String,
pub plugin_config: Value,
pub settings: bool,
pub theme: Option<String>,
#[serde(flatten)]
pub view_config: ViewConfig,
}
impl ViewerConfig {
fn _apply<T: Clone + Default>(field: &mut T, update: OptionalUpdate<T>) -> bool {
match update {
OptionalUpdate::Missing => false,
OptionalUpdate::SetDefault => {
*field = T::default();
true
}
OptionalUpdate::Update(update) => {
*field = update;
true
}
}
}
fn _apply1<T>(field: &mut T, update: Option<T>) -> bool {
match update {
None => false,
Some(update) => {
*field = update;
true
}
}
}
pub fn apply(&mut self, update: ViewerConfigUpdate) -> bool {
let mut changed = false;
changed = Self::_apply(&mut self.plugin, update.plugin) || changed;
changed = Self::_apply1(&mut self.plugin_config, update.plugin_config) || changed;
changed = Self::_apply(&mut self.settings, update.settings) || changed;
changed = self.view_config.apply_update(update.view_config) || changed;
changed
}
pub fn encode(&self, format: &Option<ViewerConfigEncoding>) -> ApiResult<JsValue> {
match format {
Some(ViewerConfigEncoding::String) => {
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
let bytes = rmp_serde::to_vec(self)?;
encoder.write_all(&bytes)?;
let encoded = encoder.finish()?;
Ok(JsValue::from(base64::encode(encoded)))
}
Some(ViewerConfigEncoding::ArrayBuffer) => {
let array = js_sys::Uint8Array::from(&rmp_serde::to_vec(self).unwrap()[..]);
let start = array.byte_offset();
let len = array.byte_length();
Ok(array
.buffer()
.slice_with_end(start, start + len)
.unchecked_into())
}
Some(ViewerConfigEncoding::JSONString) => {
Ok(JsValue::from(serde_json::to_string(self)?))
}
None | Some(ViewerConfigEncoding::Json) => Ok(JsValue::from_serde(self)?),
}
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ViewerConfigUpdate {
#[serde(default)]
pub plugin: PluginUpdate,
#[serde(default)]
pub theme: ThemeUpdate,
#[serde(default)]
pub settings: SettingsUpdate,
#[serde(default)]
pub plugin_config: Option<Value>,
#[serde(flatten)]
pub view_config: ViewConfigUpdate,
}
impl ViewerConfigUpdate {
pub fn decode(update: &JsValue) -> ApiResult<Self> {
if update.is_string() {
let js_str = update.as_string().into_apierror()?;
let bytes = base64::decode(js_str)?;
let mut decoder = ZlibDecoder::new(&*bytes);
let mut decoded = vec![];
decoder.read_to_end(&mut decoded)?;
Ok(rmp_serde::from_slice(&decoded)?)
} else if update.is_instance_of::<js_sys::ArrayBuffer>() {
let uint8array = js_sys::Uint8Array::new(update);
let mut slice = vec![0; uint8array.length() as usize];
uint8array.copy_to(&mut slice[..]);
Ok(rmp_serde::from_slice(&slice)?)
} else {
Ok(update.into_serde()?)
}
}
}
#[derive(Clone, Serialize)]
#[serde(untagged)]
pub enum OptionalUpdate<T: Clone> {
SetDefault,
Missing,
Update(T),
}
pub type PluginUpdate = OptionalUpdate<String>;
pub type SettingsUpdate = OptionalUpdate<bool>;
pub type ThemeUpdate = OptionalUpdate<String>;
impl<T: Clone> Default for OptionalUpdate<T> {
fn default() -> Self {
Self::Missing
}
}
impl<T: Clone> From<Option<T>> for OptionalUpdate<T> {
fn from(opt: Option<T>) -> Self {
match opt {
Some(v) => Self::Update(v),
None => Self::SetDefault,
}
}
}
impl<'a, T> Deserialize<'a> for OptionalUpdate<T>
where
T: Deserialize<'a> + Clone,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
Option::deserialize(deserializer).map(Into::into)
}
}