The real language, doing the real work
Lets get rusty
The ascii diagram again.
v <-- you are here (still)
(but now you are a rustacean)
+----------+ +----------+ +----------+ +----------+
| cli opts | -> | project | -+-> | print | -> | display |
+----------+ | config | | +----------+ +----------+
+----------+ |
| +----------+ +----------+
+-> | add | -> | save |
| +----------+ +----------+
|
| +----------+ +----------+
+-> | rm | -> | save |
+----------+ +----------+
So lets create the file (src/projector.rs)
> src/projector.rs
RUST IT UP
Here is what the code could look like
use std::{collections::HashMap, path::PathBuf};
use serde::{Deserialize, Serialize};
use crate::config::ProjectorConfig;
#[derive(Debug, Default, Deserialize, Serialize)]
struct ProjectorData {
projector: HashMap<PathBuf, HashMap<String, String>>
}
pub struct Projector {
config: ProjectorConfig,
data: ProjectorData,
}
fn default_projector(config: ProjectorConfig) -> Projector {
let data = ProjectorData::default();
return Projector {
config,
data,
}
}
impl From<ProjectorConfig> for Projector {
fn from(config: ProjectorConfig) -> Self {
if std::fs::metadata(&config.config).is_err() {
return default_projector(config);
}
if let Ok(data) = std::fs::read_to_string(&config.config) {
let data = serde_json::from_str(&data);
if let Ok(data) = data {
return Projector {
config,
data,
}
}
}
return default_projector(config);
}
}
impl Projector {
pub fn get_value(&self, key: &str) -> Option<&String> {
let mut out = None;
let mut curr = Some(self.config.pwd.as_path());
while let Some(p) = curr {
if let Some(dir) = self.data.projector.get(p) {
let value = dir.get(key);
if value.is_some() {
out = value;
break;
}
}
curr = p.parent();
}
return out;
}
pub fn set_value(&mut self, key: &str, value: String) {
self.data.projector
.entry(self.config.pwd.clone())
.or_insert_with(|| HashMap::new())
.insert(key.to_string(), value);
}
pub fn delete_value(&mut self, key: &str) {
self.data.projector
.entry(self.config.pwd.clone())
.or_insert_with(|| HashMap::new())
.remove(key);
}
}
Forgot the anniversary?
Testing Time
cargo add collection_macros
Your tests could look like
#[cfg(test)]
mod test {
use std::{path::PathBuf, collections::HashMap};
use collection_macros::hashmap;
use crate::config::{ProjectorConfig, Operation};
use super::{ProjectorData, Projector};
fn get_config(pwd: PathBuf) -> ProjectorConfig {
return ProjectorConfig {
config: PathBuf::from("/foo"),
operation: Operation::Print(None),
pwd,
}
}
fn get_data() -> ProjectorData {
return ProjectorData {
projector: hashmap! {
PathBuf::from("/") => hashmap! {
"foo".into() => "bar1".into(),
"bar".into() => "bazz".into(),
},
PathBuf::from("/foo") => hashmap! {
"foo".into() => "bar2".into()
},
PathBuf::from("/foo/bar") => hashmap! {
"foo".into() => "bar3".into()
},
PathBuf::from("/foo/bar/baz") => hashmap! {
"foo".into() => "bar3".into()
},
},
}
}
#[test]
fn get_value() {
let proj = Projector {
data: get_data(),
config: get_config(PathBuf::from("/foo/bar")),
};
assert_eq!(proj.get_value("foo"), Some(&String::from("bar3")));
assert_eq!(proj.get_value("bar"), Some(&String::from("bazz")));
assert_eq!(proj.get_value("notehu"), None);
}
#[test]
fn set_value() {
let mut proj = Projector {
data: get_data(),
config: get_config(PathBuf::from("/foo/bar")),
};
assert_eq!(proj.get_value("foo"), Some(&String::from("bar3")));
proj.set_value("foo", "hello, fem".into());
assert_eq!(proj.get_value("foo"), Some(&String::from("hello, fem")));
}
#[test]
fn delete_value() {
let mut proj = Projector {
data: get_data(),
config: get_config(PathBuf::from("/foo/bar")),
};
assert_eq!(proj.get_value("foo"), Some(&String::from("bar3")));
proj.delete_value("foo");
assert_eq!(proj.get_value("foo"), Some(&String::from("bar2")));
}
}