From 5648bf1685e5f16264e721661bf323d9ad2d68a6 Mon Sep 17 00:00:00 2001 From: JesusPerez Date: Wed, 1 Sep 2021 17:16:19 +0100 Subject: [PATCH] chore: add kloud --- kloud/.gitignore | 10 ++ kloud/Cargo.toml | 30 ++++ kloud/README.md | 7 + kloud/TODO.md | 9 ++ kloud/src/datacontext.rs | 35 +++++ kloud/src/defs.rs | 115 ++++++++++++++++ kloud/src/lang.rs | 122 +++++++++++++++++ kloud/src/lib.rs | 286 +++++++++++++++++++++++++++++++++++++++ kloud/src/utils.rs | 38 ++++++ 9 files changed, 652 insertions(+) create mode 100644 kloud/.gitignore create mode 100644 kloud/Cargo.toml create mode 100644 kloud/README.md create mode 100644 kloud/TODO.md create mode 100644 kloud/src/datacontext.rs create mode 100644 kloud/src/defs.rs create mode 100644 kloud/src/lang.rs create mode 100644 kloud/src/lib.rs create mode 100644 kloud/src/utils.rs diff --git a/kloud/.gitignore b/kloud/.gitignore new file mode 100644 index 0000000..c3a29c2 --- /dev/null +++ b/kloud/.gitignore @@ -0,0 +1,10 @@ +/target +target +Cargo.lock +.cache +.temp +.env +*.log +.DS_Store +logs +tmp diff --git a/kloud/Cargo.toml b/kloud/Cargo.toml new file mode 100644 index 0000000..3670537 --- /dev/null +++ b/kloud/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "kloud" +version = "0.1.0" +authors = ["JesusPerez "] +edition = "2018" +publish = false + +[dependencies] +anyhow = "1.0.40" +async-graphql = "2.8.4" +# base64 = "0.13.0" +envmnt = "0.9.0" +# rand = "0.8.3" +parking_lot = "0.11.1" +json = "0.12.4" +serde = { version = "1.0", features = ["derive"] } +serde_derive = "1.0.125" +serde_json = "1.0.64" +serde_yaml = "0.8.17" +tempfile = "3.2.0" +toml = "0.5.8" +uuid = { version = "0.8", features = ["serde", "v4"] } + +app_tools = { version = "0.1.0", path = "../../utils/app_tools" } +app_env = { version = "0.1.0", path = "../app_env" } + +[dev-dependencies] +envmnt = "0.9.0" +pretty_assertions = "0.7.2" +# test-case = "1.1.0" diff --git a/kloud/README.md b/kloud/README.md new file mode 100644 index 0000000..7863fe6 --- /dev/null +++ b/kloud/README.md @@ -0,0 +1,7 @@ +# Kloud library + +This is multiple **types** collection library for **Kloud** + +- LangItems +- DataContext +- Kloud T with stores \ No newline at end of file diff --git a/kloud/TODO.md b/kloud/TODO.md new file mode 100644 index 0000000..16162d2 --- /dev/null +++ b/kloud/TODO.md @@ -0,0 +1,9 @@ +### Kloud library + +- [ ] Implement tests for **config** + +- [ ] Extend **config** to support other **data stores** + +- [ ] Add encryption to **config** + +- [ ] Complete implementation for types diff --git a/kloud/src/datacontext.rs b/kloud/src/datacontext.rs new file mode 100644 index 0000000..f8b2ffb --- /dev/null +++ b/kloud/src/datacontext.rs @@ -0,0 +1,35 @@ + +use serde::{Serialize,Deserialize}; +use async_graphql::{SimpleObject}; + +use crate::{Profile,Auth,Permissions}; + +#[derive(Clone, Debug, Serialize, Deserialize, Default, SimpleObject)] +pub struct DataContext { + pub profile: Profile, + pub auth: Auth, + pub perms: Permissions, +} + +pub fn set_data_ctx() -> DataContext { + let data_ctx_path = &envmnt::get_or("DATA_CONTEXT", "data/ctx.yaml"); + let fallback = || { + DataContext { + profile: Profile::Pro, + auth: Auth::NotSet, + perms: Permissions::Write + } + }; + let mut data_ctx: DataContext = fallback(); + if std::path::Path::new(&data_ctx_path).exists() { + data_ctx = match std::fs::read_to_string(&data_ctx_path) { + Ok(content) => + match serde_yaml::from_str(&content) { + Ok(res) => res, + Err(_) => fallback(), + }, + Err(_) => fallback(), + } + } + data_ctx +} \ No newline at end of file diff --git a/kloud/src/defs.rs b/kloud/src/defs.rs new file mode 100644 index 0000000..e24f6e3 --- /dev/null +++ b/kloud/src/defs.rs @@ -0,0 +1,115 @@ + +use std::collections::BTreeMap; +use std::sync::Arc; +use parking_lot::RwLock; +// use parking_lot::lock_api::RwLockReadGuard; + +use serde::Deserialize; + +// use app_env::appenv::AppEnv; +// use crate::utils::load_fs_content; +use crate::datacontext::{DataContext}; // , set_data_ctx}; + +// /// REQUEST +// #[derive(Clone, Debug, Serialize, Deserialize, Default)] // , SimpleObject)] +// pub struct Kloud { +// pub key: String, +// pub data: T, +// pub name: String, +// #[serde(default = "set_data_ctx")] +// pub ctx: DataContext, +// } + +// trait KloudStore { +// fn into(s: Self) -> T; +// } + +#[derive(Clone,Default)] +pub struct KloudStore { + pub entries: Arc>>, + pub coll_key: String, + pub data_ctx: DataContext, +} +// impl<'_, T: ?Sized + AsRef> From<&'_ T> for KloudStore { +// impl KloudStore { fn into(s: T) -> T { s.clone() } } + +impl KloudStore { + pub fn new(data: BTreeMap, coll_key: String, data_ctx: DataContext) -> Self { + println!("Loading {} {} items",&coll_key,data.len()); + Self { + entries: Arc::new(RwLock::new(data)), + data_ctx, + coll_key, + } + } + pub fn set_context(&self, ctx: DataContext) -> Self { + Self { + entries: self.entries.clone(), + data_ctx: ctx, + coll_key: self.coll_key.to_owned(), + } + } + // pub fn itms_from_data_file(&self, root: &str, target: &str, frmt: &str) { + // let content = load_fs_content(root, target, frmt); + // if content.is_empty() { return; } + // self.itms_from_content(content, frmt); + // } + + // pub async fn map_data(&self, root: &str, target: &str, frmt: &str) -> String { + // let items = self.load_fs_data(root, target, frmt); + // let mut new_items: BTreeMap = BTreeMap::new(); + // for (k, v) in items { + // new_items.insert(k.to_owned(), T { + // key: k.to_owned(), + // data: v.data, + // name: v.name, + // }); + // } + // match frmt { + // "yaml" => serde_yaml::to_string(&new_items).unwrap_or_else(|_| "".to_owned()), + // "json" => serde_json::to_string(&new_items).unwrap_or_else(|_| "".to_owned()), + // "toml" => toml::to_string(&new_items).unwrap_or_else(|_| "".to_owned()), + // _ => "".to_owned(), + // } + // } + // pub async fn get_data(&self, root: &str, target: &str, frmt: &str) -> BTreeMap { + // self.load_fs_data(root, target, frmt) + // } +} + +#[derive(Debug, Deserialize)] +pub struct KloudQueryFilters { + pub id: String, + pub sort: String, + pub start: i32, + pub end: i32, + // pub offset: Option, + // pub limit: Option, +} + +#[derive(Debug, Deserialize)] +pub struct KloudQueryDefsFilters { + pub id: Option, +} + + +#[derive(Debug, Deserialize)] +pub struct KloudQueryLangFilters { + pub lang: Option, + pub section: Option, +} + +#[derive(Debug, Deserialize)] +pub struct KloudReqData { + pub id: String, + pub str_data: String, +} + +#[derive(Debug, Deserialize)] +pub struct KloudQueryConfigFilters { + pub cld: String, + pub grp: String, + pub name: String, + pub tsksrvcs: String, + pub srvrs: String, +} \ No newline at end of file diff --git a/kloud/src/lang.rs b/kloud/src/lang.rs new file mode 100644 index 0000000..3129a0c --- /dev/null +++ b/kloud/src/lang.rs @@ -0,0 +1,122 @@ +use std::collections::HashMap; +use serde::{Serialize,Deserialize}; +//use async_graphql::{Context, Enum, Interface, SimpleObject, Object, Result}; +use async_graphql::{SimpleObject}; + +use crate::utils::load_file; + +#[derive(Clone, Debug, Serialize, Deserialize, Default, SimpleObject)] +pub struct LangItems { + pub data: HashMap, + pub main: HashMap, + pub forms: HashMap, +} + +impl LangItems { + pub fn new(root: &str, target: &str, frmt: &str) -> Self { + if target.is_empty() { + return LangItems::default(); + } + let lang_path=format!("{}/{}.{}",&root,&target,&frmt); + let content = load_file(&lang_path).unwrap_or_else(|e|{ + eprintln!("{}",e); + String::from("") + }); + let lang: LangItems = match frmt { + "yaml" => serde_yaml::from_str(&content) + .unwrap_or_else(|e|{ + eprintln!("{} Failed parse {} content from {}",e,&frmt,&lang_path); + LangItems::default() + }), + "json" => serde_json::from_str(&content) + .unwrap_or_else(|e|{ + eprintln!("{} Failed parse {} content from {}",e,&frmt,&lang_path); + LangItems::default() + }), + "toml" => toml::from_str(&content) + .unwrap_or_else(|e|{ + eprintln!("{} Failed parse {} content from {}",e,&frmt,&lang_path); + LangItems::default() + }), + _ => LangItems::default(), + }; + // dbg!(&lang); + println!("Lang loaded from {}", &lang_path); + lang + } + pub fn lng_t (&self, ky: &str, ctx: &str) -> String { + let mut txt = String::from(ky); + if ky.is_empty() { + return txt; + } + let op_value = match ctx { + "main" => self.main.get(ky), + "data" => self.data.get(ky), + "forms" => self.forms.get(ky), + _ => return txt, + }; + if let Some(item) = op_value { + txt = String::from(item); + } + txt + } + pub fn get_items_section(&self, section: &str) -> HashMap { + match section { + "main" => self.main.to_owned(), + "data" => self.data.to_owned(), + "forms" => self.forms.to_owned(), + _ => HashMap::new(), + } + } + pub fn get_items_str(&self, section: &str) -> String { + match section { + "main" => serde_json::to_string(&self.main), + "data" => serde_json::to_string(&self.data), + "forms" => serde_json::to_string(&self.forms), + _ => serde_json::to_string(&self), + }.unwrap_or_else(|_|String::from("")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::{Result}; + + pub fn load_lang_content() -> LangItems { + let data_path = &envmnt::get_or("DATA_LANG_PATH", "."); + let data_target = &envmnt::get_or("DATA_LANG_TARGET", "es"); + let data_frmt = &envmnt::get_or("DATA_LANG_FORMAT", "yaml"); + let data_section = &envmnt::get_or("DATA_LANG_SECTION", ""); + println!("Data path: {}/{} in {} format.", &data_path, &data_target, &data_frmt); + println!("Section {} ", &data_section); + let lang_items = LangItems::new(&data_path,&data_target,&data_frmt); + if data_section.is_empty() { + println!("Loaded items: main ({}) data ({}) forms ({})", + &lang_items.main.len(), &lang_items.data.len(), &lang_items.forms.len()); + } else { + println!("Loaded {} items", &lang_items.get_items_section(&data_section).len()); + } + lang_items + } + + #[test] + /// Use environment vars: DATA_LANG_PATH DATA_LANG_TARGET DATA_LANG_FORMAT DATA_LANG_OUT_FORMAT DATA_LANG_SECTION + /// lang_test.sh Kloud/ta/langs es.yaml yaml toml main + fn load_lang() -> Result<()> { + let lang_items: LangItems = load_lang_content(); + assert_ne!(lang_items.main.len(), 0); + assert_ne!(lang_items.data.len(), 0); + assert_ne!(lang_items.forms.len(), 0); + + let out_format = &envmnt::get_or("DATA_LANG_OUT_FORMAT", ""); + let data_section = &envmnt::get_or("DATA_LANG_SECTION", ""); + if ! out_format.is_empty() { + println!("Out to {}: ", &out_format); + let str_data = lang_items.get_items_str(&data_section); + println!("{}",&str_data); + assert_ne!(str_data.is_empty(),true) + } + Ok(()) + } +} \ No newline at end of file diff --git a/kloud/src/lib.rs b/kloud/src/lib.rs new file mode 100644 index 0000000..a758c91 --- /dev/null +++ b/kloud/src/lib.rs @@ -0,0 +1,286 @@ +// +/*! Zterton +*/ +// Copyright 2021, Jesús Pérez Lorenzo +// +// use std::collections::HashMap; +pub mod datacontext; +pub mod defs; +pub mod utils; +pub mod lang; + +use std::fmt; +use serde::{Serialize,Deserialize}; +//use async_graphql::{Context, Enum, Interface, SimpleObject, Object, Result}; +use async_graphql::{Enum, SimpleObject}; +/* +#[derive(Eq, PartialEq, Copy, Clone,Debug, Serialize, Deserialize)] +pub enum Lang { + EN, + ES, + FR, +} +impl Default for Lang { + fn default() -> Self { + Lang::EN + } +} +*/ + +#[derive(Eq, PartialEq, Copy, Clone,Debug, Serialize, Deserialize, Enum)] +pub enum KeyType { + Primary, + Foreign, + Secundary, + Multiple, + NotSet, +} +impl Default for KeyType { + fn default() -> Self { + KeyType::Primary + } +} +impl fmt::Display for KeyType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + KeyType::Primary => write!(f, "{}", "primary"), + KeyType::Foreign => write!(f, "{}", "foreign"), + KeyType::Secundary => write!(f, "{}", "secundary"), + KeyType::Multiple => write!(f, "{}", "multiple"), + KeyType::NotSet => write!(f, "{}", "notset"), + } + } +} +impl KeyType { + #[must_use] + pub fn get_from(val: &str) -> KeyType { + match val { + "primary" => KeyType::Primary, + "foreign" => KeyType::Foreign, + "secundary" => KeyType::Secundary, + "multiple" => KeyType::Multiple, + _ => KeyType::NotSet, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Default, SimpleObject)] +pub struct KeyDef{ + name: String, + keytype: KeyType, + coll: String, + collkey: String, + priority: u16, +} + +#[derive(Eq, PartialEq, Copy, Clone,Debug, Serialize, Deserialize, Enum)] +pub enum OutFormat { + Html, + Json, + Yaml, + NotSet, +} +impl Default for OutFormat { + fn default() -> Self { + OutFormat::Json + } +} + +impl fmt::Display for OutFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + OutFormat::Html => write!(f, "{}", "html"), + OutFormat::Yaml => write!(f, "{}", "yaml"), + OutFormat::Json => write!(f, "{}", "json"), + OutFormat::NotSet => write!(f, "{}", "notset"), + } + } +} +impl OutFormat { + #[must_use] + pub fn get_from(val: &str) -> OutFormat { + match val { + "html" => OutFormat::Html, + "yaml" => OutFormat::Yaml, + "json" => OutFormat::Json, + _ => OutFormat::NotSet, + } + } +} +#[allow(non_camel_case_types)] +#[derive(Eq, PartialEq, Copy, Clone,Debug, Serialize, Deserialize, Enum)] +pub enum RunEnv { + Production, + Development, + Test, + NotSet, +} +impl Default for RunEnv { + fn default() -> Self { + RunEnv::Production + } +} +impl fmt::Display for RunEnv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RunEnv::Production => write!(f, "{}", "production"), + RunEnv::Development => write!(f, "{}", "development"), + RunEnv::Test => write!(f, "{}", "test"), + RunEnv::NotSet => write!(f, "{}", "notset"), + } + } +} +impl RunEnv { + #[must_use] + pub fn get_from(val: &str) -> RunEnv { + match val { + "production" => RunEnv::Production, + "development" => RunEnv::Development, + "test" => RunEnv::Test, + _ => RunEnv::NotSet, + } + } +} + +#[derive(Eq, PartialEq, Copy, Clone,Debug, Serialize, Deserialize, Enum)] +pub enum Platform { + Ui, + Backend, + NotSet, +} +impl Default for Platform { + fn default() -> Self { + Platform::Backend + } +} +impl fmt::Display for Platform { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Platform::Ui=> write!(f, "{}", "ui"), + Platform::Backend => write!(f, "{}", "backend"), + Platform::NotSet => write!(f, "{}", "notset"), + } + } +} +impl Platform { + #[must_use] + pub fn get_from(val: &str) -> Platform { + match val { + "ui" => Platform::Ui, + "backend" => Platform::Backend, + _ => Platform::NotSet, + } + } +} + +#[derive(Eq, PartialEq, Copy, Clone,Debug, Serialize, Deserialize, Enum)] +pub enum Profile { + Premium, + Basic, + Pro, + NotSet, +} +impl Default for Profile { + fn default() -> Self { + Profile::Basic + } +} +impl fmt::Display for Profile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Profile::Premium => write!(f, "{}", "premium"), + Profile::Basic => write!(f, "{}", "basic"), + Profile::Pro => write!(f, "{}", "pro"), + Profile::NotSet => write!(f, "{}", "notset"), + } + } +} +impl Profile { + #[must_use] + pub fn get_from(val: &str) -> Profile { + match val { + "basic" => Profile::Basic, + "pro" => Profile::Pro, + "premium" => Profile::Premium, + _ => Profile::NotSet, + } + } +} + +#[derive(Eq, PartialEq, Copy, Clone,Debug, Serialize, Deserialize, Enum)] +pub enum Permissions { + Read, + Write, + ReadWrite, + NotSet, +} +impl Default for Permissions { + fn default() -> Self { + Permissions::ReadWrite + } +} +impl fmt::Display for Permissions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Permissions::Read => write!(f, "{}", "read"), + Permissions::Write => write!(f, "{}", "write"), + Permissions::ReadWrite => write!(f, "{}", "readwrite"), + Permissions::NotSet => write!(f, "{}", "notset"), + } + } +} +impl Permissions { + #[must_use] + pub fn get_from(val: &str) -> Permissions { + match val { + "read" => Permissions::Read, + "write" => Permissions::Write, + "readwrite" => Permissions::ReadWrite, + _ => Permissions::NotSet, + } + } +} +#[derive(Eq, PartialEq, Copy, Clone,Debug, Serialize, Deserialize, Enum)] +pub enum Auth { + Allow, + Denied, + NotSet, +} +impl Default for Auth { + fn default() -> Self { + Auth::Allow + } +} + +// #[derive(Clone, Debug, Serialize, Deserialize)] +// pub struct LangItems { +// pub cell: Option, +// } +// impl Default for LangItems { +// fn default() -> Self { +// Self { +// cell: Some(String::from("cell")), +// } +// } +// } +#[derive(Clone, Debug, Serialize, Deserialize, Default,SimpleObject)] +pub struct RunContext { + pub run_env: RunEnv, + pub platform: Platform, +} + +pub fn set_data_uid() -> String { + uuid::Uuid::new_v4().to_string() +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} + +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; diff --git a/kloud/src/utils.rs b/kloud/src/utils.rs new file mode 100644 index 0000000..5bea1a5 --- /dev/null +++ b/kloud/src/utils.rs @@ -0,0 +1,38 @@ + +use anyhow::{anyhow,Result,Context}; +use std::fs; //, io}; +// use std::fs::OpenOptions; +// use std::io::Write; +use std::path::Path; + +use app_env::appenv::AppEnv; + +#[allow(clippy::missing_errors_doc)] +pub fn load_from_module(env: AppEnv, coll_key: &str) -> (String,String) { + if let Some(module) = env.modules.get(coll_key) { + match module.stores.to_owned().as_str() { + "file" => + (module.store_frmt.to_owned(), load_fs_content(&module.store_root, &module.store_path, &module.store_frmt)), + _ => { + println!("Stores {} load not defined", &module.stores); + (module.store_frmt.to_owned(), String::from("")) + }, + } + } else { + println!("Context key {} not found",coll_key); + (String::from(""), String::from("")) + } +} +#[allow(clippy::missing_errors_doc)] +pub fn load_file(file_path: &str ) -> Result { + if ! Path::new(&file_path).exists() { + return Err(anyhow!("File Path {} not found", &file_path)); + } + let content=fs::read_to_string(&file_path).with_context(|| format!("Failed to read 'file_path' from {}", &file_path))?; + Ok(content) +} +pub fn load_fs_content(root: &str, target: &str, frmt: &str) -> String { + // let home=get_env_path("Kloud_HOME","home", "", &path,false).await?; + let data_path=format!("{}/{}.{}",&root,&target,&frmt); + load_file(&data_path).unwrap_or_else(|e| { println!("{}",e); "".to_owned()}) +}