Skip to main content

renamer_rs/
processor.rs

1pub(crate) mod delimiter;
2pub(crate) mod extractor;
3pub(crate) mod format;
4pub(crate) mod inputs;
5pub(crate) mod rename;
6pub(crate) mod replacer;
7pub(crate) mod selector;
8pub(crate) mod trim;
9
10use crate::Error::InvalidValue;
11use crate::processor::inputs::InputType;
12use crate::processor::rename::{TextRenamer, filename_as_string_lossy, FileRenamer, RenameProcessor};
13use crate::{
14    Delimiter, Error, Extractor, Format, Renamed, Replacer, Selector,
15    Trim,
16};
17use indexmap::IndexSet;
18use log::trace;
19
20/// A [`ProcessorBuilder`] is used to configure the renaming process and produces [`Renamed`] when processing is activated
21#[derive(Debug)]
22pub struct ProcessorBuilder {
23    delimiters: Vec<Delimiter>,
24    extractors: Vec<Extractor>,
25    format: Format,
26    inputs: IndexSet<InputType>,
27    replacers: Vec<Replacer>,
28    selectors: Vec<Selector>,
29    trims: Vec<Trim>,
30}
31
32impl ProcessorBuilder {
33    /// Constructs new [`ProcessorBuilder`]
34    pub fn new(format: Format) -> Self {
35        Self {
36            delimiters: Vec::new(),
37            extractors: Vec::new(),
38            format,
39            inputs: IndexSet::new(),
40            replacers: Vec::new(),
41            selectors: Vec::new(),
42            trims: Vec::new(),
43        }
44    }
45
46    /// Appends a single [`Delimiter`] item to the existing configuration
47    pub fn delimiter(mut self, delimiter: Delimiter) -> Self {
48        self.delimiters.push(delimiter);
49        self
50    }
51
52    /// Appends multiple [`Delimiter`] items to the existing configuration
53    pub fn delimiters(mut self, delimiters: Vec<Delimiter>) -> Self {
54        self.delimiters.extend(delimiters);
55        self
56    }
57
58    /// Appends a single [`Extractor`] item to the existing configuration
59    pub fn extractor(mut self, extractor: Extractor) -> Self {
60        self.extractors.push(extractor);
61        self
62    }
63
64    /// Appends multiple [`Extractor`] items to the existing configuration
65    pub fn extractors(mut self, extractors: Vec<Extractor>) -> Self {
66        self.extractors.extend(extractors);
67        self
68    }
69
70    /// Appends a single file path to the existing configuration
71    pub fn input(mut self, input: InputType) -> Self {
72        self.inputs.insert(input);
73        self
74    }
75
76    /// Appends multiple file paths items to the existing configuration
77    pub fn inputs(mut self, inputs: Vec<InputType>) -> Self {
78        self.inputs.extend(inputs);
79        self
80    }
81
82    /// Appends a single [`Replacer`] item to the existing configuration
83    pub fn replacer(mut self, trim: Replacer) -> Self {
84        self.replacers.push(trim);
85        self
86    }
87
88    /// Appends multiple [`Replacer`] items to the existing configuration
89    pub fn replacers(mut self, trims: Vec<Replacer>) -> Self {
90        self.replacers.extend(trims);
91        self
92    }
93
94    /// Appends a single [`Selector`] item to the existing configuration
95    pub fn selector(mut self, selector: Selector) -> Self {
96        self.selectors.push(selector);
97        self
98    }
99
100    /// Appends multiple [`Selector`] items to the existing configuration
101    pub fn selectors(mut self, selectors: Vec<Selector>) -> Self {
102        self.selectors.extend(selectors);
103        self
104    }
105
106    /// Appends a single [`Trim`] item to the existing configuration
107    pub fn trim(mut self, trim: Trim) -> Self {
108        self.trims.push(trim);
109        self
110    }
111
112    /// Appends multiple [`Trim`] items to the existing configuration
113    pub fn trims(mut self, trims: Vec<Trim>) -> Self {
114        self.trims.extend(trims);
115        self
116    }
117
118    /// Returns [`Renamed`] trait objects based on the [`ProcessorBuilder`] configuration for all items
119    pub fn process(&self) -> Result<Vec<Box<dyn Renamed>>, Error> {
120        let mut renamed = Vec::new();
121        renamed.extend(self.process_inputs(None)?);
122        Ok(renamed)
123    }
124
125    /// Returns [`Renamed`] trait objects based on the [`ProcessorBuilder`] configuration up to the processing limit provided
126    pub fn process_subset(&self, processing_limit: usize) -> Result<Vec<Box<dyn Renamed>>, Error> {
127        match processing_limit == 0 {
128            true => Err(InvalidValue(
129                "processing_limit must be greater than 0".to_string(),
130            )),
131            false => {
132                let mut renamed = Vec::new();
133                renamed.extend(self.process_inputs(Some(processing_limit))?);
134                Ok(renamed)
135            }
136        }
137    }
138
139    fn process_inputs(
140        &self,
141        processing_limit: Option<usize>,
142    ) -> Result<Vec<Box<dyn Renamed>>, Error> {
143        let mut renamed = Vec::new();
144        for input_type in self.inputs.iter() {
145            let process_string = match input_type {
146                InputType::File(i) => filename_as_string_lossy(i.value()),
147                InputType::Text(i) => i.value().into(),
148            };
149
150            let extracted = self.process_extractors(process_string.as_str());
151            let process_strings = vec![process_string];
152            let segments = self.process_delimiters(process_strings.as_slice());
153            let segments = self.process_trims(segments);
154            let segments = self.process_replacers(segments);
155            let selected = self.process_selectors(segments.as_slice());
156            renamed.push(match input_type {
157                InputType::File(i) => FileRenamer::new(
158                    i.value(),
159                    segments,
160                    selected,
161                    extracted,
162                    self.format.clone(),
163                )
164                .rename(),
165                InputType::Text(i) => TextRenamer::new(
166                    i.value(),
167                    segments,
168                    selected,
169                    extracted,
170                    self.format.clone(),
171                )
172                .rename(),
173            });
174            if let Some(limit) = processing_limit {
175                if renamed.len() == limit {
176                    break;
177                }
178            }
179        }
180        Ok(renamed)
181    }
182
183    fn process_delimiters<S: AsRef<str>>(&self, value: &[S]) -> Vec<String> {
184        let mut output = Vec::new();
185        for delimiter in &self.delimiters {
186            for seg in value {
187                let mut segments = delimiter.split(seg);
188                output.append(&mut segments);
189            }
190            trace!(
191                "After Delimiter: |{}| --- Output Segments Count: {}",
192                delimiter,
193                output.len()
194            )
195        }
196        output
197    }
198
199    fn process_selectors(&self, segments: &[String]) -> Vec<Option<String>> {
200        self.selectors
201            .iter()
202            .map(|s| s.match_segment(segments))
203            .collect()
204    }
205
206    fn process_extractors<S: AsRef<str>>(&self, value: S) -> Vec<Option<String>> {
207        self.extractors
208            .iter()
209            .map(|e| e.extract(value.as_ref()))
210            .collect()
211    }
212
213    fn process_trims(&self, segments: Vec<String>) -> Vec<String> {
214        let mut output = segments;
215        for t in self.trims.as_slice() {
216            output = t.trim_slice(output.as_slice())
217        }
218        output
219    }
220
221    fn process_replacers(&self, segments: Vec<String>) -> Vec<String> {
222        let mut output = segments;
223        for r in self.replacers.as_slice() {
224            output = r.replace_slice(output.as_slice())
225        }
226        output
227    }
228}