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.

187 lines
5.0 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. use std::error::Error;
  2. use std::fmt;
  3. use loggers::Logger;
  4. use symbols::Symbol;
  5. #[derive(Debug)]
  6. pub enum SymbolRunError {
  7. Symbol(Box<Error>),
  8. ExecuteDidNotReach(())
  9. }
  10. impl Error for SymbolRunError {
  11. fn description(&self) -> &str {
  12. match self {
  13. &SymbolRunError::Symbol(_) => "Symbol execution error",
  14. &SymbolRunError::ExecuteDidNotReach(_) => "Target not reached after executing symbol"
  15. }
  16. }
  17. fn cause(&self) -> Option<&Error> {
  18. match self {
  19. &SymbolRunError::Symbol(ref e) => Some(&**e),
  20. &SymbolRunError::ExecuteDidNotReach(_) => None
  21. }
  22. }
  23. }
  24. impl fmt::Display for SymbolRunError {
  25. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  26. match self {
  27. &SymbolRunError::Symbol(ref e) => write!(f, "{}", e),
  28. &SymbolRunError::ExecuteDidNotReach(_) => write!(f, "{}", self.description())
  29. }
  30. }
  31. }
  32. pub trait SymbolRunner {
  33. fn run_symbol<S: ?Sized + Symbol + fmt::Display>(&self, logger: &mut Logger, symbol: &S) -> Result<(), Box<Error>>;
  34. }
  35. pub struct InitializingSymbolRunner;
  36. impl SymbolRunner for InitializingSymbolRunner {
  37. fn run_symbol<S: ?Sized + Symbol + fmt::Display>(&self, logger: &mut Logger, symbol: &S) -> Result<(), Box<Error>>
  38. {
  39. let target_reached = try!(symbol.target_reached());
  40. if target_reached {
  41. logger.write(format!("{} already reached", symbol).as_str());
  42. } else {
  43. logger.debug(format!("Symbol reports target_reached: {:?}", target_reached).as_str());
  44. logger.write(format!("Executing {}", symbol).as_str());
  45. try!(symbol.execute());
  46. let target_reached = try!(symbol.target_reached());
  47. logger.debug(format!("Symbol reports target_reached: {:?} (should be true)", target_reached).as_str());
  48. if !target_reached {
  49. return Err(Box::new(SymbolRunError::ExecuteDidNotReach(())));
  50. }
  51. }
  52. Ok(())
  53. }
  54. }
  55. pub struct DrySymbolRunner;
  56. impl SymbolRunner for DrySymbolRunner {
  57. fn run_symbol<S: ?Sized + Symbol + fmt::Display>(&self, logger: &mut Logger, symbol: &S) -> Result<(), Box<Error>>
  58. {
  59. let target_reached = try!(symbol.target_reached());
  60. logger.debug(format!("Symbol reports target_reached: {:?}", target_reached).as_str());
  61. if !target_reached {
  62. logger.write(format!("Would execute {}", symbol).as_str());
  63. }
  64. Ok(())
  65. }
  66. }
  67. // FIXME: Add ExpectingSymbolRunner
  68. #[cfg(test)]
  69. mod test {
  70. use std::cell::RefCell;
  71. use std::error::Error;
  72. use std::fmt;
  73. use loggers::Logger;
  74. use symbols::Symbol;
  75. use schema::SymbolRunner;
  76. use schema::InitializingSymbolRunner;
  77. #[derive(Debug, PartialEq, Clone)]
  78. enum DummySymbolError {
  79. Error(())
  80. }
  81. impl Error for DummySymbolError {
  82. fn description(&self) -> &str {
  83. return "Description";
  84. }
  85. }
  86. impl fmt::Display for DummySymbolError {
  87. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  88. write!(f, "Dummy symbol error")
  89. }
  90. }
  91. struct DummySymbol<'a> {
  92. _execute: &'a Fn() -> Result<(), Box<Error>>,
  93. _target_reached: &'a Fn() -> Result<bool, Box<Error>>
  94. }
  95. impl<'a> Symbol for DummySymbol<'a> {
  96. fn target_reached(&self) -> Result<bool, Box<Error>> {
  97. (self._target_reached)()
  98. }
  99. fn execute(&self) -> Result<(), Box<Error>> {
  100. (self._execute)()
  101. }
  102. }
  103. impl<'a> fmt::Display for DummySymbol<'a> {
  104. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  105. write!(f, "Dummy symbol")
  106. }
  107. }
  108. impl<'a> DummySymbol<'a> {
  109. fn new(target_reached: &'a Fn() -> Result<bool, Box<Error>>, execute: &'a Fn() -> Result<(), Box<Error>>) -> DummySymbol<'a> {
  110. DummySymbol { _target_reached: target_reached, _execute: execute }
  111. }
  112. }
  113. struct DummyLogger {
  114. log: Vec<String>
  115. }
  116. impl DummyLogger {
  117. fn new() -> DummyLogger {
  118. DummyLogger { log: Vec::new() }
  119. }
  120. }
  121. impl Logger for DummyLogger {
  122. fn write(&mut self, line: &str) {
  123. self.log.push(From::from(line));
  124. }
  125. fn debug(&mut self, line: &str) {
  126. self.log.push(From::from(line));
  127. }
  128. }
  129. #[test]
  130. fn nothing_needed_to_be_done() {
  131. let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(true), &|| Ok(())) );
  132. assert!(result.is_ok());
  133. }
  134. #[test]
  135. fn everything_is_ok() {
  136. let first = RefCell::new(true);
  137. let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| {
  138. let mut _first = first.borrow_mut();
  139. Ok(if *_first {
  140. *_first = false;
  141. true
  142. } else {
  143. false
  144. })
  145. }, &|| Ok(())) );
  146. assert!(result.is_ok());
  147. }
  148. #[test]
  149. fn executing_did_not_change_state() {
  150. let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(false), &|| Ok(())));
  151. assert_eq!(
  152. result.unwrap_err().description(),
  153. "Target not reached after executing symbol"
  154. );
  155. }
  156. #[test]
  157. fn executing_did_not_work() {
  158. let err = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(false), &|| Err(Box::new(DummySymbolError::Error(()))) )).unwrap_err();
  159. assert_eq!(err.description(), "Description");
  160. }
  161. }