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.

338 lines
9.6 KiB

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