1use crate::parser::NodeRef;
2use std::collections::HashMap;
3
4#[derive(Clone, Copy, Debug, Default)]
5struct NodeMeta {
6 readability_score: Option<f64>,
7 is_readability_data_table: bool,
8}
9
10#[derive(Default, Debug, Clone)]
29pub struct NodeScoreStore {
30 map: HashMap<usize, NodeMeta>,
31}
32
33fn node_key(node: &NodeRef) -> usize {
37 let ptr: *const _ = &**node;
38 ptr as usize
39}
40
41pub trait NodeScoreExt {
47 fn readability_score(&self, store: &NodeScoreStore) -> Option<f64>;
50
51 fn offset_readability_score(&self, store: &mut NodeScoreStore, offset: f64);
54
55 fn set_readability_score(&self, store: &mut NodeScoreStore, value: Option<f64>);
57
58 fn is_readability_data_table(&self, store: &NodeScoreStore) -> bool;
61
62 fn set_readability_data_table(&self, store: &mut NodeScoreStore, is_readability_data_table: bool);
65}
66
67impl NodeScoreExt for NodeRef {
68 fn readability_score(&self, store: &NodeScoreStore) -> Option<f64> {
69 let key = node_key(self);
70 store.map.get(&key).and_then(|m| m.readability_score)
71 }
72
73 fn offset_readability_score(&self, store: &mut NodeScoreStore, offset: f64) {
74 let key = node_key(self);
75 let entry = store.map.entry(key).or_default();
76 entry.readability_score = Some(entry.readability_score.unwrap_or(0.0) + offset);
77 }
78
79 fn set_readability_score(&self, store: &mut NodeScoreStore, value: Option<f64>) {
80 let key = node_key(self);
81 let entry = store.map.entry(key).or_default();
82 entry.readability_score = value;
83 }
84
85 fn is_readability_data_table(&self, store: &NodeScoreStore) -> bool {
86 let key = node_key(self);
87 store
88 .map
89 .get(&key)
90 .map(|m| m.is_readability_data_table)
91 .unwrap_or(false)
92 }
93
94 fn set_readability_data_table(&self, store: &mut NodeScoreStore, is_readability_data_table: bool) {
95 let key = node_key(self);
96 let entry = store.map.entry(key).or_default();
97 entry.is_readability_data_table = is_readability_data_table;
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::parser::parse_html;
105
106 #[test]
107 fn store_isolated_per_instance() {
108 let doc = parse_html("<div><p>Hello</p></div>");
109 let p = doc.select_first("p").unwrap().as_node().clone();
110 let mut store_a = NodeScoreStore::default();
111 let mut store_b = NodeScoreStore::default();
112
113 p.set_readability_score(&mut store_a, Some(10.0));
114 assert_eq!(p.readability_score(&store_a), Some(10.0));
115 assert_eq!(p.readability_score(&store_b), None);
116
117 p.set_readability_score(&mut store_b, Some(5.0));
118 assert_eq!(p.readability_score(&store_a), Some(10.0));
119 assert_eq!(p.readability_score(&store_b), Some(5.0));
120 }
121}