use std::collections::HashMap;
use crate::{value::Value, PropKey};
use gen_utils::{common::string::FixedString, err_from_to, error::Error};
pub type StyleVal = HashMap<PropKey, Value>;
pub type Style = HashMap<String, StyleVal>;
pub struct StyleVisitor;
impl StyleVisitor {
pub fn visit(
styles: &HashMap<String, HashMap<PropKey, Value>>,
id: Option<&String>,
class: Option<&Value>,
chain: &Vec<IdClass>,
) -> Result<Vec<HashMap<PropKey, Value>>, Error> {
let mut res = vec![];
if let Some(id) = id {
for (k, c_styles) in styles.iter() {
let id = format!("#{}", id);
if matcher(k, &id, chain)? {
res.push(c_styles.clone());
}
}
}
if let Some(class) = class {
let class = class_value_to_string(class)?;
for c_class in class {
for (k, c_styles) in styles.iter() {
if matcher(k, &c_class, chain)? {
res.push(c_styles.clone());
}
}
}
}
Ok(res)
}
}
fn matcher(k: &str, end: &str, chain: &Vec<IdClass>) -> Result<bool, Error> {
let k = k.split_fixed("-");
let mut flag = 0;
let mut start = 0;
let len = k.len();
for k in k.iter().rev() {
if k == &end {
flag += 1;
} else {
if flag == 0 {
break;
}
let is_id = k.starts_with("#");
let mut c_flag = false;
for (i, id_class) in chain.iter().rev().enumerate().skip(start) {
if is_id {
if let Some(c_id) = id_class.fmt_id() {
if &c_id == k {
c_flag = true;
start = i;
}
}
continue;
} else {
if let Some(c_class) = id_class.fmt_class() {
let c_class = c_class?;
if c_class.contains(&k.to_string()) {
c_flag = true;
start = i;
}
}
continue;
}
}
if c_flag {
flag += 1;
} else {
break;
}
}
}
Ok(flag == len)
}
#[derive(Debug, Clone)]
pub struct IdClass {
pub id: Option<String>,
pub class: Option<Value>,
}
impl IdClass {
pub fn split_chain(chain: &Vec<IdClass>, index: usize) -> Vec<IdClass> {
chain[..index].to_vec()
}
pub fn fmt_id(&self) -> Option<String> {
self.id.as_ref().map(|id| format!("#{}", id))
}
pub fn fmt_class(&self) -> Option<Result<Vec<String>, Error>> {
self.class
.as_ref()
.map(|class| class_value_to_string(class))
}
}
pub fn class_value_to_string(class: &Value) -> Result<Vec<String>, Error> {
match class {
Value::Vec(vec_val) => {
let mut leaf_vec = vec![];
for v in vec_val {
if let Ok(e_val) = v.as_enum() {
leaf_vec.push(
e_val
.leaf()
.map(|leaf| format!(".{}", leaf.to_string()))
.unwrap(),
);
} else {
return Err(err_from_to!("Value" => "String"));
}
}
Ok(leaf_vec)
}
Value::Enum(e_val) => Ok(vec![e_val
.leaf()
.map(|leaf| format!(".{}", leaf.to_string()))
.unwrap()]),
_ => Err(err_from_to!("Value" => "String")),
}
}