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.

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