1 use rustc_data_structures::fx::FxHashSet;
3 use std::hash::{Hash, Hasher};
11 macro_rules! try_something {
12 ($e:expr, $diag:expr, $out:expr) => ({
16 $diag.struct_err(&e.to_string()).emit();
23 #[derive(Debug, Clone, Eq)]
26 pub children: FxHashSet<CssPath>,
29 // This PartialEq implementation IS NOT COMMUTATIVE!!!
31 // The order is very important: the second object must have all first's rules.
32 // However, the first doesn't require to have all second's rules.
33 impl PartialEq for CssPath {
34 fn eq(&self, other: &CssPath) -> bool {
35 if self.name != other.name {
38 for child in &self.children {
39 if !other.children.iter().any(|c| child == c) {
48 impl Hash for CssPath {
49 fn hash<H: Hasher>(&self, state: &mut H) {
50 self.name.hash(state);
51 for x in &self.children {
58 fn new(name: String) -> CssPath {
61 children: FxHashSet::default(),
66 /// All variants contain the position they occur.
67 #[derive(Debug, Clone, Copy)]
69 StartLineComment(usize),
77 fn get_pos(&self) -> usize {
79 Events::StartLineComment(p) |
80 Events::StartComment(p) |
81 Events::EndComment(p) |
83 Events::OutBlock(p) => p,
87 fn is_comment(&self) -> bool {
89 Events::StartLineComment(_) |
90 Events::StartComment(_) |
91 Events::EndComment(_) => true,
97 fn previous_is_line_comment(events: &[Events]) -> bool {
98 if let Some(&Events::StartLineComment(_)) = events.last() {
105 fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool {
106 if let Some(&Events::StartComment(_)) = events.last() {
112 fn load_css_events(v: &[u8]) -> Vec<Events> {
114 let mut events = Vec::with_capacity(100);
116 while pos + 1 < v.len() {
118 b'/' if v[pos + 1] == b'*' => {
119 events.push(Events::StartComment(pos));
122 b'/' if is_line_comment(pos, v, &events) => {
123 events.push(Events::StartLineComment(pos));
126 b'\n' if previous_is_line_comment(&events) => {
127 events.push(Events::EndComment(pos));
129 b'*' if v[pos + 1] == b'/' => {
130 events.push(Events::EndComment(pos + 2));
133 b'{' if !previous_is_line_comment(&events) => {
134 if let Some(&Events::StartComment(_)) = events.last() {
138 events.push(Events::InBlock(pos + 1));
140 b'}' if !previous_is_line_comment(&events) => {
141 if let Some(&Events::StartComment(_)) = events.last() {
145 events.push(Events::OutBlock(pos + 1));
154 fn get_useful_next(events: &[Events], pos: &mut usize) -> Option<Events> {
155 while *pos < events.len() {
156 if !events[*pos].is_comment() {
157 return Some(events[*pos]);
164 fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec<usize> {
165 let mut ret = Vec::with_capacity(3);
167 ret.push(events[pos].get_pos());
172 if pos < 1 || !events[pos].is_comment() {
173 let x = events[pos].get_pos();
174 if *ret.last().unwrap() != x {
181 ret.push(events[pos].get_pos());
184 if ret.len() & 1 != 0 && events[pos].is_comment() {
187 ret.iter().rev().cloned().collect()
190 fn build_rule(v: &[u8], positions: &[usize]) -> String {
192 .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or(""))
201 .filter(|s| s.len() > 0)
202 .collect::<Vec<&str>>()
206 fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> FxHashSet<CssPath> {
207 let mut paths = Vec::with_capacity(50);
209 while *pos < events.len() {
210 if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
214 if let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
215 paths.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos))));
218 while let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
219 if let Some(ref mut path) = paths.last_mut() {
220 for entry in inner(v, events, pos).iter() {
221 path.children.insert(entry.clone());
225 if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
229 paths.iter().cloned().collect()
232 pub fn load_css_paths(v: &[u8]) -> CssPath {
233 let events = load_css_events(v);
236 let mut parent = CssPath::new("parent".to_owned());
237 parent.children = inner(v, &events, &mut pos);
241 pub fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec<String>) {
242 if against.name != other.name {
245 for child in &against.children {
246 let mut found = false;
247 let mut found_working = false;
248 let mut tmp = Vec::new();
250 for other_child in &other.children {
251 if child.name == other_child.name {
252 if child != other_child {
253 get_differences(child, other_child, &mut tmp);
255 found_working = true;
262 v.push(format!(" Missing \"{}\" rule", child.name));
263 } else if found_working == false {
264 v.extend(tmp.iter().cloned());
270 pub fn test_theme_against<P: AsRef<Path>>(
274 ) -> (bool, Vec<String>) {
275 let data = try_something!(fs::read(f), diag, (false, vec![]));
276 let paths = load_css_paths(&data);
277 let mut ret = vec![];
278 get_differences(against, &paths, &mut ret);