1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use regex::Regex;
pub fn cssifier<S: AsRef<str>>(xpath: S) -> Option<String> {
let xpath = xpath.as_ref();
let reg = Regex::new(r#"(?P<node>(^id\(["']?(?P<idvalue>\s*[\w/:][-/\w\s,:;.]*)["']?\)|(?P<nav>//?)(?P<tag>([a-zA-Z][a-zA-Z0-9]{0,10}|\*))(\[((?P<matched>(?P<mattr>@?[.a-zA-Z_:][-\w:.]*(\(\))?)=["'](?P<mvalue>\s*[\w/:][-/\w\s,:;.]*))["']|(?P<contained>contains\((?P<cattr>@?[.a-zA-Z_:][-\w:.]*(\(\))?),\s*["'](?P<cvalue>\s*[\w/:][-/\w\s,:;.]*)["']\)))\])?(\[(?P<nth>\d)\])?))"#).unwrap();
let mut css = String::new();
let mut position = 0;
while position < xpath.len() {
let node = reg.captures(&xpath[position..])?;
let find = reg.find(&xpath[position..])?;
let nav = match position {
0 => "",
_ => {
if node.name("nav")?.as_str() != "//" {
" "
} else {
" > "
}
}
};
let tag = if node.name("tag")?.as_str() == "*" {
""
} else {
match node.name("tag") {
Some(tag) => tag.as_str(),
_ => "",
}
};
let attr = if node.name("idvalue").is_some() {
format!("#{}", node.name("idvalue")?.as_str().replace(" ", "#"))
} else if node.name("matched").is_some() {
let mattr = node.name("mattr")?.as_str();
let mvalue = node.name("mvalue")?.as_str();
if mattr == "@id" {
format!("#{}", mvalue.replace(" ", "#"))
} else if mattr == "@class" {
format!(".{}", mvalue.replace(" ", "."))
} else if mattr == "text()" || mattr == "." {
format!(":contains(^{}$)", mvalue)
} else if mattr != "" {
let new_mvalue = if mvalue.find(" ").is_some() {
format!("\"{}\"", mvalue)
} else {
mvalue.to_string()
};
format!("[{}={}]", mattr.replace("@", ""), new_mvalue)
} else {
String::from("")
}
} else if node.name("contained").is_some() {
let cattr = node.name("cattr")?.as_str();
let cvalue = node.name("cvalue")?.as_str();
if cattr.starts_with("@") {
format!("[{}*={}]", cattr.replace("@", ""), cvalue)
} else if cattr == "text()" {
format!(":contains({})", cvalue)
} else {
String::from("")
}
} else {
String::from("")
};
let nth = if node.name("nth").is_some() {
format!(":nth-of-type({})", node.name("nth")?.as_str())
} else {
String::from("")
};
css = format!("{}{}{}{}{}", css, nav, tag, attr, nth);
position += find.end();
}
Some(css)
}
#[cfg(test)]
mod tests {
use super::cssifier;
#[test]
fn it_works() {
assert_eq!(cssifier("//a/b").unwrap(), "a b");
assert_eq!(cssifier("//a/b[@id='hello']").unwrap(), "a b#hello");
assert_eq!(
cssifier("//a/b[contains(text(), 'hello')]").unwrap(),
"a b:contains(hello)"
);
assert_eq!(cssifier("*random shit//*").unwrap(), "");
}
}