1use super::compile::CompileRunner;
2use crate::*;
3use glob::glob;
4use std::path::PathBuf;
5use tracing_error::SpanTrace;
6
7pub struct Rule {
10 name: String,
11 globs: Option<Vec<String>>,
12 creates: Option<Vec<String>>,
13 compiler: Box<dyn Compiler>,
14 version: Version,
15}
16
17impl Rule {
18 pub fn new(name: impl AsRef<str>, compiler: impl Compiler + 'static) -> Self {
19 let name = name.as_ref().to_owned();
20 Rule {
21 name,
22 globs: None,
23 creates: None,
24 compiler: Box::new(compiler),
25 version: Version::default(),
26 }
27 }
28
29 pub fn get_name(&self) -> &str {
31 &self.name
32 }
33
34 pub fn set_globs(mut self, globs: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
36 self.globs = Some(globs.into_iter().map(|s| s.as_ref().to_owned()).collect());
37 self
38 }
39 pub fn set_create(mut self, create: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
41 self.creates = Some(create.into_iter().map(|s| s.as_ref().to_owned()).collect());
42 self
43 }
44
45 pub fn set_version(mut self, version: impl Into<Version>) -> Self {
47 self.version = version.into();
48 self
49 }
50
51 #[tracing::instrument(skip(self, ctx))]
53 pub(crate) async fn compile(self, ctx: Context) -> Result<Context, Error> {
54 let src_dir = ctx.config().source_dir();
55 let paths: Vec<_> = match (&self.globs, &self.creates) {
56 (Some(globs), None) => {
57 let globs = globs
58 .iter()
59 .map(|g| src_dir.join(PathBuf::from(g)).to_string_lossy().to_string());
60 let paths = globs
61 .map(|g| glob(&g))
62 .collect::<Result<Vec<_>, _>>()
63 .map_err(|_| Error::InvalidRule {
64 trace: SpanTrace::capture(),
65 })?
66 .into_iter()
67 .map(|p| p.collect::<Result<Vec<_>, _>>())
68 .collect::<Result<Vec<_>, _>>()
69 .map_err(|_| Error::InvalidRule {
70 trace: SpanTrace::capture(),
71 })?
72 .into_iter()
73 .flatten()
74 .filter(|p| p.is_file())
75 .collect();
76 paths
77 }
78 (None, Some(paths)) => {
79 let paths = paths
80 .iter()
81 .map(|p| src_dir.join(PathBuf::from(p)))
82 .collect();
83 paths
84 }
85 _ => {
86 return Err(Error::InvalidRule {
87 trace: SpanTrace::capture(),
88 })
89 }
90 };
91 let version_files: Option<Vec<_>> = ctx
92 .metadata()
93 .read_lock()
94 .await
95 .get_version(&self.version)
96 .map(|v| v.into_iter().map(|(source, _)| source).collect());
97 let paths: Vec<_> = paths
98 .into_iter()
99 .filter(|p| {
100 version_files
101 .as_ref()
102 .map(|v| {
103 v.iter()
104 .filter(|w| &**w == &*p.to_string_lossy())
105 .next()
106 .is_none()
107 })
108 .unwrap_or(true)
109 })
110 .collect();
111
112 let name = self.get_name().to_owned();
113 let src_dir = ctx.config().source_dir();
114 let target_dir = ctx.config().target_dir();
115 let runner = CompileRunner::new(name, self.version, ctx, self.compiler);
116 for source in paths {
117 let path = source.strip_prefix(&src_dir).unwrap_or(&source);
118 let target = target_dir.join(path);
119 let path = PathBuf::from("/").join(path);
120 runner.spawn_compile(source, target, path).await;
121 }
122 let ctx = runner.join().await?;
123 Ok(ctx)
124 }
125}