darklua_core/rules/bundle/
mod.rs1pub(crate) mod path_require_mode;
2mod rename_type_declaration;
3mod require_mode;
4
5use std::path::Path;
6
7use crate::nodes::Block;
8use crate::rules::{
9 Context, Rule, RuleConfiguration, RuleConfigurationError, RuleProcessResult, RuleProperties,
10};
11use crate::Parser;
12
13pub(crate) use rename_type_declaration::RenameTypeDeclarationProcessor;
14pub use require_mode::BundleRequireMode;
15use wax::Pattern;
16
17pub const BUNDLER_RULE_NAME: &str = "bundler";
18
19#[derive(Debug)]
20pub(crate) struct BundleOptions {
21 parser: Parser,
22 modules_identifier: String,
23 excludes: Option<wax::Any<'static>>,
24}
25
26impl BundleOptions {
27 fn new<'a>(
28 parser: Parser,
29 modules_identifier: impl Into<String>,
30 excludes: impl Iterator<Item = &'a str>,
31 ) -> Self {
32 let excludes: Vec<_> = excludes
33 .filter_map(|exclusion| match wax::Glob::new(exclusion) {
34 Ok(glob) => Some(glob.into_owned()),
35 Err(err) => {
36 log::warn!(
37 "unable to create exclude matcher from `{}`: {}",
38 exclusion,
39 err.to_string()
40 );
41 None
42 }
43 })
44 .collect();
45 Self {
46 parser,
47 modules_identifier: modules_identifier.into(),
48 excludes: if excludes.is_empty() {
49 None
50 } else {
51 let any_pattern = wax::any::<wax::Glob, _>(excludes)
52 .expect("exclude globs errors should be filtered and only emit a warning");
53 Some(any_pattern)
54 },
55 }
56 }
57
58 fn parser(&self) -> &Parser {
59 &self.parser
60 }
61
62 fn modules_identifier(&self) -> &str {
63 &self.modules_identifier
64 }
65
66 fn is_excluded(&self, require: &Path) -> bool {
67 self.excludes
68 .as_ref()
69 .map(|any| any.is_match(require))
70 .unwrap_or(false)
71 }
72}
73
74#[derive(Debug)]
76pub(crate) struct Bundler {
77 require_mode: BundleRequireMode,
78 options: BundleOptions,
79}
80
81impl Bundler {
82 pub(crate) fn new<'a>(
83 parser: Parser,
84 require_mode: BundleRequireMode,
85 excludes: impl Iterator<Item = &'a str>,
86 ) -> Self {
87 Self {
88 require_mode,
89 options: BundleOptions::new(parser, DEFAULT_MODULE_IDENTIFIER, excludes),
90 }
91 }
92
93 pub(crate) fn with_modules_identifier(mut self, modules_identifier: impl Into<String>) -> Self {
94 self.options.modules_identifier = modules_identifier.into();
95 self
96 }
97}
98
99impl Rule for Bundler {
100 fn process(&self, block: &mut Block, context: &Context) -> RuleProcessResult {
101 self.require_mode
102 .process_block(block, context, &self.options)
103 }
104}
105
106impl RuleConfiguration for Bundler {
107 fn configure(&mut self, _properties: RuleProperties) -> Result<(), RuleConfigurationError> {
108 Err(RuleConfigurationError::InternalUsageOnly(
109 self.get_name().to_owned(),
110 ))
111 }
112
113 fn get_name(&self) -> &'static str {
114 BUNDLER_RULE_NAME
115 }
116
117 fn serialize_to_properties(&self) -> RuleProperties {
118 RuleProperties::new()
119 }
120}
121
122const DEFAULT_MODULE_IDENTIFIER: &str = "__DARKLUA_BUNDLE_MODULES";
123
124#[cfg(test)]
125mod test {
126 use super::*;
127 use crate::rules::{require::PathRequireMode, Rule};
128
129 use insta::assert_json_snapshot;
130
131 fn new_rule() -> Bundler {
132 Bundler::new(
133 Parser::default(),
134 BundleRequireMode::default(),
135 std::iter::empty(),
136 )
137 }
138
139 fn new_rule_with_require_mode(mode: impl Into<BundleRequireMode>) -> Bundler {
140 Bundler::new(Parser::default(), mode.into(), std::iter::empty())
141 }
142
143 #[test]
148 fn serialize_default_rule() {
149 let rule: Box<dyn Rule> = Box::new(new_rule());
150
151 assert_json_snapshot!("default_bundler", rule);
152 }
153
154 #[test]
155 fn serialize_path_require_mode_with_custom_module_folder_name() {
156 let rule: Box<dyn Rule> =
157 Box::new(new_rule_with_require_mode(PathRequireMode::new("__init__")));
158
159 assert_json_snapshot!("default_bundler", rule);
160 }
161
162 #[test]
163 fn serialize_path_require_mode_with_custom_module_folder_name_and_modules_identifier() {
164 let rule: Box<dyn Rule> = Box::new(
165 new_rule_with_require_mode(PathRequireMode::new("__init__"))
166 .with_modules_identifier("_CUSTOM_VAR"),
167 );
168
169 assert_json_snapshot!("default_bundler", rule);
170 }
171
172 #[test]
173 fn serialize_with_custom_modules_identifier() {
174 let rule: Box<dyn Rule> = Box::new(new_rule().with_modules_identifier("_CUSTOM_VAR"));
175
176 assert_json_snapshot!("default_bundler", rule);
177 }
178}