chore: add app_tools
This commit is contained in:
		
							parent
							
								
									6bcb904c42
								
							
						
					
					
						commit
						c4723576b7
					
				
							
								
								
									
										10
									
								
								app_tools/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app_tools/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
/target
 | 
			
		||||
target
 | 
			
		||||
Cargo.lock
 | 
			
		||||
.cache
 | 
			
		||||
.temp
 | 
			
		||||
.env
 | 
			
		||||
*.log
 | 
			
		||||
.DS_Store
 | 
			
		||||
logs
 | 
			
		||||
tmp
 | 
			
		||||
							
								
								
									
										29
									
								
								app_tools/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								app_tools/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "app_tools"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
authors = ["JesusPerez <jpl@jesusperez.pro>"]
 | 
			
		||||
edition = "2018"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
anyhow = "1.0.40"
 | 
			
		||||
base64 = "0.13.0"
 | 
			
		||||
envmnt = "0.9.0"
 | 
			
		||||
rand = "0.8.3"
 | 
			
		||||
regex = "1.4.3"
 | 
			
		||||
reqwest = "0.11.3"
 | 
			
		||||
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"
 | 
			
		||||
slab = "0.4.3"
 | 
			
		||||
sthash = "0.2.9"
 | 
			
		||||
tempfile = "3.2.0"
 | 
			
		||||
tera = "1.8.0"
 | 
			
		||||
thiserror = "1.0.24"
 | 
			
		||||
toml = "0.5.8"
 | 
			
		||||
yaml-rust = "0.4"
 | 
			
		||||
uuid = { version = "0.8", features = ["serde", "v4"] }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										52
									
								
								app_tools/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								app_tools/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
# App Tools Utils library
 | 
			
		||||
 | 
			
		||||
Several functions to help in applications
 | 
			
		||||
 | 
			
		||||
Utility functions for variety of tasks 
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn run_command(cmd: &str, arg1: &str, arg2: &str) -> anyhow::Result<()> 
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn from_base64(source: &str) -> String 
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
FromStr for RGB 
 | 
			
		||||
 | 
			
		||||
Trim newline
 | 
			
		||||
this function is critical for base64 content as usually adds a newline at the end, causing decode not work properly with code.
 | 
			
		||||
**trim_newline** should be called to clean content before to be decoded.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn trim_newline(s: &mut String) 
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn hash_from_data(path: &str, ctx: &mut tera::Context, data_hash: &mut HashMap<String, String>, load_extend: bool) -> anyhow::Result<()>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**get_json_val** is a simple match Option to remove quotes in json values and returned as a String
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
get_json_val(json_value: &serde_json::Value, dflt_val: String) -> String
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**get_toml_val** is a simple match Option to remove quotes in json values and returned as a String
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
get_toml_val(toml_value: &toml::Value, dflt_val: String) -> String 
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**get_toml_val** is a simple match Option to remove quotes in json values and returned as a String
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
get_yaml_val(yaml_value: &serde_yaml::Value, dflt_val: String) -> String 
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Read a **file_name** from a path (root / file_name) ( "" file_name_full_path )
 | 
			
		||||
format of reading can be matched with `file_type`
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
read_path_file(path: &str, file_name: &str, file_type: &str) -> anyhow::Result<String> 
 | 
			
		||||
````
 | 
			
		||||
							
								
								
									
										9
									
								
								app_tools/TODO.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app_tools/TODO.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
### App Env library
 | 
			
		||||
 | 
			
		||||
- [ ] Implement tests for **config** 
 | 
			
		||||
 | 
			
		||||
- [ ] Extend **config** to support other **data stores**
 | 
			
		||||
 | 
			
		||||
- [ ] Add encryption to **config**
 | 
			
		||||
 | 
			
		||||
- [ ] Complete implementation for this types by moving some code here.
 | 
			
		||||
							
								
								
									
										273
									
								
								app_tools/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								app_tools/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,273 @@
 | 
			
		||||
/*! Utility functions for variety of tasks */
 | 
			
		||||
//
 | 
			
		||||
/*! Zterton */
 | 
			
		||||
// 
 | 
			
		||||
