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.

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