gen_circleci_orb/output_writer/
mod.rs1use anyhow::Result;
2use std::collections::HashMap;
3use std::fs;
4use std::path::{Path, PathBuf};
5
6#[derive(Debug, Default, PartialEq)]
8pub struct WriteReport {
9 pub created: usize,
10 pub updated: usize,
11 pub unchanged: usize,
12}
13
14pub fn write_tree(
19 root: &Path,
20 files: &HashMap<PathBuf, String>,
21 dry_run: bool,
22) -> Result<WriteReport> {
23 let mut report = WriteReport::default();
24
25 for (rel_path, content) in files {
26 let abs_path = root.join(rel_path);
27
28 let existing = if abs_path.exists() {
29 Some(fs::read_to_string(&abs_path)?)
30 } else {
31 None
32 };
33
34 match &existing {
35 Some(current) if current == content => {
36 report.unchanged += 1;
37 }
38 Some(_) => {
39 if dry_run {
40 eprintln!("[dry-run] would update: {}", rel_path.display());
41 } else {
42 if let Some(parent) = abs_path.parent() {
43 fs::create_dir_all(parent)?;
44 }
45 fs::write(&abs_path, content)?;
46 }
47 report.updated += 1;
48 }
49 None => {
50 if dry_run {
51 eprintln!("[dry-run] would create: {}", rel_path.display());
52 } else {
53 if let Some(parent) = abs_path.parent() {
54 fs::create_dir_all(parent)?;
55 }
56 fs::write(&abs_path, content)?;
57 }
58 report.created += 1;
59 }
60 }
61 }
62
63 Ok(report)
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use tempfile::TempDir;
70
71 fn single_file(path: &str, content: &str) -> HashMap<PathBuf, String> {
72 let mut m = HashMap::new();
73 m.insert(PathBuf::from(path), content.to_string());
74 m
75 }
76
77 #[test]
78 fn new_file_is_created() {
79 let dir = TempDir::new().unwrap();
80 let files = single_file("src/foo.yml", "hello");
81 let report = write_tree(dir.path(), &files, false).unwrap();
82 assert_eq!(report.created, 1);
83 assert_eq!(report.updated, 0);
84 assert_eq!(report.unchanged, 0);
85 assert_eq!(
86 fs::read_to_string(dir.path().join("src/foo.yml")).unwrap(),
87 "hello"
88 );
89 }
90
91 #[test]
92 fn identical_file_is_skipped() {
93 let dir = TempDir::new().unwrap();
94 fs::create_dir_all(dir.path().join("src")).unwrap();
95 fs::write(dir.path().join("src/foo.yml"), "hello").unwrap();
96
97 let files = single_file("src/foo.yml", "hello");
98 let report = write_tree(dir.path(), &files, false).unwrap();
99 assert_eq!(report.unchanged, 1);
100 assert_eq!(report.created, 0);
101 assert_eq!(report.updated, 0);
102 }
103
104 #[test]
105 fn changed_file_is_updated() {
106 let dir = TempDir::new().unwrap();
107 fs::create_dir_all(dir.path().join("src")).unwrap();
108 fs::write(dir.path().join("src/foo.yml"), "old content").unwrap();
109
110 let files = single_file("src/foo.yml", "new content");
111 let report = write_tree(dir.path(), &files, false).unwrap();
112 assert_eq!(report.updated, 1);
113 assert_eq!(
114 fs::read_to_string(dir.path().join("src/foo.yml")).unwrap(),
115 "new content"
116 );
117 }
118
119 #[test]
120 fn dry_run_writes_nothing() {
121 let dir = TempDir::new().unwrap();
122 let files = single_file("src/foo.yml", "hello");
123 let report = write_tree(dir.path(), &files, true).unwrap();
124 assert_eq!(report.created, 1, "dry_run should still count created");
125 assert!(
126 !dir.path().join("src/foo.yml").exists(),
127 "dry_run must not write files"
128 );
129 }
130}