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.

269 lines
7.7 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
7 years ago
7 years ago
7 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. use std::cell::RefCell;
  91. use std::collections::HashSet;
  92. use resources::Resource;
  93. pub struct NonRepeatingSymbolRunner<'a, R> where R: 'a + SymbolRunner {
  94. upstream: &'a R,
  95. done: RefCell<HashSet<Resource>>
  96. }
  97. impl<'a, R> NonRepeatingSymbolRunner<'a, R> where R: SymbolRunner {
  98. pub fn new(symbol_runner: &'a R) -> Self {
  99. NonRepeatingSymbolRunner{ upstream: symbol_runner, done: RefCell::new(HashSet::new()) }
  100. }
  101. }
  102. impl<'a, R> SymbolRunner for NonRepeatingSymbolRunner<'a, R> where R: SymbolRunner {
  103. fn run_symbol<S: ?Sized + Symbol + fmt::Display>(&self, logger: &mut Logger, symbol: &S) -> Result<(), Box<Error>>
  104. {
  105. if let Some(resources) = symbol.provides() {
  106. let mut done = self.done.borrow_mut();
  107. let mut has_to_run = false;
  108. for resource in resources {
  109. if !done.contains(&resource) {
  110. has_to_run = true;
  111. done.insert(resource.clone());
  112. }
  113. }
  114. if !has_to_run {
  115. return Ok(());
  116. }
  117. }
  118. self.upstream.run_symbol(logger, &*symbol)
  119. }
  120. }
  121. use std::marker::PhantomData;
  122. pub struct RequirementsResolvingSymbolRunner<'a, 's, R: 'a + SymbolRunner, G: 'a + SymbolRepository<'s>>(&'a R, &'a G, PhantomData<Box<Symbol + 's>>);
  123. impl<'a, 's, R, G> RequirementsResolvingSymbolRunner<'a, 's, R, G> where R: SymbolRunner, G: SymbolRepository<'s> {
  124. pub fn new(symbol_runner: &'a R, symbol_repo: &'a G) -> Self {
  125. RequirementsResolvingSymbolRunner(symbol_runner, symbol_repo, PhantomData)
  126. }
  127. }
  128. impl<'a, 's, R, G> SymbolRunner for RequirementsResolvingSymbolRunner<'a, 's, R, G> where R: SymbolRunner, G: SymbolRepository<'s> {
  129. fn run_symbol<S: ?Sized + Symbol + fmt::Display>(&self, logger: &mut Logger, symbol: &S) -> Result<(), Box<Error>>
  130. {
  131. for resource in symbol.get_prerequisites() {
  132. if let Some(dep) = self.1.get_symbol(&resource) {
  133. try!(self.run_symbol(logger, &*dep));
  134. }
  135. }
  136. self.0.run_symbol(logger, &*symbol)
  137. }
  138. }
  139. // FIXME: Add ExpectingSymbolRunner
  140. #[cfg(test)]
  141. mod test {
  142. use std::cell::RefCell;
  143. use std::error::Error;
  144. use std::fmt;
  145. use loggers::Logger;
  146. use symbols::Symbol;
  147. use schema::SymbolRunner;
  148. use schema::InitializingSymbolRunner;
  149. #[derive(Debug, PartialEq, Clone)]
  150. enum DummySymbolError {
  151. Error(())
  152. }
  153. impl Error for DummySymbolError {
  154. fn description(&self) -> &str {
  155. return "Description";
  156. }
  157. }
  158. impl fmt::Display for DummySymbolError {
  159. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  160. write!(f, "Dummy symbol error")
  161. }
  162. }
  163. struct DummySymbol<'a> {
  164. _execute: &'a Fn() -> Result<(), Box<Error>>,
  165. _target_reached: &'a Fn() -> Result<bool, Box<Error>>
  166. }
  167. impl<'a> Symbol for DummySymbol<'a> {
  168. fn target_reached(&self) -> Result<bool, Box<Error>> {
  169. (self._target_reached)()
  170. }
  171. fn execute(&self) -> Result<(), Box<Error>> {
  172. (self._execute)()
  173. }
  174. }
  175. impl<'a> fmt::Display for DummySymbol<'a> {
  176. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  177. write!(f, "Dummy symbol")
  178. }
  179. }
  180. impl<'a> DummySymbol<'a> {
  181. fn new(target_reached: &'a Fn() -> Result<bool, Box<Error>>, execute: &'a Fn() -> Result<(), Box<Error>>) -> DummySymbol<'a> {
  182. DummySymbol { _target_reached: target_reached, _execute: execute }
  183. }
  184. }
  185. struct DummyLogger {
  186. log: Vec<String>
  187. }
  188. impl DummyLogger {
  189. fn new() -> DummyLogger {
  190. DummyLogger { log: Vec::new() }
  191. }
  192. }
  193. impl Logger for DummyLogger {
  194. fn write(&mut self, line: &str) {
  195. self.log.push(From::from(line));
  196. }
  197. fn debug(&mut self, line: &str) {
  198. self.log.push(From::from(line));
  199. }
  200. }
  201. #[test]
  202. fn nothing_needed_to_be_done() {
  203. let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(true), &|| Ok(())) );
  204. assert!(result.is_ok());
  205. }
  206. #[test]
  207. fn everything_is_ok() {
  208. let first = RefCell::new(true);
  209. let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| {
  210. let mut _first = first.borrow_mut();
  211. Ok(if *_first {
  212. *_first = false;
  213. true
  214. } else {
  215. false
  216. })
  217. }, &|| Ok(())) );
  218. assert!(result.is_ok());
  219. }
  220. #[test]
  221. fn executing_did_not_change_state() {
  222. let result = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(false), &|| Ok(())));
  223. assert_eq!(
  224. result.unwrap_err().description(),
  225. "Target not reached after executing symbol"
  226. );
  227. }
  228. #[test]
  229. fn executing_did_not_work() {
  230. let err = InitializingSymbolRunner.run_symbol( &mut DummyLogger::new(), &DummySymbol::new(&|| Ok(false), &|| Err(Box::new(DummySymbolError::Error(()))) )).unwrap_err();
  231. assert_eq!(err.description(), "Description");
  232. }
  233. }