ambient_ci/action_impl/
tar.rs1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 action::{ActionError, Context},
7 action_impl::ActionImpl,
8 vdrive::VirtualDriveBuilder,
9};
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct TarCreate {
18 archive: PathBuf,
19 directory: PathBuf,
20}
21
22impl TarCreate {
23 pub fn new(archive: PathBuf, directory: PathBuf) -> Self {
25 Self { archive, directory }
26 }
27}
28
29impl ActionImpl for TarCreate {
30 fn execute(&self, _context: &mut Context) -> Result<(), ActionError> {
31 VirtualDriveBuilder::default()
32 .filename(&self.archive)
33 .root_directory(&self.directory)
34 .create()
35 .map_err(|e| TarError::TarCreate(self.archive.clone(), self.directory.clone(), e))?;
36 Ok(())
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub struct TarExtract {
47 archive: PathBuf,
48 directory: PathBuf,
49}
50
51impl TarExtract {
52 pub fn new(archive: PathBuf, directory: PathBuf) -> Self {
54 Self { archive, directory }
55 }
56}
57
58impl ActionImpl for TarExtract {
59 fn execute(&self, _context: &mut Context) -> Result<(), ActionError> {
60 let tar = VirtualDriveBuilder::default()
61 .filename(&self.archive)
62 .root_directory(&self.directory)
63 .open()
64 .map_err(|e| TarError::TarOpen(self.archive.clone(), e))?;
65 tar.extract_to(&self.directory)
66 .map_err(|e| TarError::TarExtract(self.archive.clone(), self.directory.clone(), e))?;
67 Ok(())
68 }
69}
70
71#[derive(Debug, thiserror::Error)]
73pub enum TarError {
74 #[error("failed to create tar archive {0} from {1}")]
76 TarCreate(PathBuf, PathBuf, #[source] crate::vdrive::VirtualDriveError),
77
78 #[error("failed to open tar archive {0}")]
80 TarOpen(PathBuf, #[source] crate::vdrive::VirtualDriveError),
81
82 #[error("failed to extract tar archive {0} into {1}")]
84 TarExtract(PathBuf, PathBuf, #[source] crate::vdrive::VirtualDriveError),
85}
86
87impl From<TarError> for ActionError {
88 fn from(value: TarError) -> Self {
89 Self::Tar(value)
90 }
91}
92
93#[cfg(test)]
94mod test {
95 use crate::{
96 action::{RunnableAction, UnsafeAction},
97 plan::RunnablePlan,
98 runlog::RunLog,
99 };
100
101 use super::*;
102 use tempfile::tempdir;
103
104 fn plan() -> RunnablePlan {
105 let mut plan = RunnablePlan::default();
106 plan.set_cache_dir("/tmp");
107 plan.set_deps_dir("/tmp");
108 plan.set_source_dir("/tmp");
109 plan.set_artifacts_dir("/tmp");
110 plan
111 }
112
113 #[test]
114 fn tar_create_action() -> Result<(), Box<dyn std::error::Error>> {
115 let tmp = tempdir()?;
116 let src = tmp.path().join("src");
117 let tar = tmp.path().join("src.tar");
118
119 std::fs::create_dir(&src)?;
120 let action = RunnableAction::from_unsafe_action(&UnsafeAction::tar_create(&tar, &src));
121 let plan = plan();
122 let mut runlog = RunLog::default();
123 let mut context = Context::new(&mut runlog);
124 context.set_envs_from_plan(&plan)?;
125
126 assert!(!tar.exists());
127 assert!(action.execute(&mut context).is_ok());
128 assert!(tar.exists());
129 Ok(())
130 }
131
132 #[test]
133 fn tar_extract_action() -> Result<(), Box<dyn std::error::Error>> {
134 let tmp = tempdir()?;
135 let src = tmp.path().join("src");
136 let tar = tmp.path().join("src.tar");
137 let extracted = tmp.path().join("extracted");
138
139 std::fs::create_dir(&src)?;
140 std::fs::File::create(src.join("file.dat"))?;
141 let plan = plan();
142 let mut runlog = RunLog::default();
143 let mut context = Context::new(&mut runlog);
144 context.set_envs_from_plan(&plan)?;
145
146 RunnableAction::from_unsafe_action(&UnsafeAction::tar_create(&tar, &src))
147 .execute(&mut context)?;
148
149 let action =
150 RunnableAction::from_unsafe_action(&UnsafeAction::tar_extract(&tar, &extracted));
151 assert!(action.execute(&mut context).is_ok());
152 assert!(extracted.join("file.dat").exists());
153 Ok(())
154 }
155}