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.

336 lines
9.5 KiB

4 years ago
4 years ago
4 years ago
4 years ago
  1. use super::core::{RegularSetupCore, SetupCore};
  2. use super::runnable::Runnable;
  3. use super::util::{AddResult, AddableResource, InternalAddResult};
  4. use super::SymbolRunner;
  5. use crate::async_utils::sleep;
  6. use crate::loggers::{Logger, StoringLogger};
  7. use crate::resources::{DefaultArtifacts, DefaultResources, FromArtifact, FromResource};
  8. use crate::{DefaultBuilder, DefaultLocator};
  9. use futures_util::future::{FutureExt, Shared};
  10. use std::cell::{RefCell, RefMut};
  11. use std::collections::HashMap;
  12. use std::error::Error;
  13. use std::future::Future;
  14. use std::hash::Hash;
  15. use std::pin::Pin;
  16. use std::rc::Rc;
  17. use std::time::Duration;
  18. type Cache<Rs, As> = HashMap<Rs, Shared<Pin<Box<dyn Future<Output = (As, bool)>>>>>;
  19. #[derive(Debug)]
  20. struct SetupInner<CORE, Rs, As> {
  21. core: CORE,
  22. resources: RefCell<Cache<Rs, As>>,
  23. }
  24. #[derive(Debug)]
  25. pub struct Setup<SR, L, B, Rs, As>(Rc<SetupInner<RegularSetupCore<SR, L, B>, Rs, As>>);
  26. impl<L: 'static, B: 'static, SR: 'static, Rs: Hash + Eq + 'static, As: 'static>
  27. Setup<SR, L, B, Rs, As>
  28. {
  29. fn borrow_resources(&self) -> RefMut<'_, Cache<Rs, As>> {
  30. self.0.resources.borrow_mut()
  31. }
  32. pub async fn add<R: AddableResource>(&self, resource: R, force_run: bool) -> InternalAddResult<R>
  33. where
  34. Rs: FromResource<R>,
  35. As: FromArtifact<R> + Clone,
  36. R::Artifact: Clone,
  37. RegularSetupCore<SR, L, B>: SetupCore<R, Self>,
  38. {
  39. let (storable_resource, weak_resource) = Rs::from_resource(resource);
  40. let mut resources = self.borrow_resources();
  41. let result = if let Some(future) = resources.remove(&storable_resource) {
  42. assert!(
  43. !force_run,
  44. "Forcing to run an already-added resource is a logical error"
  45. );
  46. resources.insert(storable_resource, future.clone());
  47. drop(resources);
  48. let logger = StoringLogger::default();
  49. logger.trace(format!(
  50. "{:?} already added",
  51. weak_resource.upgrade().expect("Dangling!")
  52. ));
  53. let (t, did_run) = future.await;
  54. Ok((logger, t, did_run))
  55. } else {
  56. let inner_weak = Rc::downgrade(&self.0);
  57. let future = Box::pin(async move {
  58. let this = Self(inner_weak.upgrade().expect("Dangling!"));
  59. let resource = weak_resource.upgrade().expect("Dangling!");
  60. // Need to convert Box<Error> to String for Clone for Shared
  61. let result = this.0.core.add(&this, resource, force_run).await;
  62. result
  63. .map(|(logger, t, did_run)| (logger, As::from_artifact(t), did_run))
  64. .map_err(|(logger, e)| (logger, e.to_string()))
  65. })
  66. .shared();
  67. let future_clone = future.clone();
  68. resources.insert(
  69. storable_resource,
  70. (Box::pin(async move {
  71. let result = future_clone.await;
  72. if result.is_err() {
  73. // Step back to give the initial caller time to handle the error before unwrapping
  74. sleep(Duration::from_millis(0)).await;
  75. }
  76. result.map(|(_, t, did_run)| (t, did_run)).unwrap()
  77. }) as Pin<Box<dyn Future<Output = (As, bool)>>>)
  78. .shared(),
  79. );
  80. drop(resources);
  81. let result = future.await;
  82. result.map_err(|(logger, e)| (logger, e.into()))
  83. };
  84. result.map(|(logger, t, did_run)| (logger, t.into_artifact(), did_run))
  85. }
  86. }
  87. #[derive(Debug)]
  88. pub struct SetupFacade<
  89. SR,
  90. LOG,
  91. L = DefaultLocator,
  92. B = DefaultBuilder,
  93. Rs = DefaultResources<'static, &'static str>,
  94. As = DefaultArtifacts<'static, &'static str>,
  95. >(LOG, Setup<SR, L, B, Rs, As>);
  96. impl<SR, LOG> SetupFacade<SR, LOG> {
  97. pub fn new(symbol_runner: SR, logger: LOG) -> Self {
  98. Self::new_with(symbol_runner, logger)
  99. }
  100. }
  101. impl<L, B, As, SR, LOG, Rs: Hash + Eq> SetupFacade<SR, LOG, L, B, Rs, As> {
  102. pub fn new_with(symbol_runner: SR, logger: LOG) -> Self {
  103. Self(
  104. logger,
  105. Setup(Rc::new(SetupInner {
  106. core: RegularSetupCore::new(symbol_runner),
  107. resources: RefCell::default(),
  108. })),
  109. )
  110. }
  111. }
  112. impl<
  113. SR: 'static,
  114. LOG: 'static + Logger,
  115. L: 'static,
  116. B: 'static,
  117. Rs: 'static + Eq + Hash,
  118. As: 'static,
  119. > SetupFacade<SR, LOG, L, B, Rs, As>
  120. {
  121. pub async fn add_force<R: AddableResource>(&self, resource: R, force_run: bool) -> AddResult<R>
  122. where
  123. Rs: FromResource<R>,
  124. As: FromArtifact<R> + Clone,
  125. R::Artifact: Clone,
  126. RegularSetupCore<SR, L, B>: SetupCore<R, Setup<SR, L, B, Rs, As>>,
  127. {
  128. let result = self.1.add(resource, force_run).await;
  129. match result {
  130. Ok((logger, t, did_run)) => {
  131. if self
  132. .0
  133. .put(logger.release().into_iter().filter(|e| e.0 <= 3))
  134. == 0
  135. {
  136. self.0.write(3, ".");
  137. }
  138. Ok((t, did_run))
  139. }
  140. Err((logger, e)) => {
  141. self.0.put(logger.release());
  142. Err(e)
  143. }
  144. }
  145. }
  146. pub async fn add<R: AddableResource>(&self, resource: R) -> AddResult<R>
  147. where
  148. RegularSetupCore<SR, L, B>: SetupCore<R, Setup<SR, L, B, Rs, As>>,
  149. Rs: FromResource<R>,
  150. As: FromArtifact<R> + Clone,
  151. R::Artifact: Clone,
  152. SR: SymbolRunner,
  153. {
  154. self.add_force(resource, false).await
  155. }
  156. pub async fn run_symbol<S: Runnable>(
  157. &self,
  158. symbol: S,
  159. force: bool,
  160. ) -> Result<bool, Box<dyn Error>>
  161. where
  162. RegularSetupCore<SR, L, B>: SymbolRunner,
  163. {
  164. symbol.run(&(self.1).0.core, &self.0, force).await
  165. }
  166. }
  167. #[cfg(test)]
  168. mod test {
  169. use super::SymbolRunner;
  170. use crate::async_utils::run;
  171. use crate::loggers::{Logger, StoringLogger};
  172. use crate::resources::{FromArtifact, FromResource, Resource};
  173. use crate::symbols::Symbol;
  174. use crate::to_artifact::ToArtifact;
  175. use crate::{ImplementationBuilder, ResourceLocator, Setup};
  176. use async_trait::async_trait;
  177. use std::cell::RefCell;
  178. use std::error::Error;
  179. use std::fmt::Debug;
  180. use std::rc::{Rc, Weak};
  181. struct TestSymbolRunner {
  182. count: Rc<RefCell<usize>>,
  183. }
  184. #[async_trait(?Send)]
  185. impl SymbolRunner for TestSymbolRunner {
  186. async fn run_symbol<S: Symbol + Debug, L: Logger>(
  187. &self,
  188. symbol: &S,
  189. _logger: &L,
  190. force: bool,
  191. ) -> Result<bool, Box<dyn Error>> {
  192. let run = force || !symbol.target_reached().await?;
  193. if run {
  194. *self.count.borrow_mut() += 1;
  195. }
  196. Ok(run)
  197. }
  198. }
  199. #[derive(Debug, PartialEq, Eq, Hash)]
  200. struct TestResource<T>(&'static str, T);
  201. impl<T> Resource for TestResource<T> {
  202. type Artifact = ();
  203. }
  204. #[derive(Debug, Hash, PartialEq, Eq)]
  205. enum Resources {
  206. A(Rc<TestResource<&'static str>>),
  207. B(Rc<TestResource<()>>),
  208. }
  209. impl FromResource<TestResource<&'static str>> for Resources {
  210. fn from_resource(from: TestResource<&'static str>) -> (Self, Weak<TestResource<&'static str>>) {
  211. let inner = Rc::new(from);
  212. (Self::A(Rc::clone(&inner)), Rc::downgrade(&inner))
  213. }
  214. }
  215. impl FromResource<TestResource<()>> for Resources {
  216. fn from_resource(from: TestResource<()>) -> (Self, Weak<TestResource<()>>) {
  217. let inner = Rc::new(from);
  218. (Self::B(Rc::clone(&inner)), Rc::downgrade(&inner))
  219. }
  220. }
  221. #[derive(Clone)]
  222. struct Artifacts;
  223. impl<V> FromArtifact<TestResource<V>> for Artifacts {
  224. fn from_artifact(_from: ()) -> Self {
  225. Self
  226. }
  227. #[allow(clippy::unused_unit)]
  228. fn into_artifact(self) -> () {
  229. #[allow(clippy::unused_unit)]
  230. ()
  231. }
  232. }
  233. struct TestResourceLocator;
  234. impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
  235. type Prerequisites = ();
  236. fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
  237. ((), ())
  238. }
  239. }
  240. struct TestImplementationBuilder;
  241. impl ImplementationBuilder<TestResource<&'static str>> for TestImplementationBuilder {
  242. type Implementation = TestSymbol;
  243. type Prerequisites = TestResource<()>;
  244. fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
  245. TestResource(resource.1, ())
  246. }
  247. fn create(
  248. resource: &TestResource<&'static str>,
  249. (): &(),
  250. _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
  251. ) -> Self::Implementation {
  252. TestSymbol {
  253. reached: resource.0.chars().next().unwrap().is_uppercase(),
  254. }
  255. }
  256. }
  257. impl ImplementationBuilder<TestResource<()>> for TestImplementationBuilder {
  258. type Implementation = TestSymbol;
  259. type Prerequisites = ();
  260. fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
  261. fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Implementation {
  262. TestSymbol {
  263. reached: resource.0.chars().next().unwrap().is_uppercase(),
  264. }
  265. }
  266. }
  267. #[derive(Debug)]
  268. struct TestSymbol {
  269. reached: bool,
  270. }
  271. #[async_trait(?Send)]
  272. impl Symbol for TestSymbol {
  273. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  274. Ok(self.reached)
  275. }
  276. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  277. Ok(())
  278. }
  279. }
  280. fn get_setup() -> (
  281. Rc<RefCell<usize>>,
  282. Setup<
  283. TestSymbolRunner,
  284. StoringLogger,
  285. TestResourceLocator,
  286. TestImplementationBuilder,
  287. Resources,
  288. Artifacts,
  289. >,
  290. ) {
  291. let count = Rc::new(RefCell::new(0));
  292. let runner = TestSymbolRunner {
  293. count: Rc::clone(&count),
  294. };
  295. (count, Setup::new_with(runner, StoringLogger::new()))
  296. }
  297. #[test]
  298. fn correctly_uses_force() {
  299. run(async {
  300. let (count, setup) = get_setup();
  301. setup.add(TestResource("A", "b")).await.unwrap();
  302. assert_eq!(*count.borrow(), 2);
  303. setup.add(TestResource("A", "b")).await.unwrap();
  304. assert_eq!(*count.borrow(), 2);
  305. let (count, setup) = get_setup();
  306. setup.add(TestResource("A", "B")).await.unwrap();
  307. assert_eq!(*count.borrow(), 0);
  308. });
  309. }
  310. }