greentic_component/scaffold/
write.rs1#![cfg(feature = "cli")]
2
3use std::fs;
4use std::io::{self, Write};
5use std::path::{Path, PathBuf};
6
7use tempfile::NamedTempFile;
8use thiserror::Error;
9
10#[derive(Debug, Default, Clone)]
11pub struct Writer;
12
13impl Writer {
14 pub fn new() -> Self {
15 Self
16 }
17
18 pub fn write_all(
19 &self,
20 root: &Path,
21 files: &[GeneratedFile],
22 ) -> Result<Vec<String>, WriteError> {
23 if !root.exists() {
24 fs::create_dir_all(root)
25 .map_err(|err| WriteError::CreateDir(root.to_path_buf(), err))?;
26 }
27
28 let mut created = Vec::with_capacity(files.len());
29 for file in files {
30 let target = root.join(&file.relative_path);
31 if let Some(parent) = target.parent() {
32 fs::create_dir_all(parent)
33 .map_err(|err| WriteError::CreateDir(parent.to_path_buf(), err))?;
34 }
35
36 let mut tmp = NamedTempFile::new_in(target.parent().unwrap_or(root))
37 .map_err(|err| WriteError::WriteFile(file.relative_path.to_path_buf(), err))?;
38 tmp.write_all(&file.contents)
39 .map_err(|err| WriteError::WriteFile(file.relative_path.to_path_buf(), err))?;
40 tmp.flush()
41 .map_err(|err| WriteError::WriteFile(file.relative_path.to_path_buf(), err))?;
42 tmp.as_file()
43 .sync_all()
44 .map_err(|err| WriteError::WriteFile(file.relative_path.to_path_buf(), err))?;
45
46 tmp.persist(&target)
47 .map_err(|err| WriteError::Persist(file.relative_path.to_path_buf(), err.error))?;
48
49 if file.executable {
50 set_executable(&target).map_err(|err| {
51 WriteError::Permissions(file.relative_path.to_path_buf(), err)
52 })?;
53 }
54
55 created.push(file.relative_path.display().to_string());
56 }
57
58 created.sort();
59 Ok(created)
60 }
61}
62
63fn set_executable(path: &Path) -> io::Result<()> {
64 #[cfg(unix)]
65 {
66 use std::os::unix::fs::PermissionsExt;
67 let metadata = fs::metadata(path)?;
68 let mut permissions = metadata.permissions();
69 let current = permissions.mode();
70 permissions.set_mode(current | 0o755);
71 fs::set_permissions(path, permissions)
72 }
73 #[cfg(not(unix))]
74 {
75 let _ = path;
76 Ok(())
77 }
78}
79
80#[derive(Debug, Clone)]
81pub struct GeneratedFile {
82 pub relative_path: PathBuf,
83 pub contents: Vec<u8>,
84 pub executable: bool,
85}
86
87#[derive(Debug, Error)]
88pub enum WriteError {
89 #[error("failed to create directory {0}: {1}")]
90 CreateDir(PathBuf, #[source] io::Error),
91 #[error("failed to write {0}: {1}")]
92 WriteFile(PathBuf, #[source] io::Error),
93 #[error("failed to persist {0}: {1}")]
94 Persist(PathBuf, #[source] io::Error),
95 #[error("failed to update permissions for {0}: {1}")]
96 Permissions(PathBuf, #[source] io::Error),
97}