use std::path::Path;
use super::{Config, PkgsParseError, VarMap};
use crate::config::{Package, PackageType};
impl Config {
pub fn get(&self, name: &str) -> Result<NamedPackage, PkgsParseError> {
NamedPackage::try_new(
name,
self.packages[name].clone(),
VarMap::try_new(&self.vars)?, )
}
}
#[derive(Debug)]
pub struct NamedPackage {
name: String,
kind: PackageType,
maps: Vec<(String, String)>,
}
impl NamedPackage {
pub fn try_new(name: &str, package: Package, mut vars: VarMap) -> Result<Self, PkgsParseError> {
vars.extends(&package.vars)?;
let maps = package
.maps
.into_iter()
.map(|(k, v)| {
let mut v = vars.parse(&v)?;
let k_path = Path::new(&k);
if v.ends_with('/') {
v.push_str(
k_path
.file_name()
.ok_or_else(|| PkgsParseError::NoneFilename(k.clone()))?
.to_string_lossy()
.as_ref(),
);
}
Ok((k, v))
})
.collect::<Result<Vec<_>, PkgsParseError>>()?;
Ok(Self {
name: name.to_string(),
kind: package.kind,
maps,
})
}
pub fn get_directory(&self) -> String {
match self.kind() {
PackageType::Local => self.name.to_string(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> PackageType {
self.kind
}
pub fn maps(&self) -> &[(String, String)] {
&self.maps
}
#[cfg(test)]
pub fn insert_map(&mut self, key: impl AsRef<str>, value: impl AsRef<str>) {
self.maps
.push((key.as_ref().to_string(), value.as_ref().to_string()));
}
#[cfg(test)]
pub fn remove_map(&mut self, key: impl AsRef<str>) {
self.maps.retain(|(k, _)| k.as_str() != key.as_ref());
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use super::*;
use crate::{fs::home_dir, test_utils::prelude::*};
fn setup() -> Config {
let vars = vec![
("APP_DIR".to_string(), "${HOME}/myapp".to_string()),
("MY_VAR1".to_string(), "hello".to_string()),
("MY_VAR2".to_string(), "${MY_VAR1}_world".to_string()),
];
let packages = BTreeMap::from_iter([(
"test_pkg".to_string(),
Package {
kind: PackageType::Local,
vars: vec![],
maps: vec![
("app_dir".to_string(), "${APP_DIR}".to_string()),
("path".to_string(), "/usr/local/${MY_VAR2}".to_string()),
("config".to_string(), "${MY_VAR1}_config".to_string()),
],
},
)]);
Config { vars, packages }
}
#[gtest]
fn it_works() -> Result<()> {
let config = setup();
let pkg = config.get("test_pkg")?;
expect_eq!(pkg.name(), "test_pkg");
expect_eq!(pkg.kind(), PackageType::Local);
expect_eq!(pkg.get_directory(), "test_pkg");
expect_eq!(
*pkg.maps(),
[
(
"app_dir".into(),
home_dir().join("myapp").to_str().unwrap().into()
),
("path".into(), "/usr/local/hello_world".into()),
("config".into(), "hello_config".into())
]
);
Ok(())
}
#[gtest]
fn local_vars() -> Result<()> {
let mut config = setup();
config
.packages
.get_mut("test_pkg")
.unwrap()
.vars
.push(("MY_VAR1".to_string(), "hi".to_string()));
let pkg = config.get("test_pkg")?;
expect_eq!(
*pkg.maps(),
[
(
"app_dir".into(),
home_dir().join("myapp").to_str().unwrap().into()
),
("path".into(), "/usr/local/hello_world".into()),
("config".into(), "hi_config".into())
]
);
Ok(())
}
#[gtest]
fn unknown_var_when_build() -> Result<()> {
let mut config = setup();
config
.vars
.push(("MY_VAR3".to_string(), "${UNKNOWN}".to_string()));
let err = config.get("test_pkg").unwrap_err();
expect_that!(err, pat!(PkgsParseError::VarsBuild(_)));
Ok(())
}
#[gtest]
fn unknown_var_when_parse() -> Result<()> {
let mut config = setup();
config
.packages
.get_mut("test_pkg")
.unwrap()
.maps
.push(("bad".to_string(), "${UNKNOWN}".to_string()));
let err = config.get("test_pkg").unwrap_err();
expect_that!(err, pat!(PkgsParseError::VarsParse(_)));
Ok(())
}
mod trailing_slash {
use super::*;
fn setup(src: &str, dst: &str) -> Config {
let vars = vec![
("APP_DIR".to_string(), "${HOME}/myapp".to_string()),
("MY_VAR1".to_string(), "hello/".to_string()),
];
let packages = BTreeMap::from_iter([(
"test_pkg".to_string(),
Package {
kind: PackageType::Local,
vars: vec![],
maps: vec![(src.to_string(), dst.to_string())],
},
)]);
Config { vars, packages }
}
#[gtest]
fn common_trailing_slash() -> Result<()> {
let src = "path/to/trailing_slash";
let config = setup(src, "/usr/bin/");
let pkg = config.get("test_pkg")?;
expect_eq!(
*pkg.maps(),
[(src.into(), "/usr/bin/trailing_slash".into())]
);
Ok(())
}
#[gtest]
fn no_trailing_slash() -> Result<()> {
let src = "path/to/no_trailing_slash";
let config = setup(src, "/usr/local/bin");
let pkg = config.get("test_pkg")?;
expect_eq!(*pkg.maps(), [(src.into(), "/usr/local/bin".into())]);
Ok(())
}
#[gtest]
fn trailing_slash_in_var() -> Result<()> {
let src = "path/to/trailing_var";
let config = setup(src, "${MY_VAR1}");
let pkg = config.get("test_pkg")?;
expect_eq!(*pkg.maps(), [(src.into(), "hello/trailing_var".into())]);
Ok(())
}
#[gtest]
fn no_filename() -> Result<()> {
let src = "no_filename/..";
let config = setup(src, "/usr/bin/");
let err = config.get("test_pkg").unwrap_err();
expect_that!(err, pat!(PkgsParseError::NoneFilename(src)));
Ok(())
}
}
mod local_vars {}
}