tar.rs
1 use std::path::PathBuf; 2 3 use serde::{Deserialize, Serialize}; 4 5 use crate::{ 6 action::{ActionError, Context}, 7 action_impl::ActionImpl, 8 vdrive::VirtualDriveBuilder, 9 }; 10 11 /// Create a `tar` archive from a directory. 12 /// 13 /// This is meant for internal use by Ambient. It can't be used in any kind 14 /// of plan, pre-plan, or post-plan. It can be used in a runnable plan. 15 /// It is generated by Ambient to set up execution of a runnable plan. 16 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 17 pub struct TarCreate { 18 archive: PathBuf, 19 directory: PathBuf, 20 } 21 22 impl TarCreate { 23 /// Create a new `TarCreate` action. 24 pub fn new(archive: PathBuf, directory: PathBuf) -> Self { 25 Self { archive, directory } 26 } 27 } 28 29 impl 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 /// Extract a tar archive into a directory. 41 /// 42 /// This is meant for internal use by Ambient. It can't be used in any kind 43 /// of plan, pre-plan, or post-plan. It can be used in a runnable plan. 44 /// It is generated by Ambient to set up execution of a runnable plan. 45 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] 46 pub struct TarExtract { 47 archive: PathBuf, 48 directory: PathBuf, 49 } 50 51 impl TarExtract { 52 /// Create a new `TarExtract` action. 53 pub fn new(archive: PathBuf, directory: PathBuf) -> Self { 54 Self { archive, directory } 55 } 56 } 57 58 impl 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 /// Errors from tar actions. 72 #[derive(Debug, thiserror::Error)] 73 pub enum TarError { 74 /// Can't create a tar archive. 75 #[error("failed to create tar archive {0} from {1}")] 76 TarCreate(PathBuf, PathBuf, #[source] crate::vdrive::VirtualDriveError), 77 78 /// Can't open a tar archive to extract it. 79 #[error("failed to open tar archive {0}")] 80 TarOpen(PathBuf, #[source] crate::vdrive::VirtualDriveError), 81 82 /// Can't extract a tar archive. 83 #[error("failed to extract tar archive {0} into {1}")] 84 TarExtract(PathBuf, PathBuf, #[source] crate::vdrive::VirtualDriveError), 85 } 86 87 impl From<TarError> for ActionError { 88 fn from(value: TarError) -> Self { 89 Self::Tar(value) 90 } 91 } 92 93 #[cfg(test)] 94 mod 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 }