// Copyright 2020, Jesús Pérez Lorenzo
 | 
			
		||||
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::str::FromStr;
 | 
			
		||||
use std::fs;
 | 
			
		||||
use anyhow::anyhow;
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
use std::process::Command;
 | 
			
		||||
 | 
			
		||||
use serde::Deserialize;
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Default, Clone, Debug)]
 | 
			
		||||
struct Commit {
 | 
			
		||||
  hash: String,
 | 
			
		||||
  message: String,
 | 
			
		||||
}
 | 
			
		||||
/// A very simple Operating System Command runner capturing output
 | 
			
		||||
/// Allows two arguments, they can be empty
 | 
			
		||||
///```rust
 | 
			
		||||
/// let res =
 | 
			
		||||
/// match run_command("pwd", "", "") {
 | 
			
		||||
///   Ok(_) => format!("{} {}", "pwd","Done"),
 | 
			
		||||
///   Err(e) => format!("{}",e),
 | 
			
		||||
/// };
 | 
			
		||||
/// println!("Result: {}",res);
 | 
			
		||||
/// ```
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
#[allow(clippy::missing_errors_doc, clippy::filter_map)]
 | 
			
		||||
pub fn run_command(cmd: &str, arg1: &str, arg2: &str) -> anyhow::Result<()> {
 | 
			
		||||
  let output = match arg2 {
 | 
			
		||||
    "" => match arg1 {
 | 
			
		||||
      "" => Command::new(cmd).output()?,
 | 
			
		||||
      _ => Command::new(cmd).arg(arg1).output()?,
 | 
			
		||||
    },
 | 
			
		||||
    _ => Command::new(cmd).arg(arg1).arg(arg2).output()?,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if !output.status.success() {
 | 
			
		||||
    //dbg!(&output);
 | 
			
		||||
    let code = output.status.code().unwrap_or(-1);
 | 
			
		||||
    // let code = match output.status.code() {
 | 
			
		||||
    //   Some(val) => val,
 | 
			
		||||
    //   None => -1,
 | 
			
		||||
    // };
 | 
			
		||||
    let stderr = String::from_utf8(output.stderr)?;
 | 
			
		||||
    let msg = format!(
 | 
			
		||||
      "Command {} executed with failing errors({}): {}",
 | 
			
		||||
      &cmd, &code, &stderr
 | 
			
		||||
    );
 | 
			
		||||
    println!("{}", &msg);
 | 
			
		||||
    return Err(anyhow!(msg));
 | 
			
		||||
    // error_chain::bail!("Command {} executed with failing error code", &cmd);
 | 
			
		||||
  }
 | 
			
		||||
  let pattern = Regex::new(
 | 
			
		||||
    r"(?x)
 | 
			
		||||
                             ([0-9a-fA-F]+) # commit hash
 | 
			
		||||
                             (.*)           # The commit message",
 | 
			
		||||
  )?;
 | 
			
		||||
  String::from_utf8(output.stdout)?
 | 
			
		||||
    .lines()
 | 
			
		||||
    .filter_map(|line| pattern.captures(line))
 | 
			
		||||
    .map(|cap| Commit {
 | 
			
		||||
      hash: cap[1].to_string(),
 | 
			
		||||
      message: cap[2].trim().to_string(),
 | 
			
		||||
    })
 | 
			
		||||
    .take(5)
 | 
			
		||||
    .for_each(|x| println!("{:?}", x));
 | 
			
		||||
 | 
			
		||||
  Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[must_use]
 | 
			
		||||
pub fn from_base64(source: &str) -> String {
 | 
			
		||||
  match base64::decode(source) {
 | 
			
		||||
    Ok(res_deco) =>
 | 
			
		||||
      String::from_utf8(res_deco).unwrap_or_else(
 | 
			
		||||
        |e|{ println!("Error utf8 decode: {}", e); "".to_string() }
 | 
			
		||||
      ),
 | 
			
		||||
    Err(e) => {
 | 
			
		||||
      println!("Error 64 decode: {}", e);
 | 
			
		||||
      source.to_owned()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
 | 
			
		||||
pub struct RGB {
 | 
			
		||||
  pub r: u8,
 | 
			
		||||
  pub g: u8,
 | 
			
		||||
  pub b: u8,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FromStr for RGB {
 | 
			
		||||
  type Err = std::num::ParseIntError;
 | 
			
		||||
 | 
			
		||||
  // Parses a color hex code of the form '#rRgGbB..' into an
 | 
			
		||||
  // instance of 'RGB'
 | 
			
		||||
  fn from_str(hex_code: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
    // u8::from_str_radix(src: &str, radix: u32) converts a string
 | 
			
		||||
    // slice in a given base to u8
 | 
			
		||||
    let r: u8 = u8::from_str_radix(&hex_code[1..3], 16)?;
 | 
			
		||||
    let g: u8 = u8::from_str_radix(&hex_code[3..5], 16)?;
 | 
			
		||||
    let b: u8 = u8::from_str_radix(&hex_code[5..7], 16)?;
 | 
			
		||||
 | 
			
		||||
    Ok(RGB { r, g, b })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RGB {
 | 
			
		||||
#[must_use]
 | 
			
		||||
  pub fn as_u8(&self) -> [u8; 3] {
 | 
			
		||||
    [self.r,self.g,self.b]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
/// Trim newline
 | 
			
		||||
/// this function is critical for base64 content as usually adds a newline at the end, causing decode not work properly with code.
 | 
			
		||||
/// `trim_newline` should be called to clean content before to be decoded.
 | 
			
		||||
pub fn trim_newline(s: &mut String) {
 | 
			
		||||
  if s.ends_with('\n') {
 | 
			
		||||
    s.pop();
 | 
			
		||||
    if s.ends_with('\r') {
 | 
			
		||||
      s.pop();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 
 | 
			
		||||
#[allow(clippy::missing_errors_doc,clippy::implicit_hasher)]
 | 
			
		||||
pub fn hash_from_data(path: &str, ctx: &mut tera::Context, data_hash: &mut HashMap<String, String>, load_extend: bool) -> anyhow::Result<()> {
 | 
			
		||||
  // {HashMap<String, String>> {
 | 
			
		||||
  let mut check_value = | key: String, v: String | {
 | 
			
		||||
    if let Some(first_char) = v.chars().next() {
 | 
			
		||||
      if first_char == '@' {
 | 
			
		||||
        let field_file = &v[1..];
 | 
			
		||||
        let _ = hash_from_data(field_file, ctx, data_hash, load_extend);
 | 
			
		||||
        //  dbg!("{}",&data_hash);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    data_hash.insert(key, v);
 | 
			
		||||
  };
 | 
			
		||||
  let vars_content = fs::read_to_string(&path)?;
 | 
			
		||||
  //let mut data_hash: HashMap<String, String> = HashMap::new();
 | 
			
		||||
  if let Some(extension) = path.split('.').last() {
 | 
			
		||||
    //println!("File {} extension {}", &path, &extension);
 | 
			
		||||
    match extension {
 | 
			
		||||
      "json" => {
 | 
			
		||||
        let data: HashMap<String, serde_json::Value> = serde_json::from_str(&vars_content)?;
 | 
			
		||||
        if load_extend {
 | 
			
		||||
          match tera::Context::from_serialize(data) {
 | 
			
		||||
            Ok(res) => {
 | 
			
		||||
              ctx.extend(res); 
 | 
			
		||||
            },
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
              println!("Error loading {}: {}",&path, e);
 | 
			
		||||
            },
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          for (key, value) in data {
 | 
			
		||||
            let val = get_json_val(&value, "".into());
 | 
			
		||||
            check_value(key,val);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      "toml" => {
 | 
			
		||||
        // As a table content
 | 
			
		||||
        let data: toml::Value = toml::from_str(&vars_content)?;
 | 
			
		||||
        // let data = &vars_content.parse::<toml::Value>()?;
 | 
			
		||||
        if load_extend {
 | 
			
		||||
          match tera::Context::from_serialize(data) {
 | 
			
		||||
            Ok(res) => {
 | 
			
		||||
              ctx.extend(res); 
 | 
			
		||||
            },
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
              println!("Error loading {}: {}",&path, e);
 | 
			
		||||
            },
 | 
			
		||||
          }
 | 
			
		||||
        } else if let Some(table_data) = data.as_table() {
 | 
			
		||||
            for (key, value) in table_data {
 | 
			
		||||
              println!("-{}-{}",&key,&value);
 | 
			
		||||
              let val = get_toml_val(&value, "".into());
 | 
			
		||||
              check_value(key.into(),val);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      "yaml" => {
 | 
			
		||||
        let data: HashMap<String, serde_yaml::Value> = serde_yaml::from_str(&vars_content)?;
 | 
			
		||||
        if load_extend {
 | 
			
		||||
          match tera::Context::from_serialize(data) {
 | 
			
		||||
            Ok(res) => {
 | 
			
		||||
              ctx.extend(res); 
 | 
			
		||||
            },
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
              println!("Error loading {}: {}",&path, e);
 | 
			
		||||
            },
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          for (key, value) in data {
 | 
			
		||||
            let val = get_yaml_val(&value, "".into());
 | 
			
		||||
            check_value(key,val);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      _ => {
 | 
			
		||||
        println!(
 | 
			
		||||
          "File {} format extension {} not defined ",
 | 
			
		||||
          &path, &extension
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    println!("File {} unknown extension", &path);
 | 
			
		||||
  }
 | 
			
		||||
  // dbg!(&data_hash);
 | 
			
		||||
  Ok(()) // data_hash)
 | 
			
		||||
}
 | 
			
		||||
/// `get_json_val` is a simple match Option to remove quotes in json values and returned as a String
 | 
			
		||||
#[must_use]
 | 
			
		||||
pub fn get_json_val(json_value: &serde_json::Value, dflt_val: String) -> String {
 | 
			
		||||
  match json_value.as_str() {
 | 
			
		||||
    Some(val) => val.to_string(),
 | 
			
		||||
    None => dflt_val,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
/// `get_toml_val` is a simple match Option to remove quotes in json values and returned as a String
 | 
			
		||||
#[must_use]
 | 
			
		||||
pub fn get_toml_val(toml_value: &toml::Value, dflt_val: String) -> String {
 | 
			
		||||
  match toml_value.as_str() {
 | 
			
		||||
    Some(val) => val.to_string(),
 | 
			
		||||
    None => dflt_val,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
/// `get_toml_val` is a simple match Option to remove quotes in json values and returned as a String
 | 
			
		||||
#[must_use]
 | 
			
		||||
pub fn get_yaml_val(yaml_value: &serde_yaml::Value, dflt_val: String) -> String {
 | 
			
		||||
  match yaml_value.as_str() {
 | 
			
		||||
    Some(val) => val.to_string(),
 | 
			
		||||
    None => dflt_val,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
/// Read a `file_name` from a path (root / `file_name`) ( "" `file_name_full_path` )
 | 
			
		||||
/// format of reading can be matched with `file_type`
 | 
			
		||||
/// TODO implement other types to read
 | 
			
		||||
#[allow(clippy::missing_errors_doc)]
 | 
			
		||||
pub fn read_path_file(path: &str, file_name: &str, file_type: &str) -> anyhow::Result<String> {
 | 
			
		||||
  let mut result = String::from("");
 | 
			
		||||
  let mut root_path = format!("{}/", path);
 | 
			
		||||
  if root_path.as_str() == "/" {
 | 
			
		||||
    root_path = String::from("");
 | 
			
		||||
  }
 | 
			
		||||
  //println!("{}",format!("{}{}", &root_path.as_str(), &file_name));
 | 
			
		||||
  match file_type {
 | 
			
		||||
    "content" => {
 | 
			
		||||
      result = fs::read_to_string(format!("{}{}", &root_path.as_str(), &file_name).as_str())?
 | 
			
		||||
    }
 | 
			
		||||
    _ => println!(
 | 
			
		||||
      "File {}{} type {} not defined ",
 | 
			
		||||
      &root_path, &file_name, &file_type
 | 
			
		||||
    ),
 | 
			
		||||
  }
 | 
			
		||||
  Ok(result)
 | 
			
		||||
}
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn it_works() {
 | 
			
		||||
        assert_eq!(2 + 2, 4);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user