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.

300 lines
8.1 KiB

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