use std::prelude::v1::*;
use serde::{ Deserialize, Serialize };
use std::fmt::{ Debug, Formatter };
use std::collections::BTreeMap;
use std::sync::Arc;
use matchit::{ Router };
use parking_lot::Mutex;
use crate::RegistryFeature;
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Features {
feature_map: BTreeMap<String, crate::RegistryFeature>,
#[serde(skip)]
feature_router: Router<String>,
feature_fallback: Option<String>
}
impl Features {
pub fn new() -> Self {
Self {
feature_map: BTreeMap::new(),
feature_router: Router::new(),
feature_fallback: None
}
}
pub async fn add(&mut self, feature_arc: Arc<dyn crate::Feature>, base_path: String, router: &mut product_os_router::ProductOSRouter) {
let registry_feature = feature_arc.register(feature_arc.clone(), base_path, router).await;
let identifier = registry_feature.identifier.clone();
self.feature_map.insert(identifier.clone(), registry_feature);
match feature_arc.init_feature().await {
Ok(_) => {}
Err(_) => {}
}
self.setup_router();
}
pub async fn add_mut(&mut self, feature_arc_mut: Arc<Mutex<dyn crate::Feature>>, base_path: String, router: &mut product_os_router::ProductOSRouter) {
let feature_locked = feature_arc_mut.try_lock_for(core::time::Duration::new(10, 0));
match feature_locked {
Some(mut feature_mut) => {
let registry_feature = feature_mut.register_mut(feature_arc_mut.clone(), base_path, router).await;
let identifier = registry_feature.identifier.clone();
self.feature_map.insert(identifier.clone(), registry_feature);
match feature_mut.init_feature_mut().await {
Ok(_) => {}
Err(_) => {}
}
self.setup_router();
}
None => panic!("Failed to lock mut feature")
}
}
pub fn remove(&mut self, identifier: String) {
self.feature_map.remove(identifier.as_str());
self.setup_router();
}
pub fn get(&self, identifier: &str) -> Option<&RegistryFeature> {
self.feature_map.get(identifier)
}
pub fn find(&self, path: &str) -> Option<(&RegistryFeature, BTreeMap<String, String>)> {
match self.feature_router.at(path) {
Ok(result) => {
match self.get(result.value.as_str()) {
None => None,
Some(registry_feature) => {
let mut parameters = BTreeMap::new();
for (key, value) in result.params.iter() {
parameters.insert(key.to_string(), value.to_string());
}
Some((registry_feature, parameters))
}
}
},
Err(_) => {
match &self.feature_fallback {
None => None,
Some(id) => {
let mut parameters = BTreeMap::new();
parameters.insert("*path".to_string(), path.to_string());
Some((self.feature_map.get(id.as_str()).unwrap(), parameters))
}
}
}
}
}
pub fn setup_router(&mut self) {
self.feature_router = Router::new();
self.feature_fallback = None;
for (identifier, registry_feature) in self.feature_map.iter() {
for path in ®istry_feature.paths {
if path.starts_with("/*") {
if self.feature_fallback == None {
self.feature_fallback = Some(identifier.to_owned())
}
else {
panic!("Issue setting fallback router {} - fallback already defined", identifier)
}
}
else {
match self.feature_router.insert(path, identifier.clone()) {
Ok(_) => {}
Err(e) => panic!("Issue inserting path for identifier {}: {} - {}", identifier, path, e)
}
}
}
}
}
}
impl Debug for Features {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.feature_map)
}
}