melodium_loader/
loader.rs

1use crate::package::PackageTrait as Package;
2use crate::package_manager::{PackageManager, PackageManagerConfiguration};
3use crate::{LoadingConfig, PackageInfo};
4use melodium_common::descriptor::{
5    Collection, Context, Entry, Function, Identifier, IdentifierRequirement, Loader as LoaderTrait,
6    LoadingError, LoadingResult, Model, PackageRequirement, Treatment,
7};
8use melodium_repository::network::NetworkRepositoryConfiguration;
9use melodium_repository::{Repository, RepositoryConfig};
10use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard};
11
12/**
13 * Manages loading of Mélodium packages.
14 *
15 * The loader take care of managing package dependencies, loading inner elements of packages,
16 * and building a coherent [Collection].
17 *
18 * Loading can be made through the `load`-ing functions, and then final collection rendered using `build()`.
19 *
20 * This loader aims to be lazy, please read carefully the behavior of each `load`-ing function in order to make best
21 * use of them.
22 */
23#[derive(Debug)]
24pub struct Loader {
25    collection: RwLock<Collection>,
26    package_manager: PackageManager,
27}
28
29impl Loader {
30    pub fn new(config: LoadingConfig) -> Self {
31        Self {
32            collection: RwLock::new(Collection::new()),
33            package_manager: PackageManager::new(PackageManagerConfiguration {
34                repositories: vec![Arc::new(Mutex::new(Repository::new(RepositoryConfig {
35                    repository_location: {
36                        let mut path = std::env::var_os("MELODIUM_HOME")
37                            .map(|var| var.into())
38                            .or_else(|| {
39                                simple_home_dir::home_dir().map(|mut path| {
40                                    path.push(".melodium");
41                                    path
42                                })
43                            })
44                            .unwrap_or_else(|| {
45                                let mut path = std::env::temp_dir();
46                                path.push("melodium");
47                                path
48                            });
49                        path.push(env!("CARGO_PKG_VERSION"));
50                        path
51                    },
52                    network: if cfg!(feature = "network") {
53                        Some(NetworkRepositoryConfiguration::new())
54                    } else {
55                        None
56                    },
57                })))],
58                core_packages: config.core_packages,
59                search_locations: config.search_locations,
60                raw_elements: config.raw_elements,
61                allow_network: cfg!(feature = "network"),
62            }),
63        }
64    }
65
66    /**
67     * Loads the given package, according to requirements.
68     *
69     * This function _does not_ load any package content on its own, see [Self::load], [Self::load_all] or the functions of [LoaderTrait]
70     * to get elements required loaded.
71     */
72    pub fn load_package(&self, requirement: &PackageRequirement) -> LoadingResult<()> {
73        self.package_manager
74            .get_package(requirement)
75            .and(LoadingResult::new_success(()))
76    }
77
78    /**
79     * Loads the given raw package content.
80     *
81     * Returns the name of the package and its main entry point, if any.
82     *
83     * This function _does not_ load any package content on its own, see [Self::load], [Self::load_all] or the functions of [LoaderTrait]
84     * to get elements required loaded.
85     */
86    pub fn load_raw(&self, raw_content: Arc<Vec<u8>>) -> LoadingResult<Arc<dyn PackageInfo>> {
87        self.package_manager
88            .add_raw_package(raw_content)
89            .and_then(|pkg| LoadingResult::new_success(Arc::clone(&pkg) as Arc<dyn PackageInfo>))
90    }
91
92    /**
93     * Load the given identifier.
94     */
95    pub fn load(
96        &self,
97        identifier_requirement: &IdentifierRequirement,
98    ) -> LoadingResult<Identifier> {
99        self.get_with_load(identifier_requirement)
100            .and_then(|entry| LoadingResult::new_success(entry.identifier()))
101    }
102
103    /**
104     * Load all the elements from all the packages.
105     *
106     * Packages concerned have to be already loaded through [Self::load_package] of [Self::load_raw] functions.
107     */
108    pub fn load_all(&self) -> LoadingResult<()> {
109        let mut result = LoadingResult::new_success(());
110        for package in self.package_manager.get_packages() {
111            if let Some(additions) = result.merge_degrade_failure(package.full_collection(self)) {
112                self.add_collection(additions);
113            }
114        }
115        result
116    }
117
118    /**
119     * Proceed to build of coherent collection.
120     */
121    pub fn build(&self) -> LoadingResult<Arc<Collection>> {
122        let mut result = LoadingResult::new_success(());
123        let collection = Arc::new(self.collection.read().unwrap().clone());
124
125        for package in self.package_manager.get_packages() {
126            result.merge_degrade_failure(package.make_building(&collection));
127        }
128
129        result.and(LoadingResult::new_success(collection))
130    }
131
132    pub fn packages(&self) -> Vec<Arc<dyn PackageInfo>> {
133        self.package_manager
134            .get_packages()
135            .into_iter()
136            .map(|pkg| Arc::clone(&pkg) as Arc<dyn PackageInfo>)
137            .collect()
138    }
139
140    pub fn collection(&self) -> RwLockReadGuard<Collection> {
141        self.collection.read().unwrap()
142    }
143
144    pub fn get_with_load(
145        &self,
146        identifier_requirement: &IdentifierRequirement,
147    ) -> LoadingResult<Entry> {
148        let mut result = LoadingResult::new_success(());
149        let entry = self
150            .collection
151            .read()
152            .unwrap()
153            .get(identifier_requirement)
154            .cloned();
155        if let Some(entry) = entry {
156            result.and_degrade_failure(LoadingResult::new_success(entry))
157        } else if let Some(package) = result.merge_degrade_failure(
158            self.package_manager
159                .get_package(&identifier_requirement.package_requirement()),
160        ) {
161            package
162                .element(self, &identifier_requirement)
163                .and_then(|additions| {
164                    self.add_collection(additions);
165                    result.and_degrade_failure(LoadingResult::new_success(
166                        self.collection
167                            .read()
168                            .unwrap()
169                            .get(identifier_requirement)
170                            .unwrap()
171                            .clone(),
172                    ))
173                })
174        } else {
175            result.and_degrade_failure(LoadingResult::new_failure(LoadingError::no_package(
176                167,
177                identifier_requirement.package_requirement(),
178            )))
179        }
180    }
181
182    fn add_collection(&self, other_collection: Collection) {
183        let existing = self.collection.read().unwrap().identifiers();
184        let mut others = other_collection.identifiers();
185
186        others.retain(|id| !existing.contains(id));
187
188        if !others.is_empty() {
189            let mut collection = self.collection.write().unwrap();
190            for id in &others {
191                collection.insert(other_collection.get(&id.into()).unwrap().clone());
192            }
193        }
194    }
195}
196
197impl LoaderTrait for Loader {
198    fn load_context(
199        &self,
200        identifier_requirement: &IdentifierRequirement,
201    ) -> LoadingResult<Arc<dyn Context>> {
202        self.get_with_load(identifier_requirement)
203            .and_then(|entry| match entry {
204                Entry::Context(context) => LoadingResult::new_success(context),
205                _ => LoadingResult::new_failure(LoadingError::context_expected(
206                    168,
207                    None,
208                    identifier_requirement.clone(),
209                )),
210            })
211    }
212
213    fn load_function(
214        &self,
215        identifier_requirement: &IdentifierRequirement,
216    ) -> LoadingResult<Arc<dyn Function>> {
217        self.get_with_load(identifier_requirement)
218            .and_then(|entry| match entry {
219                Entry::Function(function) => LoadingResult::new_success(function),
220                _ => LoadingResult::new_failure(LoadingError::function_expected(
221                    169,
222                    None,
223                    identifier_requirement.clone(),
224                )),
225            })
226    }
227
228    fn load_model(
229        &self,
230        identifier_requirement: &IdentifierRequirement,
231    ) -> LoadingResult<Arc<dyn Model>> {
232        self.get_with_load(identifier_requirement)
233            .and_then(|entry| match entry {
234                Entry::Model(model) => LoadingResult::new_success(model),
235                _ => LoadingResult::new_failure(LoadingError::model_expected(
236                    170,
237                    None,
238                    identifier_requirement.clone(),
239                )),
240            })
241    }
242
243    fn load_treatment(
244        &self,
245        identifier_requirement: &IdentifierRequirement,
246    ) -> LoadingResult<Arc<dyn Treatment>> {
247        self.get_with_load(identifier_requirement)
248            .and_then(|entry| match entry {
249                Entry::Treatment(treatment) => LoadingResult::new_success(treatment),
250                _ => LoadingResult::new_failure(LoadingError::treatment_expected(
251                    171,
252                    None,
253                    identifier_requirement.clone(),
254                )),
255            })
256    }
257}