use std::path::{Path, PathBuf};
use async_trait::async_trait;
use orion_variate::vars::VarCollection;
use crate::{
addr::{AddrResult, Address, accessor::rename_path},
update::{DownloadOptions, UploadOptions},
};
use getset::{CloneGetters, CopyGetters, Getters, MutGetters, Setters, WithSetters};
#[derive(
Clone,
Debug,
Getters,
Setters,
WithSetters,
MutGetters,
CopyGetters,
CloneGetters,
Default,
PartialEq,
)]
pub struct UpdateUnit {
#[getset(get = "pub", set = "pub", get_mut, set_with)]
pub position: PathBuf,
pub vars: Option<VarCollection>,
}
impl UpdateUnit {
pub fn new(position: PathBuf, vars: VarCollection) -> Self {
Self {
position,
vars: Some(vars),
}
}
pub fn vars(&self) -> Option<&VarCollection> {
self.vars.as_ref()
}
}
impl From<PathBuf> for UpdateUnit {
fn from(value: PathBuf) -> Self {
Self {
vars: None,
position: value,
}
}
}
#[async_trait]
pub trait ResourceUploader {
async fn upload_from_local(
&self,
source: &Address,
dest: &Path,
options: &UploadOptions,
) -> AddrResult<UpdateUnit>;
}
#[async_trait]
pub trait ResourceDownloader {
async fn download_to_local(
&self,
source: &Address,
dest: &Path,
options: &DownloadOptions,
) -> AddrResult<UpdateUnit>;
async fn download_rename(
&self,
addr: &Address,
path: &Path,
name: &str,
options: &DownloadOptions,
) -> AddrResult<UpdateUnit> {
let mut target = self.download_to_local(addr, path, options).await?;
let path = rename_path(target.position(), name)?;
target.set_position(path);
Ok(target)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::addr::{AddrResult, Address};
use crate::update::{DownloadOptions, UploadOptions};
use async_trait::async_trait;
use std::path::PathBuf;
#[test]
fn test_update_unit_new() {
let position = PathBuf::from("/test/path");
let vars = VarCollection::default();
let unit = UpdateUnit::new(position.clone(), vars.clone());
assert_eq!(*unit.position(), position);
assert_eq!(unit.vars(), Some(&vars));
}
#[test]
fn test_update_unit_default() {
let unit = UpdateUnit::default();
assert_eq!(*unit.position(), PathBuf::new());
assert!(unit.vars().is_none());
}
#[test]
fn test_update_unit_from_pathbuf() {
let path = PathBuf::from("/test/path");
let unit: UpdateUnit = path.clone().into();
assert_eq!(*unit.position(), path);
assert!(unit.vars().is_none());
}
#[test]
fn test_update_unit_position_getter() {
let position = PathBuf::from("/test/path");
let unit = UpdateUnit::new(position.clone(), VarCollection::default());
assert_eq!(*unit.position(), position);
}
#[test]
fn test_update_unit_vars_getter() {
let vars = VarCollection::default();
let unit = UpdateUnit::new(PathBuf::from("/test/path"), vars.clone());
assert_eq!(unit.vars(), Some(&vars));
}
#[test]
fn test_update_unit_vars_getter_none() {
let unit: UpdateUnit = PathBuf::from("/test/path").into();
assert!(unit.vars().is_none());
}
#[test]
fn test_update_unit_set_position() {
let mut unit = UpdateUnit::new(PathBuf::from("/old/path"), VarCollection::default());
let new_position = PathBuf::from("/new/path");
unit.set_position(new_position.clone());
assert_eq!(*unit.position(), new_position);
}
#[test]
fn test_update_unit_with_position() {
let unit = UpdateUnit::default();
let new_position = PathBuf::from("/new/path");
let modified_unit = unit.with_position(new_position.clone());
assert_eq!(*modified_unit.position(), new_position);
}
#[test]
fn test_update_unit_position_mut() {
let mut unit = UpdateUnit::new(PathBuf::from("/old/path"), VarCollection::default());
unit.position_mut().push("subdir");
assert_eq!(*unit.position(), PathBuf::from("/old/path/subdir"));
}
#[test]
fn test_update_unit_clone() {
let original = UpdateUnit::new(PathBuf::from("/test/path"), VarCollection::default());
let cloned = original.clone();
assert_eq!(*original.position(), *cloned.position());
assert_eq!(original.vars(), cloned.vars());
}
#[test]
fn test_update_unit_partial_eq() {
let path = PathBuf::from("/test/path");
let vars = VarCollection::default();
let unit1 = UpdateUnit::new(path.clone(), vars.clone());
let unit2 = UpdateUnit::new(path.clone(), vars.clone());
let unit3 = UpdateUnit::new(PathBuf::from("/different/path"), vars.clone());
assert_eq!(unit1, unit2);
assert_ne!(unit1, unit3);
}
#[test]
fn test_update_unit_debug() {
let unit = UpdateUnit::new(PathBuf::from("/test/path"), VarCollection::default());
let debug_str = format!("{unit:?}");
assert!(debug_str.contains("UpdateUnit"));
assert!(debug_str.contains("/test/path"));
}
struct MockUploader;
#[async_trait]
impl ResourceUploader for MockUploader {
async fn upload_from_local(
&self,
_source: &Address,
dest: &Path,
_options: &UploadOptions,
) -> AddrResult<UpdateUnit> {
Ok(UpdateUnit::new(
dest.to_path_buf(),
VarCollection::default(),
))
}
}
#[tokio::test]
async fn test_resource_uploader_upload() {
let uploader = MockUploader;
let source = Address::Local(crate::addr::LocalPath::from("/local/source"));
let dest = Path::new("/remote/dest");
let options = UploadOptions::default();
let result = uploader.upload_from_local(&source, dest, &options).await;
assert!(result.is_ok());
let unit = result.unwrap();
assert_eq!(*unit.position(), PathBuf::from("/remote/dest"));
}
struct MockDownloader;
#[async_trait]
impl ResourceDownloader for MockDownloader {
async fn download_to_local(
&self,
_source: &Address,
dest: &Path,
_options: &DownloadOptions,
) -> AddrResult<UpdateUnit> {
Ok(UpdateUnit::new(
dest.to_path_buf(),
VarCollection::default(),
))
}
}
#[tokio::test]
async fn test_resource_downloader_download() {
let downloader = MockDownloader;
let source = Address::Local(crate::addr::LocalPath::from("/remote/source"));
let dest = Path::new("/local/dest");
let options = DownloadOptions::default();
let result = downloader.download_to_local(&source, dest, &options).await;
assert!(result.is_ok());
let unit = result.unwrap();
assert_eq!(*unit.position(), PathBuf::from("/local/dest"));
}
#[test]
fn test_update_unit_empty_path() {
let unit = UpdateUnit::new(PathBuf::new(), VarCollection::default());
assert!(unit.position().as_os_str().is_empty());
}
#[tokio::test]
async fn test_uploader_with_different_addresses() {
let uploader = MockUploader;
let options = UploadOptions::default();
let addresses = vec![
Address::Local(crate::addr::LocalPath::from("/local/path")),
Address::Http(crate::addr::HttpResource::from("https://example.com/file")),
];
for addr in addresses {
let dest = Path::new("/dest");
let result = uploader.upload_from_local(&addr, dest, &options).await;
assert!(result.is_ok());
}
}
#[tokio::test]
async fn test_downloader_with_different_addresses() {
let downloader = MockDownloader;
let options = DownloadOptions::default();
let addresses = vec![
Address::Local(crate::addr::LocalPath::from("/local/path")),
Address::Http(crate::addr::HttpResource::from("https://example.com/file")),
];
for addr in addresses {
let dest = Path::new("/dest");
let result = downloader.download_to_local(&addr, dest, &options).await;
assert!(result.is_ok());
}
}
#[test]
fn test_update_unit_clone_performance() {
let unit = UpdateUnit::new(PathBuf::from("/test/path"), VarCollection::default());
let start = std::time::Instant::now();
for _ in 0..1000 {
let _ = unit.clone();
}
let duration = start.elapsed();
assert!(duration.as_millis() < 100);
}
}