use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use anyhow::Result;
mod config;
mod copy_manager;
mod device;
mod error;
mod handler;
mod scst_tgt;
mod stat;
mod target;
pub use config::*;
pub use copy_manager::*;
pub use device::*;
pub use error::*;
pub use handler::*;
pub use scst_tgt::*;
pub use stat::*;
pub use target::*;
pub(crate) trait Layer {
fn root(&self) -> &Path;
fn load<P: AsRef<Path>>(&mut self, root: P) -> Result<()>;
fn mgmt<S: AsRef<OsStr>>(&mut self, root: S, cmd: S) -> Result<()> {
let mgmt = Path::new(root.as_ref()).join("mgmt");
echo(mgmt.as_ref(), cmd.as_ref())
}
}
#[derive(Debug, Default, Clone)]
pub struct Options {
inner: HashMap<String, String>,
}
impl Options {
pub fn new() -> Self {
Options {
inner: HashMap::new(),
}
}
pub fn insert<S: AsRef<str>>(&mut self, k: S, v: S) -> &Self {
self.inner
.insert(k.as_ref().to_string(), v.as_ref().to_string());
self
}
pub fn contains_keys<'a>(&self, keys: &'a [String]) -> Vec<&'a str> {
keys.iter()
.filter(|key| self.inner.contains_key(*key))
.filter_map(|key| Some(key.as_str()))
.collect::<Vec<&str>>()
}
pub fn different_set(&self, keys: &[String]) -> Vec<&str> {
self.inner
.keys()
.filter_map(|key| {
if !keys.contains(key) {
Some(key.as_str())
} else {
None
}
})
.collect::<Vec<&str>>()
}
pub fn pack(&self) -> Option<String> {
let slice = self
.inner
.iter()
.filter_map(|(key, value)| Some(key.to_owned() + "=" + value))
.collect::<Vec<String>>();
if slice.len() > 0 {
return Some(slice.join(";"));
}
None
}
pub fn pack_with_check(&self, keys: &[String]) -> Result<Option<String>> {
let sets = self
.inner
.keys()
.filter_map(|key| {
if !keys.contains(key) {
Some(key.as_str())
} else {
None
}
})
.collect::<Vec<&str>>();
if sets.len() > 0 {
return Err(anyhow::anyhow!("invalid paramsters [{}]", sets.join(",")));
}
Ok(self.pack())
}
}
pub(crate) fn read_fl<P: AsRef<Path>>(path: P) -> Result<String> {
let text = fs::read_to_string(path)?;
let value = text.split('\n').next().unwrap_or("0").to_string();
Ok(value)
}
pub(crate) fn read_dir<P: AsRef<Path>>(path: P) -> Result<fs::ReadDir> {
let read_dir = fs::read_dir(path).map_err(|e| ScstError::Io(e))?;
Ok(read_dir)
}
pub(crate) fn read_link<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
let buf = fs::read_link(path).map_err(|e| ScstError::Io(e))?;
Ok(buf)
}
pub(crate) fn echo<S: AsRef<OsStr>>(root: S, cmd: S) -> Result<()> {
let cmd_str = cmd.as_ref().to_string_lossy();
let mut fd = fs::File::create(Path::new(root.as_ref()))?;
fd.write(cmd_str.as_bytes()).map_err(|e| ScstError::Io(e))?;
Ok(())
}
pub(crate) fn cmd_with_options(
cmd: &str,
params: &Vec<String>,
options: &Options,
) -> Result<String> {
let cmd_string = cmd.to_string();
let out = options
.pack_with_check(¶ms)?
.and_then(|s| {
let mut c = cmd_string.clone();
c.push_str(" ");
c.push_str(&s);
Some(c)
})
.or(Some(cmd_string))
.unwrap();
Ok(out)
}
#[cfg(test)]
mod tests {
use std::io::Write;
use super::*;
fn foo_file(text: &str) -> Result<String> {
let path = std::env::temp_dir().join("foo.txt");
let mut file = fs::File::create(path.clone())?;
file.write_all(text.as_bytes())?;
Ok(path.to_string_lossy().to_string())
}
#[test]
fn test_read_fl() -> Result<()> {
let fl1 = foo_file("1")?;
assert_eq!(read_fl(fl1)?, "1");
let fl2 = foo_file("3.1\nDEBUG")?;
assert_eq!(read_fl(fl2)?, "3.1");
let fl3 = foo_file("open\n[key]")?;
assert_eq!(read_fl(fl3)?, "open");
Ok(())
}
#[test]
pub fn test_options() -> Result<()> {
let mut opt = Options::new();
let s = opt.pack();
assert_eq!(s, None);
let s = opt.pack_with_check(&Vec::new());
assert_eq!(s.unwrap(), None);
opt.insert("a", "b");
assert_eq!(opt.pack(), Some("a=b".to_string()));
opt.insert("a", "b");
assert!(opt.pack_with_check(&["c".to_string()]).is_err());
Ok(())
}
}