stripper_lib/
utils.rs

1// Copyright 2015 Gomez Guillaume
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use consts::{END_INFO, FILE, FILE_COMMENT, MOD_COMMENT, OUTPUT_COMMENT_FILE};
16use std::ffi::OsStr;
17use std::fs;
18use std::io;
19use std::io::prelude::*;
20use std::path::Path;
21use types::TypeStruct;
22
23use crate::Type;
24
25pub fn loop_over_files<S>(
26    path: &Path,
27    func: &mut dyn FnMut(&Path, &str),
28    files_to_ignore: &[S],
29    verbose: bool,
30) where
31    S: AsRef<Path>,
32{
33    do_loop_over_files(path, path, func, files_to_ignore, verbose)
34}
35
36pub fn do_loop_over_files<S>(
37    work_dir: &Path,
38    path: &Path,
39    func: &mut dyn FnMut(&Path, &str),
40    files_to_ignore: &[S],
41    verbose: bool,
42) where
43    S: AsRef<Path>,
44{
45    match fs::read_dir(path) {
46        Ok(it) => {
47            let mut entries = vec![];
48
49            for entry in it {
50                entries.push(entry.unwrap().path().to_owned());
51            }
52            entries.sort();
53            for entry in entries {
54                check_path_type(work_dir, &entry, func, files_to_ignore, verbose);
55            }
56        }
57        Err(e) => {
58            println!(
59                "Error while trying to iterate over {}: {}",
60                path.display(),
61                e
62            );
63        }
64    }
65}
66
67fn check_path_type<S>(
68    work_dir: &Path,
69    path: &Path,
70    func: &mut dyn FnMut(&Path, &str),
71    files_to_ignore: &[S],
72    verbose: bool,
73) where
74    S: AsRef<Path>,
75{
76    match fs::metadata(path) {
77        Ok(m) => {
78            if m.is_dir() {
79                if path == Path::new("..") || path == Path::new(".") {
80                    return;
81                }
82                do_loop_over_files(work_dir, path, func, files_to_ignore, verbose);
83            } else {
84                let path_suffix = strip_prefix(path, work_dir).unwrap();
85                let ignore = path == Path::new(&format!("./{}", OUTPUT_COMMENT_FILE))
86                    || path.extension() != Some(OsStr::new("rs"))
87                    || files_to_ignore.iter().any(|s| s.as_ref() == path_suffix);
88                if ignore {
89                    if verbose {
90                        println!("-> {}: ignored", path.display());
91                    }
92                    return;
93                }
94                if verbose {
95                    println!("-> {}", path.display());
96                }
97                func(work_dir, path_suffix.to_str().unwrap());
98            }
99        }
100        Err(e) => {
101            println!("An error occurred on '{}': {}", path.display(), e);
102        }
103    }
104}
105
106pub fn join(s: &[String], join_part: &str) -> String {
107    let mut ret = String::new();
108    let mut it = 0;
109
110    while it < s.len() {
111        ret.push_str(&s[it]);
112        it += 1;
113        if it < s.len() {
114            ret.push_str(join_part);
115        }
116    }
117    ret
118}
119
120// lifted from libstd for Path::strip_prefix is unstable
121
122fn strip_prefix<'a>(self_: &'a Path, base: &'a Path) -> Result<&'a Path, ()> {
123    iter_after(self_.components(), base.components())
124        .map(|c| c.as_path())
125        .ok_or(())
126}
127
128fn iter_after<A, I, J>(mut iter: I, mut prefix: J) -> Option<I>
129where
130    I: Iterator<Item = A> + Clone,
131    J: Iterator<Item = A>,
132    A: PartialEq,
133{
134    loop {
135        let mut iter_next = iter.clone();
136        match (iter_next.next(), prefix.next()) {
137            (Some(x), Some(y)) => {
138                if x != y {
139                    return None;
140                }
141            }
142            (Some(_), None) => return Some(iter),
143            (None, None) => return Some(iter),
144            (None, Some(_)) => return None,
145        }
146        iter = iter_next;
147    }
148}
149
150pub fn write_comment(id: &TypeStruct, comment: &str, ignore_macro: bool) -> String {
151    if ignore_macro {
152        format!("{}{}{}\n{}", MOD_COMMENT, id, END_INFO, comment)
153    } else {
154        format!("{}{:?}{}\n{}", MOD_COMMENT, id, END_INFO, comment)
155    }
156}
157pub fn write_item_doc<F>(w: &mut dyn Write, id: &TypeStruct, f: F) -> io::Result<()>
158where
159    F: FnOnce(&mut dyn Write) -> io::Result<()>,
160{
161    writeln!(w, "{}{}{}", MOD_COMMENT, id, END_INFO)?;
162    f(w)
163}
164
165pub fn write_file_comment(comment: &str, id: &Option<TypeStruct>, ignore_macro: bool) -> String {
166    if let Some(ref t) = *id {
167        if ignore_macro {
168            format!("{} {}{}\n{}", FILE_COMMENT, t, END_INFO, comment)
169        } else {
170            format!("{} {:?}{}\n{}", FILE_COMMENT, t, END_INFO, comment)
171        }
172    } else {
173        format!("{}{}\n{}", FILE_COMMENT, END_INFO, comment)
174    }
175}
176
177pub fn write_file(file: &str) -> String {
178    format!("{}{}{}", FILE, file, END_INFO)
179}
180
181pub fn write_file_name(w: &mut dyn Write, name: Option<&str>) -> io::Result<()> {
182    writeln!(w, "{}{}{}", FILE, name.unwrap_or("*"), END_INFO)
183}
184
185/// If the [`TypeStruct`]'s oldest parent (the parent with a *parent*=None) is a [`Type::Macro`][crate::Type::Macro],
186/// this funciton will remove that parent.
187///
188/// Useful when trying to compare equality of a `struct SomeStruct` in the doc file,
189/// and `SomeStruct` in the source code is wrapped inside a macro.
190/// In the resulting code, `SomeStruct` would end up outside of the macro,
191/// so even if the perceived path of `SomeStruct` is `macro SomeMacro::struct SomeStruct`,
192/// the content from `struct SomeStruct` has to be written there.
193///
194/// This funciton is recursive.
195pub fn remove_macro_parent(tys: &mut TypeStruct) {
196    if let Some(parent) = &mut tys.parent {
197        if parent.ty == Type::Macro {
198            tys.parent = None;
199        } else {
200            remove_macro_parent(parent)
201        }
202    }
203}