1use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
50use std::fs::create_dir_all;
51use std::path::{Path, PathBuf};
52use walkdir::WalkDir;
53
54pub fn copy_glob(glob: impl AsRef<str>, output_dir: impl AsRef<Path>) {
55 let options = CopyGlobOptionsBuilder::new().build();
56 copy_glob_with(glob, output_dir, &options);
57}
58
59pub fn copy_glob_with(
60 glob: impl AsRef<str>,
61 output_dir: impl AsRef<Path>,
62 options: &CopyGlobOptions,
63) {
64 let glob = glob.as_ref();
65 let matcher = new_glob(glob);
66 let current_dir = options.root.as_ref();
67
68 create_dir_all(&output_dir).unwrap();
69
70 for entry in WalkDir::new(¤t_dir) {
71 let entry = entry.expect("Unable to read dir");
72 if options.exclude.is_match(entry.path()) || !matcher.is_match(entry.path()) {
73 continue;
74 }
75
76 let path = entry.path().display();
77 println!("cargo:rerun-if-changed={path}");
78
79 let metadata = entry.metadata().expect("Unable to get metadata");
80 let dirname = get_str(entry.path())
81 .strip_prefix(get_str(current_dir))
82 .expect("Unable to strip a string")
83 .trim_start_matches("\\")
84 .trim_start_matches("/");
85 let output_path = output_dir.as_ref().join(dirname);
86
87 if metadata.is_dir() {
88 create_dir_all(output_path).expect("Unable to create output dir");
89 } else if metadata.is_file() {
90 let parent = output_path.parent().unwrap();
91
92 if parent.exists() {
93 std::fs::copy(entry.path(), output_path).expect("Unable to copy file");
94 } else {
95 std::fs::copy(entry.path(), output_dir.as_ref().join(entry.file_name()))
96 .expect("Unable to copy file");
97 }
98 }
99 }
100}
101
102pub struct CopyGlobOptions {
103 root: PathBuf,
104 exclude: GlobSet,
105}
106
107pub struct CopyGlobOptionsBuilder {
108 root: PathBuf,
109 exclude: GlobSetBuilder,
110}
111
112impl CopyGlobOptionsBuilder {
113 pub fn new() -> Self {
114 let root = get_root_path();
115 let mut exclude = GlobSet::builder();
116
117 exclude.add(Glob::new("**/target/*").unwrap());
118
119 Self { root, exclude }
120 }
121}
122
123impl CopyGlobOptionsBuilder {
124 pub fn set_root_path(mut self, root: impl AsRef<Path>) -> Self {
125 self.root = root.as_ref().to_path_buf();
126 self
127 }
128
129 pub fn add_exclude(mut self, glob: impl AsRef<str>) -> Self {
130 self.exclude.add(Glob::new(glob.as_ref()).unwrap());
131 self
132 }
133
134 pub fn build(self) -> CopyGlobOptions {
135 CopyGlobOptions {
136 root: self.root,
137 exclude: self.exclude.build().unwrap(),
138 }
139 }
140}
141
142pub fn new_glob(glob: impl AsRef<str>) -> GlobMatcher {
143 Glob::new(glob.as_ref())
144 .expect("Wrong pattern")
145 .compile_matcher()
146}
147
148pub fn get_target_folder() -> PathBuf {
149 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target");
150
151 #[cfg(debug_assertions)]
152 return path.join("debug");
153
154 #[cfg(not(debug_assertions))]
155 return path.join("release");
156}
157
158pub fn get_root_path() -> PathBuf {
159 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
160}
161
162fn get_str(path: &Path) -> &str {
163 path.to_str().expect("Unable to convert path to str")
164}