#[cfg(feature = "storage")]
pub mod sqlite;
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::selector::Selector;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ElementData {
pub tag: String,
pub attributes: HashMap<String, String>,
pub text: Option<String>,
pub path: Vec<String>,
pub parent_name: Option<String>,
pub parent_attribs: Option<HashMap<String, String>>,
pub parent_text: Option<String>,
pub siblings: Vec<String>,
pub children: Vec<String>,
}
impl ElementData {
pub fn from_selector(sel: &Selector) -> Self {
let tag = sel.tag().to_owned();
let attributes: HashMap<String, String> = sel
.attrib()
.iter()
.filter(|(_, v)| !v.trim().is_empty())
.map(|(k, v)| (k.to_owned(), v.trim().to_owned()))
.collect();
let text = trimmed_text_or_none(&sel.text());
let path: Vec<String> = {
let mut p: Vec<String> = sel.ancestors().iter().map(|a| a.tag().to_owned()).collect();
p.reverse();
p.push(tag.clone());
p
};
let (parent_name, parent_attribs, parent_text) = match sel.parent() {
Some(parent) => {
let pattribs: HashMap<String, String> = parent
.attrib()
.iter()
.map(|(k, v)| (k.to_owned(), v.to_string()))
.collect();
(
Some(parent.tag().to_owned()),
Some(pattribs),
trimmed_text_or_none(&parent.text()),
)
}
None => (None, None, None),
};
let siblings: Vec<String> = sel.siblings().iter().map(|s| s.tag().to_owned()).collect();
let children: Vec<String> = sel.children().iter().map(|c| c.tag().to_owned()).collect();
Self {
tag,
attributes,
text,
path,
parent_name,
parent_attribs,
parent_text,
siblings,
children,
}
}
}
fn trimmed_text_or_none(text: &str) -> Option<String> {
let trimmed = text.trim();
if trimmed.is_empty() {
None
} else {
Some(trimmed.to_owned())
}
}
pub trait StorageSystem {
fn save(&self, data: &ElementData, identifier: &str) -> crate::Result<()>;
fn retrieve(&self, identifier: &str) -> crate::Result<Option<ElementData>>;
}