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.

345 lines
9.5 KiB

  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. Ok($(runner.run_symbol($name, force)? || )* false)
  121. }
  122. }
  123. );
  124. }
  125. for_each_tuple!(runnable_for_tuple);
  126. impl<SR: SymbolRunner, L, Rs: Hash + Eq, B> Setup<SR, L, Rs, B> {
  127. pub fn add<R: Resource>(&mut self, resource: R) -> Result<(R::Artifact, bool), Box<dyn Error>>
  128. where
  129. B: SymbolBuilder<R>,
  130. <B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
  131. L: ResourceLocator<R>,
  132. Rs: From<R> + BorrowResource<R>,
  133. Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
  134. {
  135. self.add_force(resource, false)
  136. }
  137. pub fn add_force<R: Resource>(
  138. &mut self,
  139. resource: R,
  140. force_run: bool,
  141. ) -> Result<(R::Artifact, bool), Box<dyn Error>>
  142. where
  143. B: SymbolBuilder<R>,
  144. <B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
  145. L: ResourceLocator<R>,
  146. Rs: From<R> + BorrowResource<R>,
  147. Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
  148. {
  149. let (target, target_prereqs) = L::locate(&resource);
  150. let storable_resource = Rs::from(resource);
  151. let did_run = if self.resources.get(&storable_resource).is_some() {
  152. assert!(
  153. !force_run,
  154. "Forcing to run an already-added resource is a logical error"
  155. );
  156. false
  157. } else {
  158. let (_, target_prereqs_did_run) = self.handle(target_prereqs)?;
  159. let (symbol, prereqs_did_run) =
  160. self.get_symbol(storable_resource.borrow_resource().unwrap(), &target)?;
  161. self.resources.insert(storable_resource);
  162. self.run_symbol(
  163. symbol,
  164. force_run || target_prereqs_did_run || prereqs_did_run,
  165. )?
  166. };
  167. Ok((target, did_run))
  168. }
  169. fn get_symbol<R: Resource>(
  170. &mut self,
  171. resource: &R,
  172. target: &R::Artifact,
  173. ) -> Result<(<B as SymbolBuilder<R>>::Symbol, bool), Box<dyn Error>>
  174. where
  175. B: SymbolBuilder<R>,
  176. Self: CanHandle<B::Prerequisites>,
  177. {
  178. let (prereqs, prereqs_did_run) = self.handle(B::prerequisites(resource))?;
  179. Ok((B::create(resource, target, prereqs), prereqs_did_run))
  180. }
  181. pub fn run_symbol<S: Runnable>(&self, symbol: S, force: bool) -> Result<bool, Box<dyn Error>> {
  182. symbol.run(&self.symbol_runner, force)
  183. }
  184. }
  185. #[cfg(test)]
  186. mod test {
  187. use crate::resources::{BorrowResource, Resource};
  188. use crate::schema::SymbolRunner;
  189. use crate::symbols::Symbol;
  190. use crate::to_artifact::ToArtifact;
  191. use crate::{ResourceLocator, Setup, SymbolBuilder};
  192. use std::cell::RefCell;
  193. use std::error::Error;
  194. use std::fmt::Debug;
  195. use std::rc::Rc;
  196. struct TestSymbolRunner {
  197. count: Rc<RefCell<usize>>,
  198. }
  199. impl SymbolRunner for TestSymbolRunner {
  200. fn run_symbol<S: Symbol + Debug>(
  201. &self,
  202. symbol: &S,
  203. force: bool,
  204. ) -> Result<bool, Box<dyn Error>> {
  205. let run = force || !symbol.target_reached()?;
  206. if run {
  207. *self.count.borrow_mut() += 1;
  208. }
  209. Ok(run)
  210. }
  211. }
  212. #[derive(Debug, PartialEq, Eq, Hash)]
  213. struct TestResource<T>(&'static str, T);
  214. impl<T> Resource for TestResource<T> {
  215. type Artifact = ();
  216. }
  217. #[derive(Debug, Hash, PartialEq, Eq)]
  218. enum Resources {
  219. A(TestResource<&'static str>),
  220. B(TestResource<()>),
  221. }
  222. impl From<TestResource<&'static str>> for Resources {
  223. fn from(from: TestResource<&'static str>) -> Self {
  224. Self::A(from)
  225. }
  226. }
  227. impl From<TestResource<()>> for Resources {
  228. fn from(from: TestResource<()>) -> Self {
  229. Self::B(from)
  230. }
  231. }
  232. impl BorrowResource<TestResource<&'static str>> for Resources {
  233. fn borrow_resource(&self) -> Option<&TestResource<&'static str>> {
  234. match self {
  235. Self::A(a) => Some(a),
  236. _ => None,
  237. }
  238. }
  239. }
  240. impl BorrowResource<TestResource<()>> for Resources {
  241. fn borrow_resource(&self) -> Option<&TestResource<()>> {
  242. match self {
  243. Self::B(b) => Some(b),
  244. _ => None,
  245. }
  246. }
  247. }
  248. struct TestResourceLocator;
  249. impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
  250. type Prerequisites = ();
  251. fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
  252. ((), ())
  253. }
  254. }
  255. struct TestSymbolBuilder;
  256. impl SymbolBuilder<TestResource<&'static str>> for TestSymbolBuilder {
  257. type Symbol = TestSymbol;
  258. type Prerequisites = TestResource<()>;
  259. fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
  260. TestResource(resource.1, ())
  261. }
  262. fn create(
  263. resource: &TestResource<&'static str>,
  264. (): &(),
  265. _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
  266. ) -> Self::Symbol {
  267. TestSymbol {
  268. reached: resource.0.chars().next().unwrap().is_uppercase(),
  269. }
  270. }
  271. }
  272. impl SymbolBuilder<TestResource<()>> for TestSymbolBuilder {
  273. type Symbol = TestSymbol;
  274. type Prerequisites = ();
  275. fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
  276. fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Symbol {
  277. TestSymbol {
  278. reached: resource.0.chars().next().unwrap().is_uppercase(),
  279. }
  280. }
  281. }
  282. #[derive(Debug)]
  283. struct TestSymbol {
  284. reached: bool,
  285. }
  286. impl Symbol for TestSymbol {
  287. fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  288. Ok(self.reached)
  289. }
  290. fn execute(&self) -> Result<(), Box<dyn Error>> {
  291. Ok(())
  292. }
  293. }
  294. fn get_setup() -> (
  295. Rc<RefCell<usize>>,
  296. Setup<TestSymbolRunner, TestResourceLocator, Resources, TestSymbolBuilder>,
  297. ) {
  298. let count = Rc::new(RefCell::new(0));
  299. let runner = TestSymbolRunner {
  300. count: Rc::clone(&count),
  301. };
  302. (count, Setup::new_with(runner))
  303. }
  304. #[test]
  305. fn correctly_uses_force() {
  306. let (count, mut setup) = get_setup();
  307. setup.add(TestResource("A", "b")).unwrap();
  308. assert_eq!(*count.borrow(), 2);
  309. setup.add(TestResource("A", "b")).unwrap();
  310. assert_eq!(*count.borrow(), 2);
  311. let (count, mut setup) = get_setup();
  312. setup.add(TestResource("A", "B")).unwrap();
  313. assert_eq!(*count.borrow(), 0);
  314. }
  315. }