use crate::error::*;
use crate::package_definition;
use crate::paths;
use crate::platform;
use std::fs;
use std::path::Path;
pub fn put(
home: &Path,
name: &str,
version: &str,
content_dir: &Path,
) -> Result<()> {
let def_path = content_dir.join("package.yaml");
if !def_path.exists() {
bail!("content directory does not contain package.yaml");
}
let dir = paths::store_path(home, name, version);
fs::create_dir_all(&dir).context("failed to create store directory")?;
copy_dir_contents(content_dir, &dir)?;
if let Ok(def) = package_definition::parse_file(&def_path) {
if let Some(ref binary_name) = def.binary {
let bin_filename = format!("{}{}", binary_name, platform::exe_suffix());
let bin_dest = dir.join(&bin_filename);
if bin_dest.exists() {
set_executable(&bin_dest)?;
}
}
}
Ok(())
}
fn copy_dir_contents(src: &Path, dst: &Path) -> Result<()> {
for entry in fs::read_dir(src).context("failed to read content directory")? {
let entry = entry?;
let file_type = entry.file_type()?;
let dest = dst.join(entry.file_name());
if file_type.is_dir() {
fs::create_dir_all(&dest)?;
copy_dir_contents(&entry.path(), &dest)?;
} else {
fs::copy(entry.path(), &dest).context("failed to copy file to store")?;
}
}
Ok(())
}
pub fn remove_version(home: &Path, name: &str, version: &str) -> Result<()> {
let dir = paths::store_path(home, name, version);
if dir.exists() {
fs::remove_dir_all(&dir).context("failed to remove version directory")?;
}
Ok(())
}
pub fn remove(home: &Path, name: &str) -> Result<()> {
let store = paths::store_dir(home).join(name);
if store.exists() {
fs::remove_dir_all(&store).context("failed to remove store directory")?;
}
Ok(())
}
#[cfg(unix)]
fn set_executable(path: &Path) -> Result<()> {
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(path)?.permissions();
perms.set_mode(perms.mode() | 0o111);
fs::set_permissions(path, perms)?;
Ok(())
}
#[cfg(windows)]
fn set_executable(_path: &Path) -> Result<()> {
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util;
fn create_content_dir(yaml: &str) -> tempfile::TempDir {
let dir = tempfile::tempdir().unwrap();
fs::write(dir.path().join("package.yaml"), yaml).unwrap();
dir
}
#[test]
fn test_put_binary_package() {
let home = test_util::temp_home("store");
let content = create_content_dir(
"name: testmod\nversion: \"1.0.0\"\nbinary: testmod\ncommands:\n default:\n description: test\n",
);
test_util::create_dummy_binary(
content.path(),
&format!("testmod{}", platform::exe_suffix()),
);
put(home.path(), "testmod", "1.0.0", content.path()).unwrap();
assert!(paths::definition_path(home.path(), "testmod", "1.0.0").exists());
assert!(paths::store_binary_path(home.path(), "testmod", "1.0.0", "testmod").exists());
}
#[test]
fn test_put_definition_only() {
let home = test_util::temp_home("store");
let content = create_content_dir(
"name: my-wrapper\nversion: \"1.0.0\"\ncommands:\n default:\n description: test\n",
);
put(home.path(), "my-wrapper", "1.0.0", content.path()).unwrap();
assert!(paths::definition_path(home.path(), "my-wrapper", "1.0.0").exists());
}
#[test]
fn test_put_missing_package_yaml() {
let home = test_util::temp_home("store");
let content = tempfile::tempdir().unwrap();
let result = put(home.path(), "test", "1.0.0", content.path());
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("package.yaml"));
}
#[test]
fn test_put_multi_file_package() {
let home = test_util::temp_home("store");
let content = create_content_dir(
"name: py-tool\nversion: \"1.0.0\"\ncommands:\n default:\n description: test\n",
);
fs::write(content.path().join("main.py"), "print('hello')").unwrap();
fs::create_dir_all(content.path().join("lib")).unwrap();
fs::write(content.path().join("lib").join("utils.py"), "# utils").unwrap();
put(home.path(), "py-tool", "1.0.0", content.path()).unwrap();
let store = paths::store_path(home.path(), "py-tool", "1.0.0");
assert!(store.join("package.yaml").exists());
assert!(store.join("main.py").exists());
assert!(store.join("lib").join("utils.py").exists());
}
#[test]
fn test_remove_version() {
let home = test_util::temp_home("store");
let content = create_content_dir(
"name: testmod\nversion: \"1.0.0\"\ncommands:\n default:\n description: test\n",
);
put(home.path(), "testmod", "1.0.0", content.path()).unwrap();
put(home.path(), "testmod", "2.0.0", content.path()).unwrap();
assert!(paths::store_path(home.path(), "testmod", "1.0.0").exists());
assert!(paths::store_path(home.path(), "testmod", "2.0.0").exists());
remove_version(home.path(), "testmod", "1.0.0").unwrap();
assert!(!paths::store_path(home.path(), "testmod", "1.0.0").exists());
assert!(paths::store_path(home.path(), "testmod", "2.0.0").exists());
}
#[test]
fn test_remove_all() {
let home = test_util::temp_home("store");
let content = create_content_dir(
"name: testmod\nversion: \"1.0.0\"\ncommands:\n default:\n description: test\n",
);
put(home.path(), "testmod", "1.0.0", content.path()).unwrap();
put(home.path(), "testmod", "2.0.0", content.path()).unwrap();
remove(home.path(), "testmod").unwrap();
assert!(!paths::store_dir(home.path()).join("testmod").exists());
}
}