use super::{CommandDetail, Device, Response};
use crate::error::{ClientError, Error, Result};
use async_trait::async_trait;
use serde::{de, Deserialize, Serialize};
use serde_json::Value;
use std::fmt::{self, Debug};
use std::result::Result as StdResult;
#[async_trait]
pub trait Write<T> {
async fn write(&self, new_value: T) -> Result<()>;
}
#[derive(Debug, Clone)]
pub enum EndpointBase {
Static,
Dynamic,
}
impl EndpointBase {
pub fn as_str(&self) -> String {
String::from("/menu_native")
+ match self {
Self::Static => "/static",
Self::Dynamic => "/dynamic",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SettingType {
Slider,
Value,
Menu,
List,
XList,
#[doc(hidden)]
Other(String),
}
impl<'de> Deserialize<'de> for SettingType {
fn deserialize<D>(deserializer: D) -> StdResult<SettingType, D::Error>
where
D: de::Deserializer<'de>,
{
Ok(match String::deserialize(deserializer)?.as_str() {
"T_VALUE_ABS_V1" => SettingType::Slider,
"T_LIST_V1" => SettingType::List,
"T_VALUE_V1" => SettingType::Value,
"T_MENU_V1" => SettingType::Menu,
"T_LIST_X_V1" => SettingType::XList,
other => SettingType::Other(other.into()),
})
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct SliderInfo {
#[serde(rename = "DECMARKER")]
#[serde(default)]
pub dec_marker: String,
#[serde(rename = "INCMARKER")]
#[serde(default)]
pub inc_marker: String,
pub increment: i32,
#[serde(rename = "MAXIMUM")]
pub max: i32,
#[serde(rename = "MINIMUM")]
pub min: i32,
pub center: Option<i32>,
}
#[derive(Clone, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct SubSetting {
#[serde(rename = "CNAME")]
endpoint: String,
hashval: Option<u32>,
#[serde(deserialize_with = "string_to_bool", default)]
hidden: bool,
name: String,
#[serde(deserialize_with = "string_to_bool", default)]
readonly: bool,
#[serde(rename = "TYPE")]
object_type: SettingType,
value: Option<Value>, #[serde(skip)]
device: Option<Device>,
}
impl fmt::Debug for SubSetting {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut d = f.debug_struct("SubSetting");
d.field("name", &self.name);
d.field("value", &self.value);
d.field("hidden", &self.hidden);
d.field("read_only", &self.readonly);
d.field("object_type", &self.object_type);
d.finish()
}
}
impl SubSetting {
pub async fn expand(&self) -> Result<Vec<SubSetting>> {
log::trace!("SubSetting Expand");
if !matches!(self.object_type, SettingType::Menu) {
return Ok(vec![self.clone()]);
}
let mut settings: Vec<SubSetting> = self.dynamic_response().await?.settings()?;
for s in settings.iter_mut() {
s.add_parent_data(self);
if s.object_type == SettingType::Value {
s.object_type = SettingType::Slider;
if s.slider_info().await?.is_none() {
s.object_type = SettingType::Value;
}
}
}
Ok(settings)
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn hidden(&self) -> bool {
self.hidden
}
pub fn read_only(&self) -> bool {
self.readonly
}
pub fn setting_type(&self) -> SettingType {
self.object_type.clone()
}
pub fn is_boolean(&self) -> bool {
if let Some(value) = self.value.clone() {
value.is_boolean()
} else {
false
}
}
pub fn is_string(&self) -> bool {
if let Some(value) = self.value.clone() {
value.is_string()
} else {
false
}
}
pub fn is_number(&self) -> bool {
if let Some(value) = self.value.clone() {
value.is_number()
} else {
false
}
}
pub fn value<T>(&self) -> Option<T>
where
T: for<'de> Deserialize<'de>,
{
if let Some(value) = self.value.clone() {
serde_json::from_value(value).ok()
} else {
None
}
}
pub async fn update<T>(&self, new_value: T) -> Result<()>
where
SubSetting: Write<T>,
T: Serialize + for<'de> Deserialize<'de> + Debug,
{
log::trace!("Update SubSetting");
if matches!(self.object_type, SettingType::Menu)
|| self.readonly
|| self.value.is_none()
|| self.hashval.is_none()
{
Err(ClientError::WriteSettingsReadOnly.into())
}
else if let Err(_e) = serde_json::from_value::<T>(self.value.clone().unwrap()) {
Err(Error::setting_type_bad_match(
self.value.clone().unwrap(),
serde_json::json!(new_value),
))
}
else {
self.write(new_value).await
}
}
pub async fn slider_info(&self) -> Result<Option<SliderInfo>> {
log::trace!("Get Slider Info");
if self.object_type == SettingType::Slider {
match self.static_response().await?.slider_info() {
Some(info) => Ok(Some(info)),
None => Ok(self.dynamic_response().await?.slider_info()),
}
} else {
Ok(None)
}
}
pub async fn elements(&self) -> Result<Vec<String>> {
log::trace!("Get Elements");
if self.object_type == SettingType::List || self.object_type == SettingType::XList {
match self.dynamic_response().await?.elements() {
Ok(elements) => Ok(elements),
Err(_) => Ok(self.static_response().await?.elements().unwrap_or_default()),
}
} else {
Ok(Vec::new())
}
}
pub(super) fn endpoint(&self) -> String {
self.endpoint.clone()
}
async fn dynamic_response(&self) -> Result<Response> {
log::trace!("Get Dynamic Response");
let device = self.device.clone().unwrap();
Ok(device
.send_command(CommandDetail::ReadSettings(
EndpointBase::Dynamic,
self.endpoint(),
))
.await?)
}
async fn static_response(&self) -> Result<Response> {
log::trace!("Get Static Response");
let device = self.device.clone().unwrap();
Ok(device
.send_command(CommandDetail::ReadSettings(
EndpointBase::Static,
self.endpoint(),
))
.await?)
}
async fn root(device: Device) -> Result<Vec<SubSetting>> {
log::trace!("Get Settings Root");
let root = SubSetting {
endpoint: format!("/{}", device.settings_root()),
hashval: None,
hidden: false,
name: "".into(),
readonly: false,
object_type: SettingType::Menu,
value: None,
device: Some(device.clone()),
};
root.expand().await
}
fn add_parent_data(&mut self, parent: &SubSetting) {
self.device = parent.device.clone();
self.endpoint = format!("{}/{}", parent.endpoint, self.endpoint);
}
}
#[async_trait]
impl Write<String> for SubSetting {
async fn write(&self, new_value: String) -> Result<()> {
match self.setting_type() {
SettingType::List | SettingType::XList => {
if !self.elements().await?.contains(&new_value) {
return Err(Error::setting_non_element());
}
}
SettingType::Value => {}
_ => {
panic!("Bad Type")
}
}
let device = self.device.clone().unwrap();
device
.send_command(CommandDetail::WriteSettings(
self.endpoint.clone(),
self.hashval.unwrap(),
serde_json::json!(new_value),
))
.await
.map(drop)
}
}
#[async_trait]
impl Write<i32> for SubSetting {
async fn write(&self, new_value: i32) -> Result<()> {
match self.setting_type() {
SettingType::Value => {}
SettingType::Slider => {
let slider_info = self.slider_info().await?.unwrap();
if new_value > slider_info.max || new_value < slider_info.min {
return Err(Error::setting_outside_bounds(
slider_info.min,
slider_info.max,
new_value,
));
}
}
_ => {
panic!("Bad Type")
}
}
let device = self.device.clone().unwrap();
device
.send_command(CommandDetail::WriteSettings(
self.endpoint.clone(),
self.hashval.unwrap(),
serde_json::json!(new_value),
))
.await
.map(drop)
}
}
#[async_trait]
impl Write<bool> for SubSetting {
async fn write(&self, new_value: bool) -> Result<()> {
if matches!(self.setting_type(), SettingType::Value) {
let device = self.device.clone().unwrap();
device
.send_command(CommandDetail::WriteSettings(
self.endpoint.clone(),
self.hashval.unwrap(),
serde_json::json!(new_value),
))
.await
.map(drop)
} else {
panic!("Bad Type")
}
}
}
#[async_trait]
impl Write<f64> for SubSetting {
async fn write(&self, new_value: f64) -> Result<()> {
self.write(new_value as i32).await
}
}
#[async_trait]
impl Write<f32> for SubSetting {
async fn write(&self, new_value: f32) -> Result<()> {
self.write(new_value as i32).await
}
}
#[async_trait]
impl Write<u128> for SubSetting {
async fn write(&self, new_value: u128) -> Result<()> {
self.write(new_value as f64).await
}
}
#[async_trait]
impl Write<u64> for SubSetting {
async fn write(&self, new_value: u64) -> Result<()> {
self.write(new_value as f64).await
}
}
#[async_trait]
impl Write<u32> for SubSetting {
async fn write(&self, new_value: u32) -> Result<()> {
self.write(new_value as f64).await
}
}
#[async_trait]
impl Write<u16> for SubSetting {
async fn write(&self, new_value: u16) -> Result<()> {
self.write(new_value as i32).await
}
}
#[async_trait]
impl Write<u8> for SubSetting {
async fn write(&self, new_value: u8) -> Result<()> {
self.write(new_value as i32).await
}
}
#[async_trait]
impl Write<i128> for SubSetting {
async fn write(&self, new_value: i128) -> Result<()> {
self.write(new_value as f64).await
}
}
#[async_trait]
impl Write<i64> for SubSetting {
async fn write(&self, new_value: i64) -> Result<()> {
self.write(new_value as f64).await
}
}
#[async_trait]
impl Write<i16> for SubSetting {
async fn write(&self, new_value: i16) -> Result<()> {
self.write(new_value as i32).await
}
}
#[async_trait]
impl Write<i8> for SubSetting {
async fn write(&self, new_value: i8) -> Result<()> {
self.write(new_value as i32).await
}
}
pub async fn root(device: Device) -> Result<Vec<SubSetting>> {
SubSetting::root(device).await
}
fn string_to_bool<'de, D>(deserializer: D) -> StdResult<bool, D::Error>
where
D: de::Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;
string
.to_lowercase()
.parse::<bool>()
.map_err(|_| de::Error::invalid_type(de::Unexpected::Str(&string), &"a boolean"))
}