A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
3.0 KiB

7 years ago
7 years ago
5 years ago
5 years ago
7 years ago
7 years ago
7 years ago
5 years ago
5 years ago
7 years ago
7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
7 years ago
7 years ago
5 years ago
5 years ago
7 years ago
7 years ago
5 years ago
  1. use crate::command_runner::CommandRunner;
  2. use crate::storage::Storage;
  3. use crate::symbols::Symbol;
  4. use async_trait::async_trait;
  5. use std::borrow::Borrow;
  6. use std::error::Error;
  7. use std::fs;
  8. use std::io;
  9. use std::marker::PhantomData;
  10. use std::path::Path;
  11. use std::str::FromStr;
  12. #[derive(Debug, PartialEq)]
  13. pub enum StorageDirection {
  14. Load,
  15. Store,
  16. }
  17. #[derive(Debug)]
  18. pub struct SavedDirectory<_C, C, P, S> {
  19. path: P,
  20. storage: S,
  21. dir: StorageDirection,
  22. command_runner: C,
  23. phantom: PhantomData<_C>,
  24. }
  25. impl<_C, C, P, S> SavedDirectory<_C, C, P, S> {
  26. pub fn new(path: P, storage: S, dir: StorageDirection, command_runner: C) -> Self {
  27. Self {
  28. path,
  29. storage,
  30. dir,
  31. command_runner,
  32. phantom: PhantomData::default(),
  33. }
  34. }
  35. }
  36. #[async_trait(?Send)]
  37. impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, S: Storage> Symbol
  38. for SavedDirectory<_C, C, P, S>
  39. {
  40. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  41. let metadata = fs::metadata(self.path.as_ref());
  42. // Check if dir exists
  43. if let Err(e) = metadata {
  44. return if e.kind() == io::ErrorKind::NotFound {
  45. Ok(self.dir == StorageDirection::Store)
  46. } else {
  47. Err(Box::new(e))
  48. };
  49. }
  50. if !metadata.unwrap().is_dir() {
  51. return Err(Box::new(io::Error::new(
  52. io::ErrorKind::AlreadyExists,
  53. "Could not create a directory, non-directory file exists",
  54. )));
  55. }
  56. let dump_date = self.storage.recent_date()?;
  57. let output = self
  58. .command_runner
  59. .borrow()
  60. .get_output(
  61. "sh",
  62. args![
  63. "-c",
  64. format!(
  65. "find {} -printf '%T@\\n' | sort -r | head -n1 | grep '^[0-9]\\+' -o",
  66. self.path.as_ref().to_str().unwrap()
  67. ),
  68. ],
  69. )
  70. .await?;
  71. let modified_date = u64::from_str(String::from_utf8(output)?.trim_end())?;
  72. if if self.dir == StorageDirection::Store {
  73. modified_date > dump_date
  74. } else {
  75. dump_date > modified_date
  76. } {
  77. let output = self
  78. .command_runner
  79. .borrow()
  80. .run_with_args(
  81. "diff",
  82. args!["-rq", self.storage.read_filename()?, self.path.as_ref()],
  83. )
  84. .await?;
  85. match output.status.code() {
  86. Some(0) => Ok(true),
  87. Some(1) => Ok(false),
  88. _ => Err(String::from_utf8(output.stderr)?.into()),
  89. }
  90. } else {
  91. Ok(true)
  92. }
  93. }
  94. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  95. if self.dir == StorageDirection::Load {
  96. self
  97. .command_runner
  98. .borrow()
  99. .run_successfully("rm", args!["-rf", self.path.as_ref()])
  100. .await?;
  101. self
  102. .command_runner
  103. .borrow()
  104. .run_successfully(
  105. "cp",
  106. args!["-a", self.storage.read_filename()?, self.path.as_ref()],
  107. )
  108. .await
  109. } else {
  110. self
  111. .command_runner
  112. .borrow()
  113. .run_successfully(
  114. "cp",
  115. args!["-a", self.path.as_ref(), self.storage.write_filename()],
  116. )
  117. .await
  118. }
  119. }
  120. }
  121. #[cfg(test)]
  122. mod test {}