use std::collections::HashMap;
use std::path::{Path, PathBuf};
#[cfg(feature = "parallel")]
use std::sync::{Arc, Mutex};
#[cfg(feature = "native")]
use walkdir::WalkDir;
use crate::config_chirho::ModuleConfigChirho;
use crate::error_chirho::{ErrorChirho, ResultChirho};
use super::module_factory_chirho::{LoadedModuleChirho, load_module_chirho};
#[derive(Debug, Clone)]
#[derive(Default)]
pub struct SwMgrConfigChirho {
pub parallel_chirho: bool,
pub num_threads_chirho: usize,
}
pub struct SwMgrChirho {
mod_paths_chirho: Vec<PathBuf>,
modules_chirho: HashMap<String, ModuleConfigChirho>,
config_chirho: SwMgrConfigChirho,
}
impl SwMgrChirho {
pub fn new_chirho() -> Self {
Self {
mod_paths_chirho: Vec::new(),
modules_chirho: HashMap::new(),
config_chirho: SwMgrConfigChirho::default(),
}
}
pub fn with_config_chirho(config_chirho: SwMgrConfigChirho) -> Self {
Self {
mod_paths_chirho: Vec::new(),
modules_chirho: HashMap::new(),
config_chirho,
}
}
pub fn with_system_paths_chirho() -> ResultChirho<Self> {
let mut mgr_chirho = Self::new_chirho();
mgr_chirho.add_system_paths_chirho();
mgr_chirho.load_modules_chirho()?;
Ok(mgr_chirho)
}
pub fn with_system_paths_and_config_chirho(config_chirho: SwMgrConfigChirho) -> ResultChirho<Self> {
let mut mgr_chirho = Self::with_config_chirho(config_chirho);
mgr_chirho.add_system_paths_chirho();
mgr_chirho.load_modules_chirho()?;
Ok(mgr_chirho)
}
pub fn add_system_paths_chirho(&mut self) {
let paths_chirho = vec![
PathBuf::from("/usr/share/sword"),
PathBuf::from("/usr/local/share/sword"),
dirs::home_dir()
.map(|h_chirho| h_chirho.join(".sword"))
.unwrap_or_default(),
PathBuf::from("/Applications/SWORD"),
PathBuf::from("./modules"),
];
for path_chirho in paths_chirho {
if path_chirho.exists() {
self.mod_paths_chirho.push(path_chirho);
}
}
}
pub fn add_path_chirho<P: AsRef<Path>>(&mut self, path_chirho: P) {
self.mod_paths_chirho.push(path_chirho.as_ref().to_path_buf());
}
pub fn get_config_chirho(&self) -> &SwMgrConfigChirho {
&self.config_chirho
}
pub fn set_parallel_chirho(&mut self, parallel_chirho: bool) {
self.config_chirho.parallel_chirho = parallel_chirho;
}
#[cfg(feature = "native")]
pub fn load_modules_chirho(&mut self) -> ResultChirho<()> {
self.modules_chirho.clear();
#[cfg(feature = "parallel")]
if self.config_chirho.parallel_chirho {
return self.load_modules_parallel_chirho();
}
for path_chirho in &self.mod_paths_chirho.clone() {
self.load_modules_from_path_chirho(path_chirho)?;
}
Ok(())
}
#[cfg(not(feature = "native"))]
pub fn load_modules_chirho(&mut self) -> ResultChirho<()> {
Ok(())
}
pub fn register_module_chirho(&mut self, config_chirho: ModuleConfigChirho) {
self.modules_chirho.insert(config_chirho.name_chirho.clone(), config_chirho);
}
#[cfg(all(feature = "native", feature = "parallel"))]
fn load_modules_parallel_chirho(&mut self) -> ResultChirho<()> {
use rayon::prelude::*;
if self.config_chirho.num_threads_chirho > 0 {
rayon::ThreadPoolBuilder::new()
.num_threads(self.config_chirho.num_threads_chirho)
.build_global()
.ok(); }
let mut conf_paths_chirho: Vec<PathBuf> = Vec::new();
for path_chirho in &self.mod_paths_chirho {
let mods_d_chirho = path_chirho.join("mods.d");
if mods_d_chirho.exists() {
for entry_result_chirho in WalkDir::new(&mods_d_chirho)
.max_depth(1)
.into_iter()
.filter_map(|e_chirho| e_chirho.ok())
{
let conf_path_chirho = entry_result_chirho.path().to_path_buf();
if conf_path_chirho.extension().map(|e_chirho| e_chirho == "conf").unwrap_or(false) {
conf_paths_chirho.push(conf_path_chirho);
}
}
}
}
let modules_chirho: Arc<Mutex<HashMap<String, ModuleConfigChirho>>> =
Arc::new(Mutex::new(HashMap::new()));
conf_paths_chirho.par_iter().for_each(|conf_path_chirho| {
if let Ok(config_chirho) = ModuleConfigChirho::from_file_chirho(conf_path_chirho) {
if let Ok(mut map_chirho) = modules_chirho.lock() {
map_chirho.insert(config_chirho.name_chirho.clone(), config_chirho);
}
}
});
if let Ok(map_chirho) = Arc::try_unwrap(modules_chirho) {
self.modules_chirho = map_chirho.into_inner().unwrap_or_default();
}
Ok(())
}
#[cfg(feature = "native")]
fn load_modules_from_path_chirho(&mut self, path_chirho: &Path) -> ResultChirho<()> {
let mods_d_chirho = path_chirho.join("mods.d");
if mods_d_chirho.exists() {
for entry_result_chirho in WalkDir::new(&mods_d_chirho)
.max_depth(1)
.into_iter()
.filter_map(|e_chirho| e_chirho.ok())
{
let conf_path_chirho = entry_result_chirho.path();
if conf_path_chirho.extension().map(|e_chirho| e_chirho == "conf").unwrap_or(false) {
if let Ok(config_chirho) = ModuleConfigChirho::from_file_chirho(conf_path_chirho) {
self.modules_chirho.insert(config_chirho.name_chirho.clone(), config_chirho);
}
}
}
}
Ok(())
}
pub fn get_module_chirho(&self, name_chirho: &str) -> Option<&ModuleConfigChirho> {
self.modules_chirho.get(name_chirho)
}
pub fn get_module_names_chirho(&self) -> Vec<&str> {
self.modules_chirho.keys().map(|s_chirho| s_chirho.as_str()).collect()
}
pub fn get_modules_chirho(&self) -> &HashMap<String, ModuleConfigChirho> {
&self.modules_chirho
}
pub fn get_modules_by_type_chirho(&self, mod_type_chirho: &str) -> Vec<&ModuleConfigChirho> {
self.modules_chirho
.values()
.filter(|m_chirho| {
m_chirho.get_chirho("ModDrv")
.map(|d_chirho| d_chirho.contains(mod_type_chirho))
.unwrap_or(false)
})
.collect()
}
pub fn get_bibles_chirho(&self) -> Vec<&ModuleConfigChirho> {
self.modules_chirho
.values()
.filter(|m_chirho| m_chirho.is_bible_chirho())
.collect()
}
pub fn get_commentaries_chirho(&self) -> Vec<&ModuleConfigChirho> {
self.modules_chirho
.values()
.filter(|m_chirho| m_chirho.is_commentary_chirho())
.collect()
}
pub fn get_lexicons_chirho(&self) -> Vec<&ModuleConfigChirho> {
self.modules_chirho
.values()
.filter(|m_chirho| m_chirho.is_lexicon_chirho())
.collect()
}
pub fn get_genbooks_chirho(&self) -> Vec<&ModuleConfigChirho> {
self.modules_chirho
.values()
.filter(|m_chirho| m_chirho.is_genbook_chirho())
.collect()
}
pub fn module_count_chirho(&self) -> usize {
self.modules_chirho.len()
}
pub fn has_module_chirho(&self, name_chirho: &str) -> bool {
self.modules_chirho.contains_key(name_chirho)
}
pub fn get_module_data_path_chirho(&self, name_chirho: &str) -> Option<PathBuf> {
let config_chirho = self.modules_chirho.get(name_chirho)?;
let data_path_chirho = config_chirho.data_path_chirho()?;
let clean_data_path_chirho = data_path_chirho.strip_prefix("./").unwrap_or(data_path_chirho);
for base_path_chirho in &self.mod_paths_chirho {
let full_path_chirho = base_path_chirho.join(clean_data_path_chirho);
if full_path_chirho.exists() {
return Some(full_path_chirho);
}
}
None
}
pub fn get_module_base_path_chirho(&self, name_chirho: &str) -> Option<PathBuf> {
let config_chirho = self.modules_chirho.get(name_chirho)?;
let data_path_chirho = config_chirho.data_path_chirho()?;
let clean_data_path_chirho = data_path_chirho.strip_prefix("./").unwrap_or(data_path_chirho);
let is_genbook_chirho = config_chirho.is_genbook_chirho();
for base_path_chirho in &self.mod_paths_chirho {
let full_path_chirho = base_path_chirho.join(clean_data_path_chirho);
let path_to_check_chirho = if is_genbook_chirho {
full_path_chirho.parent().map(|p_chirho| p_chirho.to_path_buf())
} else {
Some(full_path_chirho)
};
if let Some(check_path_chirho) = path_to_check_chirho {
if check_path_chirho.exists() {
return Some(base_path_chirho.clone());
}
}
}
None
}
pub fn load_module_chirho(&self, name_chirho: &str) -> ResultChirho<LoadedModuleChirho> {
let config_chirho = self.modules_chirho.get(name_chirho)
.ok_or_else(|| ErrorChirho::ModuleNotFoundChirho {
module_name_chirho: name_chirho.to_string(),
})?;
let base_path_chirho = self.get_module_base_path_chirho(name_chirho)
.ok_or_else(|| ErrorChirho::ModuleNotFoundChirho {
module_name_chirho: name_chirho.to_string(),
})?;
load_module_chirho(&base_path_chirho, config_chirho)
}
}
impl Default for SwMgrChirho {
fn default() -> Self {
Self::new_chirho()
}
}
mod dirs {
use std::path::PathBuf;
pub fn home_dir() -> Option<PathBuf> {
std::env::var_os("HOME").map(PathBuf::from)
}
}
#[cfg(test)]
mod tests_chirho {
use super::*;
#[test]
fn test_new_manager_chirho() {
let mgr_chirho = SwMgrChirho::new_chirho();
assert_eq!(mgr_chirho.module_count_chirho(), 0);
}
#[test]
fn test_add_path_chirho() {
let mut mgr_chirho = SwMgrChirho::new_chirho();
mgr_chirho.add_path_chirho("/some/path");
assert!(!mgr_chirho.mod_paths_chirho.is_empty());
}
#[test]
fn test_config_default_chirho() {
let config_chirho = SwMgrConfigChirho::default();
assert_eq!(config_chirho.num_threads_chirho, 0);
}
#[test]
fn test_with_config_chirho() {
let config_chirho = SwMgrConfigChirho {
parallel_chirho: true,
num_threads_chirho: 4,
};
let mgr_chirho = SwMgrChirho::with_config_chirho(config_chirho);
assert!(mgr_chirho.config_chirho.parallel_chirho);
assert_eq!(mgr_chirho.config_chirho.num_threads_chirho, 4);
}
#[test]
fn test_set_parallel_chirho() {
let mut mgr_chirho = SwMgrChirho::new_chirho();
mgr_chirho.set_parallel_chirho(true);
assert!(mgr_chirho.config_chirho.parallel_chirho);
}
}