use std::collections::HashMap;
use std::convert::TryFrom;
use std::iter::Iterator;
use either::Either;
use num_enum::TryFromPrimitive;
use serde::Serialize;
use uuid::Uuid;
use crate::language::Color;
use crate::language::StartUpPosition;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PropertyGroup {
pub name: String,
pub guid: Option<Uuid>,
pub properties: HashMap<String, Either<String, PropertyGroup>>,
}
impl Serialize for PropertyGroup {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("VB6PropertyGroup", 3)?;
state.serialize_field("name", &self.name)?;
if let Some(guid) = &self.guid {
state.serialize_field("guid", &guid.to_string())?;
} else {
state.serialize_field("guid", &"None")?;
}
state.serialize_field("properties", &self.properties)?;
state.end()
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Properties {
key_value_store: HashMap<String, String>,
}
impl AsRef<HashMap<String, String>> for Properties {
fn as_ref(&self) -> &HashMap<String, String> {
&self.key_value_store
}
}
pub struct PropertiesIter<'a> {
iter: std::collections::hash_map::Iter<'a, String, String>,
}
impl Properties {
#[must_use]
pub fn iter(&self) -> PropertiesIter<'_> {
PropertiesIter {
iter: self.key_value_store.iter(),
}
}
}
impl<'a> IntoIterator for &'a Properties {
type Item = (&'a String, &'a String);
type IntoIter = PropertiesIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> Iterator for PropertiesIter<'a> {
type Item = (&'a String, &'a String);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<K, V> FromIterator<(K, V)> for Properties
where
K: Into<String>,
V: Into<String>,
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
let mut props = Properties::new();
for (k, v) in iter {
props.insert(k, v);
}
props
}
}
impl<K, V> Extend<(K, V)> for Properties
where
K: Into<String>,
V: Into<String>,
{
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
for (k, v) in iter {
self.insert(k, v);
}
}
}
impl Properties {
#[must_use]
pub fn new() -> Self {
Properties {
key_value_store: HashMap::new(),
}
}
pub fn insert(&mut self, property_key: impl Into<String>, value: impl Into<String>) {
self.key_value_store
.insert(property_key.into(), value.into());
}
#[must_use]
pub fn len(&self) -> usize {
self.key_value_store.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.key_value_store.is_empty()
}
#[must_use]
pub fn contains_key(&self, property_key: &str) -> bool {
self.key_value_store.contains_key(property_key)
}
#[must_use]
pub fn keys(&self) -> Vec<&String> {
self.key_value_store.keys().collect()
}
pub fn remove(&mut self, property_key: &str) -> Option<String> {
self.key_value_store.remove(property_key)
}
pub fn clear(&mut self) {
self.key_value_store.clear();
}
#[must_use]
pub fn get(&self, property_key: &str) -> Option<&String> {
self.key_value_store.get(property_key)
}
#[must_use]
pub fn get_bool(&self, property_key: &str, default: bool) -> bool {
match self.key_value_store.get(property_key) {
Some(value) => match value.as_str() {
"0" => false,
"1" | "-1" => true,
_ => default,
},
None => default,
}
}
#[must_use]
pub fn get_color(&self, property_key: &str, default: Color) -> Color {
match self.key_value_store.get(property_key) {
Some(value) => Color::from_hex(value).unwrap_or(default),
None => default,
}
}
#[must_use]
pub fn get_i32(&self, property_key: &str, default: i32) -> i32 {
match self.key_value_store.get(property_key) {
Some(value) => value.parse::<i32>().unwrap_or(default),
None => default,
}
}
#[must_use]
pub fn get_property<T>(&self, property_key: &str, default: T) -> T
where
T: TryFrom<i32> + TryFromPrimitive,
{
match self.key_value_store.get(property_key) {
Some(value) => value
.parse::<i32>()
.ok()
.and_then(|v| T::try_from(v).ok())
.unwrap_or(default),
None => default,
}
}
#[must_use]
pub fn get_startup_position(
&self,
property_key: &str,
default: StartUpPosition,
) -> StartUpPosition {
match self.key_value_store.get(property_key) {
Some(value) => {
match value.parse::<i32>() {
Ok(0) => {
let client_height = self.get_i32("ClientHeight", 3000);
let client_width = self.get_i32("ClientWidth", 3000);
let client_top = self.get_i32("ClientTop", 200);
let client_left = self.get_i32("ClientLeft", 100);
StartUpPosition::Manual {
client_height,
client_width,
client_top,
client_left,
}
}
Ok(1) => StartUpPosition::CenterOwner,
Ok(2) => StartUpPosition::CenterScreen,
_ => StartUpPosition::WindowsDefault,
}
}
None => default,
}
}
#[must_use]
pub fn get_option<T>(&self, property_key: &str, default: Option<T>) -> Option<T>
where
T: for<'a> TryFrom<&'a str>,
{
match self.key_value_store.get(property_key) {
Some(value) => T::try_from(value.as_str()).ok().or(default),
None => default,
}
}
}