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.

94 lines
2.5 KiB

7 years ago
  1. use std::error::Error;
  2. use std::fmt;
  3. use std::io;
  4. use std::ops::Deref;
  5. use symbols::Symbol;
  6. #[derive(Debug)]
  7. pub enum FileError<E: Error> {
  8. ExecError(E),
  9. GenericError
  10. }
  11. impl From<io::Error> for FileError<io::Error> {
  12. fn from(err: io::Error) -> FileError<io::Error> {
  13. FileError::ExecError(err)
  14. }
  15. }
  16. impl<E: Error> Error for FileError<E> {
  17. fn description(&self) -> &str {
  18. match self {
  19. &FileError::ExecError(ref e) => e.description(),
  20. &FileError::GenericError => "Generic error"
  21. }
  22. }
  23. fn cause(&self) -> Option<&Error> {
  24. match self {
  25. &FileError::ExecError(ref e) => Some(e),
  26. _ => None
  27. }
  28. }
  29. }
  30. impl<E: Error> fmt::Display for FileError<E> {
  31. fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  32. write!(f, "{}", self.description())
  33. }
  34. }
  35. use std::convert::AsRef;
  36. pub struct File<C, D> where C: Deref<Target=str>, D: AsRef<str> + fmt::Display {
  37. path: D,
  38. content: C
  39. }
  40. impl<C, D> File<C, D> where C: Deref<Target=str>, D: AsRef<str> + fmt::Display {
  41. pub fn new(path: D, content: C) -> Self {
  42. File {
  43. path: path,
  44. content: content
  45. }
  46. }
  47. }
  48. use std::fs::File as FsFile;
  49. use std::io::{Read, Write};
  50. impl<C, D> Symbol for File<C, D> where C: Deref<Target=str>, D: AsRef<str> + fmt::Display {
  51. type Error = FileError<io::Error>;
  52. fn target_reached(&self) -> Result<bool, Self::Error> {
  53. let file = FsFile::open(self.path.as_ref());
  54. // Check if file exists
  55. if let Err(e) = file {
  56. return if e.kind() == io::ErrorKind::NotFound {
  57. Ok(false)
  58. } else {
  59. Err(e.into())
  60. };
  61. }
  62. // Check if content is the same
  63. let file_content = file.unwrap().bytes();
  64. let content_equal = try!(self.content.bytes().zip(file_content).fold(
  65. Ok(true),
  66. |state, (target_byte, file_byte_option)| state.and_then(|s| file_byte_option.map(|file_byte| s && file_byte == target_byte))
  67. ));
  68. return Ok(content_equal)
  69. }
  70. fn execute(&self) -> Result<(), Self::Error> {
  71. //try!(self.command_runner.run_with_args("mkdir", &["-p", Path::new(&path).parent().unwrap().to_str().unwrap()]));
  72. // FIXME: Permissions
  73. // try!(create_dir_all(Path::new(&path).parent().unwrap()));
  74. try!(try!(FsFile::create(self.path.as_ref())).write_all(self.content.as_bytes()));
  75. Ok(())
  76. }
  77. }
  78. impl<C, D> fmt::Display for File<C, D> where C: Deref<Target=str>, D: AsRef<str> + fmt::Display {
  79. fn fmt(&self, f: &mut fmt::Formatter) -> Result<(),fmt::Error>{
  80. write!(f, "File {}", self.path)
  81. }
  82. }