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.

396 lines
11 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. use crate::resources::{BorrowResource, DefaultResources, Resource};
  2. use crate::schema::SymbolRunner;
  3. use crate::symbols::Symbol;
  4. use crate::to_artifact::ToArtifact;
  5. use crate::{DefaultBuilder, DefaultLocator, ResourceLocator, SymbolBuilder};
  6. use std::collections::HashSet;
  7. use std::error::Error;
  8. use std::fmt::Debug;
  9. use std::hash::Hash;
  10. use std::marker::PhantomData;
  11. pub trait CanHandle<X: ToArtifact> {
  12. fn handle(&mut self, x: X) -> Result<(X::Artifact, bool), Box<dyn Error>>;
  13. }
  14. macro_rules! can_handle {
  15. ( $($name:ident)* ) => (
  16. #[allow(non_snake_case)]
  17. impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, $($name: Resource,)*>
  18. CanHandle<($($name,)*)>
  19. for Setup<_SR, _L, _R, _B>
  20. where
  21. $(
  22. _B: SymbolBuilder<$name>,
  23. <_B as SymbolBuilder<$name>>::Symbol: Runnable + Debug,
  24. _L: ResourceLocator<$name>,
  25. _R: From<$name> + BorrowResource<$name>,
  26. Self: CanHandle<<_B as SymbolBuilder<$name>>::Prerequisites> +
  27. CanHandle<<_L as ResourceLocator<$name>>::Prerequisites>
  28. ),*
  29. {
  30. fn handle(&mut self, ($($name,)*): ($($name,)*)) -> Result<(($($name::Artifact,)*), bool), Box<dyn Error>>
  31. {
  32. $(let $name = self.add($name)?;)*
  33. Ok((($($name.0,)*), false $(|| $name.1)*))
  34. }
  35. }
  36. );
  37. }
  38. for_each_tuple!(can_handle);
  39. // This is for self-referential T
  40. // FIXME: Wait for specialization
  41. impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, T: Resource> CanHandle<Option<T>>
  42. for Setup<_SR, _L, _R, _B>
  43. where
  44. _B: SymbolBuilder<T>,
  45. <_B as SymbolBuilder<T>>::Symbol: Runnable + Debug,
  46. _L: ResourceLocator<T, Prerequisites = Option<T>>,
  47. _R: From<T> + BorrowResource<T>,
  48. Self: CanHandle<<_B as SymbolBuilder<T>>::Prerequisites>,
  49. {
  50. fn handle(
  51. &mut self,
  52. r: Option<T>,
  53. ) -> Result<(<Option<T> as ToArtifact>::Artifact, bool), Box<dyn Error>> {
  54. Ok(match r {
  55. Some(r) => {
  56. let (result, did_run) = self.add(r)?;
  57. (Some(result), did_run)
  58. }
  59. None => (None, false),
  60. })
  61. }
  62. }
  63. impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, T: Resource> CanHandle<T> for Setup<_SR, _L, _R, _B>
  64. where
  65. _B: SymbolBuilder<T>,
  66. <_B as SymbolBuilder<T>>::Symbol: Runnable + Debug,
  67. _L: ResourceLocator<T>,
  68. _R: From<T> + Debug + BorrowResource<T>,
  69. Self: CanHandle<<_B as SymbolBuilder<T>>::Prerequisites>
  70. + CanHandle<<_L as ResourceLocator<T>>::Prerequisites>,
  71. {
  72. fn handle(&mut self, r: T) -> Result<(<T as ToArtifact>::Artifact, bool), Box<dyn Error>> {
  73. self.add(r)
  74. }
  75. }
  76. pub struct Setup<
  77. SR,
  78. L = DefaultLocator,
  79. R = DefaultResources<'static, &'static str>,
  80. B = DefaultBuilder,
  81. > {
  82. symbol_runner: SR,
  83. resources: HashSet<R>,
  84. phantom: PhantomData<(L, B)>,
  85. }
  86. // https://github.com/rust-lang/rust/issues/27336
  87. impl<SR> Setup<SR> {
  88. pub fn new(symbol_runner: SR) -> Self {
  89. Self {
  90. symbol_runner,
  91. resources: Default::default(),
  92. phantom: Default::default(),
  93. }
  94. }
  95. }
  96. impl<SR, L, R: Hash + Eq, B> Setup<SR, L, R, B> {
  97. pub fn new_with(symbol_runner: SR) -> Self {
  98. Self {
  99. symbol_runner,
  100. resources: Default::default(),
  101. phantom: Default::default(),
  102. }
  103. }
  104. }
  105. pub trait Runnable {
  106. fn run<R: SymbolRunner>(&self, runner: &R, force: bool) -> Result<bool, Box<dyn Error>>;
  107. }
  108. impl<S: Symbol + Debug> Runnable for S {
  109. fn run<R: SymbolRunner>(&self, runner: &R, force: bool) -> Result<bool, Box<dyn Error>> {
  110. runner.run_symbol(self, force)
  111. }
  112. }
  113. macro_rules! runnable_for_tuple {
  114. ( $($name:ident)* ) => (
  115. #[allow(non_snake_case)]
  116. impl<$($name: Symbol + Debug,)*> Runnable for ($($name,)*) {
  117. #[allow(unused)]
  118. fn run<_R: SymbolRunner>(&self, runner: &_R, force: bool) -> Result<bool, Box<dyn Error>> {
  119. let ($($name,)*) = self;
  120. let mut result = false;
  121. $(result = runner.run_symbol($name, force || result)? || result;)*
  122. Ok(result)
  123. }
  124. }
  125. );
  126. }
  127. for_each_tuple!(runnable_for_tuple);
  128. impl<SR: SymbolRunner, L, Rs: Hash + Eq, B> Setup<SR, L, Rs, B> {
  129. pub fn add<R: Resource>(&mut self, resource: R) -> Result<(R::Artifact, bool), Box<dyn Error>>
  130. where
  131. B: SymbolBuilder<R>,
  132. <B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
  133. L: ResourceLocator<R>,
  134. Rs: From<R> + BorrowResource<R>,
  135. Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
  136. {
  137. self.add_force(resource, false)
  138. }
  139. pub fn add_force<R: Resource>(
  140. &mut self,
  141. resource: R,
  142. force_run: bool,
  143. ) -> Result<(R::Artifact, bool), Box<dyn Error>>
  144. where
  145. B: SymbolBuilder<R>,
  146. <B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
  147. L: ResourceLocator<R>,
  148. Rs: From<R> + BorrowResource<R>,
  149. Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
  150. {
  151. let (target, target_prereqs) = L::locate(&resource);
  152. let storable_resource = Rs::from(resource);
  153. let did_run = if self.resources.get(&storable_resource).is_some() {
  154. assert!(
  155. !force_run,
  156. "Forcing to run an already-added resource is a logical error"
  157. );
  158. false
  159. } else {
  160. let (_, target_prereqs_did_run) = self.handle(target_prereqs)?;
  161. let (symbol, prereqs_did_run) =
  162. self.get_symbol(storable_resource.borrow_resource().unwrap(), &target)?;
  163. self.resources.insert(storable_resource);
  164. self.run_symbol(
  165. symbol,
  166. force_run || target_prereqs_did_run || prereqs_did_run,
  167. )?
  168. };
  169. Ok((target, did_run))
  170. }
  171. fn get_symbol<R: Resource>(
  172. &mut self,
  173. resource: &R,
  174. target: &R::Artifact,
  175. ) -> Result<(<B as SymbolBuilder<R>>::Symbol, bool), Box<dyn Error>>
  176. where
  177. B: SymbolBuilder<R>,
  178. Self: CanHandle<B::Prerequisites>,
  179. {
  180. let (prereqs, prereqs_did_run) = self.handle(B::prerequisites(resource))?;
  181. Ok((B::create(resource, target, prereqs), prereqs_did_run))
  182. }
  183. pub fn run_symbol<S: Runnable>(&self, symbol: S, force: bool) -> Result<bool, Box<dyn Error>> {
  184. symbol.run(&self.symbol_runner, force)
  185. }
  186. }
  187. #[cfg(test)]
  188. mod test {
  189. use crate::resources::{BorrowResource, Resource};
  190. use crate::schema::SymbolRunner;
  191. use crate::symbols::Symbol;
  192. use crate::to_artifact::ToArtifact;
  193. use crate::{ResourceLocator, Setup, SymbolBuilder};
  194. use std::cell::RefCell;
  195. use std::error::Error;
  196. use std::fmt::Debug;
  197. use std::rc::Rc;
  198. struct TestSymbolRunner {
  199. count: Rc<RefCell<usize>>,
  200. }
  201. impl SymbolRunner for TestSymbolRunner {
  202. fn run_symbol<S: Symbol + Debug>(
  203. &self,
  204. symbol: &S,
  205. force: bool,
  206. ) -> Result<bool, Box<dyn Error>> {
  207. let run = force || !symbol.target_reached()?;
  208. if run {
  209. *self.count.borrow_mut() += 1;
  210. }
  211. Ok(run)
  212. }
  213. }
  214. #[derive(Debug, PartialEq, Eq, Hash)]
  215. struct TestResource<T>(&'static str, T);
  216. impl<T> Resource for TestResource<T> {
  217. type Artifact = ();
  218. }
  219. #[derive(Debug, Hash, PartialEq, Eq)]
  220. enum Resources {
  221. A(TestResource<&'static str>),
  222. B(TestResource<()>),
  223. }
  224. impl From<TestResource<&'static str>> for Resources {
  225. fn from(from: TestResource<&'static str>) -> Self {
  226. Self::A(from)
  227. }
  228. }
  229. impl From<TestResource<()>> for Resources {
  230. fn from(from: TestResource<()>) -> Self {
  231. Self::B(from)
  232. }
  233. }
  234. impl BorrowResource<TestResource<&'static str>> for Resources {
  235. fn borrow_resource(&self) -> Option<&TestResource<&'static str>> {
  236. match self {
  237. Self::A(a) => Some(a),
  238. _ => None,
  239. }
  240. }
  241. }
  242. impl BorrowResource<TestResource<()>> for Resources {
  243. fn borrow_resource(&self) -> Option<&TestResource<()>> {
  244. match self {
  245. Self::B(b) => Some(b),
  246. _ => None,
  247. }
  248. }
  249. }
  250. struct TestResourceLocator;
  251. impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
  252. type Prerequisites = ();
  253. fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
  254. ((), ())
  255. }
  256. }
  257. struct TestSymbolBuilder;
  258. impl SymbolBuilder<TestResource<&'static str>> for TestSymbolBuilder {
  259. type Symbol = TestSymbol;
  260. type Prerequisites = TestResource<()>;
  261. fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
  262. TestResource(resource.1, ())
  263. }
  264. fn create(
  265. resource: &TestResource<&'static str>,
  266. (): &(),
  267. _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
  268. ) -> Self::Symbol {
  269. TestSymbol {
  270. reached: resource.0.chars().next().unwrap().is_uppercase(),
  271. }
  272. }
  273. }
  274. impl SymbolBuilder<TestResource<()>> for TestSymbolBuilder {
  275. type Symbol = TestSymbol;
  276. type Prerequisites = ();
  277. fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
  278. fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Symbol {
  279. TestSymbol {
  280. reached: resource.0.chars().next().unwrap().is_uppercase(),
  281. }
  282. }
  283. }
  284. #[derive(Debug)]
  285. struct TestSymbol {
  286. reached: bool,
  287. }
  288. impl Symbol for TestSymbol {
  289. fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  290. Ok(self.reached)
  291. }
  292. fn execute(&self) -> Result<(), Box<dyn Error>> {
  293. Ok(())
  294. }
  295. }
  296. fn get_setup() -> (
  297. Rc<RefCell<usize>>,
  298. Setup<TestSymbolRunner, TestResourceLocator, Resources, TestSymbolBuilder>,
  299. ) {
  300. let count = Rc::new(RefCell::new(0));
  301. let runner = TestSymbolRunner {
  302. count: Rc::clone(&count),
  303. };
  304. (count, Setup::new_with(runner))
  305. }
  306. #[test]
  307. fn correctly_uses_force() {
  308. let (count, mut setup) = get_setup();
  309. setup.add(TestResource("A", "b")).unwrap();
  310. assert_eq!(*count.borrow(), 2);
  311. setup.add(TestResource("A", "b")).unwrap();
  312. assert_eq!(*count.borrow(), 2);
  313. let (count, mut setup) = get_setup();
  314. setup.add(TestResource("A", "B")).unwrap();
  315. assert_eq!(*count.borrow(), 0);
  316. }
  317. #[test]
  318. fn correctly_handles_symbol_tuples() {
  319. let (count, setup) = get_setup();
  320. setup
  321. .run_symbol(
  322. (TestSymbol { reached: false }, TestSymbol { reached: false }),
  323. false,
  324. )
  325. .unwrap();
  326. assert_eq!(*count.borrow(), 2);
  327. let (count, setup) = get_setup();
  328. setup
  329. .run_symbol(
  330. (TestSymbol { reached: true }, TestSymbol { reached: false }),
  331. false,
  332. )
  333. .unwrap();
  334. assert_eq!(*count.borrow(), 1);
  335. // An unreached symbol forces all further symbols
  336. let (count, setup) = get_setup();
  337. setup
  338. .run_symbol(
  339. (TestSymbol { reached: false }, TestSymbol { reached: true }),
  340. false,
  341. )
  342. .unwrap();
  343. assert_eq!(*count.borrow(), 2);
  344. let (count, setup) = get_setup();
  345. setup
  346. .run_symbol(
  347. (TestSymbol { reached: true }, TestSymbol { reached: true }),
  348. false,
  349. )
  350. .unwrap();
  351. assert_eq!(*count.borrow(), 0);
  352. let (count, setup) = get_setup();
  353. setup
  354. .run_symbol(
  355. (TestSymbol { reached: true }, TestSymbol { reached: true }),
  356. true,
  357. )
  358. .unwrap();
  359. assert_eq!(*count.borrow(), 2);
  360. }
  361. }