Skip to main content

hathor/
lib.rs

1/*!
2# Hathor file generator
3
4Allows to quckly generate a set of similar files.
5
6Named after [Hathor](https://en.wikipedia.org/wiki/Hathor) - goddess in ancient Egyptian religion.
7
8## Legal
9
10Dual-licensed under `MIT` or the [UNLICENSE](http://unlicense.org/).
11
12## Features
13
14*This is now in progress project. Things are broken. No warranty provided.*
15
16Currently supported:
17
18- generate set of same size files with predefined content based on string `0123456789`
19
20## Usage
21
22### Installation
23
24```bash
25cargo install hathor
26```
27
28### Generate files
29
30To generate 5 files with the size 15 in directory `test` use:
31
32```bash
33$ mkdir test
34$ hathor generate 15 5 test
35$ ls test/
360 1 2 3 4
37$ cat test/0
38012345678901234
39```
40
41### Help
42
43Check `--help` for future usage information.
44
45```bash
46$ hathor --help
47hathor 0.1.0
48Hathor - a file generator
49
50USAGE:
51    hathor <SUBCOMMAND>
52
53FLAGS:
54    -h, --help       Prints help information
55    -V, --version    Prints version information
56
57SUBCOMMANDS:
58    generate    Generates a set of same size files with repeating pattern `0123456789`
59    help        Prints this message or the help of the given subcommand(s)
60```
61*/
62
63use std::{
64    io,
65    path::{Path, PathBuf},
66};
67
68const DEFAULT_CONTENT: &str = "0123456789";
69
70pub trait FileWriter {
71    fn write<P, C>(&mut self, path: P, content: C) -> io::Result<()>
72    where
73        P: AsRef<Path>,
74        C: AsRef<[u8]>;
75}
76
77pub trait FilenameGenerator {
78    fn generate(&mut self) -> PathBuf;
79}
80
81pub trait PathFilenameGenerator: FilenameGenerator {
82    type Name: ToString;
83    fn next_name(&mut self) -> Self::Name;
84
85    fn base_path(&self) -> &Path;
86}
87
88impl<T, M: ToString> FilenameGenerator for T
89where
90    T: PathFilenameGenerator<Name = M>,
91{
92    fn generate(&mut self) -> PathBuf {
93        let name = self.next_name().to_string();
94        self.base_path().join(name)
95    }
96}
97
98pub struct OrdinalPathFilenameGenerator {
99    root: PathBuf,
100    current: usize,
101}
102
103impl<T> From<T> for OrdinalPathFilenameGenerator
104where
105    T: AsRef<Path>,
106{
107    fn from(value: T) -> Self {
108        Self {
109            root: value.as_ref().into(),
110            current: 0,
111        }
112    }
113}
114
115impl PathFilenameGenerator for OrdinalPathFilenameGenerator {
116    type Name = usize;
117
118    fn next_name(&mut self) -> usize {
119        let current = self.current;
120        self.current += 1;
121        current
122    }
123
124    fn base_path(&self) -> &Path {
125        &self.root
126    }
127}
128
129pub struct DefaultFileWriter {}
130
131impl FileWriter for DefaultFileWriter {
132    fn write<P, C>(&mut self, path: P, content: C) -> io::Result<()>
133    where
134        P: AsRef<Path>,
135        C: AsRef<[u8]>,
136    {
137        std::fs::write(path, content)
138    }
139}
140
141pub struct FileGeneratorBuilder {}
142
143impl FileGeneratorBuilder {
144    pub fn with_size(size: usize) -> SizedFileGenerator {
145        SizedFileGenerator { size }
146    }
147}
148
149pub trait FileGenerator: Sized {
150    fn write<PG, F>(&self, path_generator: &mut PG, file_writer: &mut F) -> io::Result<()>
151    where
152        PG: FilenameGenerator,
153        F: FileWriter;
154
155    fn generate_to<P>(&self, path: P) -> io::Result<()>
156    where
157        P: AsRef<Path>,
158    {
159        self.write(
160            &mut OrdinalPathFilenameGenerator::from(path),
161            &mut DefaultFileWriter {},
162        )
163    }
164
165    fn repeat(self, count: usize) -> RepeatFileGenerator<Self> {
166        RepeatFileGenerator {
167            count,
168            generator: self,
169        }
170    }
171}
172
173pub struct SizedFileGenerator {
174    size: usize,
175}
176
177impl FileGenerator for SizedFileGenerator {
178    fn write<PG, F>(&self, path_generator: &mut PG, file_writer: &mut F) -> io::Result<()>
179    where
180        PG: FilenameGenerator,
181        F: FileWriter,
182    {
183        let buffer: Vec<u8> = DEFAULT_CONTENT.bytes().cycle().take(self.size).collect();
184
185        file_writer.write(path_generator.generate(), buffer)
186    }
187}
188
189pub struct RepeatFileGenerator<G: FileGenerator> {
190    count: usize,
191    generator: G,
192}
193
194impl<G: FileGenerator> FileGenerator for RepeatFileGenerator<G> {
195    fn write<PG, F>(&self, path_generator: &mut PG, file_writer: &mut F) -> io::Result<()>
196    where
197        PG: FilenameGenerator,
198        F: FileWriter,
199    {
200        for _ in 0..self.count {
201            self.generator.write(path_generator, file_writer)?;
202        }
203
204        Ok(())
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use std::path::PathBuf;
211
212    use crate::{FileGenerator, FileGeneratorBuilder, FileWriter, OrdinalPathFilenameGenerator};
213
214    struct TestFileGenerator {
215        files: Vec<(PathBuf, Vec<u8>)>,
216    }
217
218    impl FileWriter for TestFileGenerator {
219        fn write<P, C>(&mut self, path: P, content: C) -> std::io::Result<()>
220        where
221            P: AsRef<std::path::Path>,
222            C: AsRef<[u8]>,
223        {
224            self.files
225                .push((path.as_ref().into(), content.as_ref().into()));
226
227            Ok(())
228        }
229    }
230
231    #[test]
232    fn repeated_file_generator() {
233        let mut test_file_generator = TestFileGenerator { files: vec![] };
234
235        let _ = FileGeneratorBuilder::with_size(1)
236            .repeat(3)
237            .write(
238                &mut OrdinalPathFilenameGenerator::from("/test/path"),
239                &mut test_file_generator,
240            )
241            .unwrap();
242
243        assert_eq!(
244            test_file_generator.files,
245            vec![
246                (PathBuf::from("/test/path/0"), b"0".to_vec()),
247                (PathBuf::from("/test/path/1"), b"0".to_vec()),
248                (PathBuf::from("/test/path/2"), b"0".to_vec())
249            ]
250        );
251    }
252}