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.

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