Skip to main content

gize_generator/
plan.rs

1//! A `Plan` is the set of file operations a generator wants to perform. Building the plan
2//! is pure (no I/O), which makes generators easy to test and makes `--dry-run` trivial: we
3//! simply render the plan instead of applying it.
4
5use std::path::PathBuf;
6
7/// What a single file operation intends to do.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum OpKind {
10    /// Create a new file. If it already exists, the writer will skip it unless `--force`.
11    Create,
12    /// Create parent directories only (no file contents).
13    Mkdir,
14}
15
16/// A single planned file operation.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct FileOp {
19    pub kind: OpKind,
20    pub path: PathBuf,
21    pub contents: String,
22}
23
24impl FileOp {
25    pub fn create(path: impl Into<PathBuf>, contents: impl Into<String>) -> Self {
26        Self {
27            kind: OpKind::Create,
28            path: path.into(),
29            contents: contents.into(),
30        }
31    }
32
33    pub fn mkdir(path: impl Into<PathBuf>) -> Self {
34        Self {
35            kind: OpKind::Mkdir,
36            path: path.into(),
37            contents: String::new(),
38        }
39    }
40}
41
42/// An ordered collection of [`FileOp`]s produced by a generator.
43#[derive(Debug, Clone, Default, PartialEq, Eq)]
44pub struct Plan {
45    pub ops: Vec<FileOp>,
46}
47
48impl Plan {
49    pub fn new() -> Self {
50        Self::default()
51    }
52
53    pub fn create(mut self, path: impl Into<PathBuf>, contents: impl Into<String>) -> Self {
54        self.ops.push(FileOp::create(path, contents));
55        self
56    }
57
58    pub fn mkdir(mut self, path: impl Into<PathBuf>) -> Self {
59        self.ops.push(FileOp::mkdir(path));
60        self
61    }
62
63    /// Append every operation from `other`, preserving order. Used to compose a base plan
64    /// with an optional add-on (e.g. `gize new`'s built-in `users` slice).
65    pub fn extend(mut self, other: Plan) -> Self {
66        self.ops.extend(other.ops);
67        self
68    }
69
70    pub fn is_empty(&self) -> bool {
71        self.ops.is_empty()
72    }
73}