From af1af8dce1673b6f63efb9bb188ad1d23eb41da1 Mon Sep 17 00:00:00 2001 From: Marc Plano-Lesay Date: Sun, 17 Nov 2024 15:31:12 +0100 Subject: [PATCH] Add a libraries command and scan subcommand --- src/actions/fetch_all_libraries.rs | 31 +++++++++++ src/actions/mod.rs | 2 + src/actions/scan_library.rs | 27 ++++++++++ src/args.rs | 15 ++++++ src/commands/mod.rs | 1 + src/commands/scan_libraries.rs | 17 ++++++ src/main.rs | 6 ++- src/models/library.rs | 83 ++++++++++++++++++++++++++++++ src/models/mod.rs | 1 + 9 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 src/actions/fetch_all_libraries.rs create mode 100644 src/actions/scan_library.rs create mode 100644 src/commands/scan_libraries.rs create mode 100644 src/models/library.rs diff --git a/src/actions/fetch_all_libraries.rs b/src/actions/fetch_all_libraries.rs new file mode 100644 index 0000000..e26a56d --- /dev/null +++ b/src/actions/fetch_all_libraries.rs @@ -0,0 +1,31 @@ +use color_eyre::eyre::Result; + +use crate::{context::Context, models::library::Library}; + +use super::action::Action; + +pub struct FetchAllLibraries {} + +impl Action for FetchAllLibraries { + type Input = (); + type Output = Vec; + + fn new(_input: Self::Input) -> Self { + Self {} + } + + fn describe(&self) -> String { + String::from("Fetching all libraries") + } + + async fn execute(&self, ctx: &Context) -> Result { + Ok(ctx + .client + .get_all_libraries() + .await? + .clone() + .into_iter() + .map(Into::into) + .collect()) + } +} diff --git a/src/actions/mod.rs b/src/actions/mod.rs index 0c570d0..6389237 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -2,5 +2,7 @@ pub mod action; pub mod confirm; pub mod fetch_album_assets; pub mod fetch_all_albums; +pub mod fetch_all_libraries; pub mod fetch_all_persons; +pub mod scan_library; pub mod update_person_date_of_birth; diff --git a/src/actions/scan_library.rs b/src/actions/scan_library.rs new file mode 100644 index 0000000..692a95c --- /dev/null +++ b/src/actions/scan_library.rs @@ -0,0 +1,27 @@ +use color_eyre::eyre::Result; + +use crate::{context::Context, models::library::Library}; + +use super::action::Action; + +pub struct ScanLibrary { + library: Library, +} + +impl Action for ScanLibrary { + type Input = Library; + type Output = (); + + fn new(input: Self::Input) -> Self { + Self { library: input } + } + + fn describe(&self) -> String { + format!("Scanning library {}", self.library.name) + } + + async fn execute(&self, ctx: &Context) -> Result { + ctx.client.scan_library(&self.library.id).await?; + Ok(()) + } +} diff --git a/src/args.rs b/src/args.rs index 4cb8fe7..d6a2f35 100644 --- a/src/args.rs +++ b/src/args.rs @@ -44,6 +44,14 @@ pub(crate) enum Commands { assets_command: AssetsCommands, }, + /// Libraries related commands + #[serde(rename = "libaries")] + Libraries { + #[command(subcommand)] + #[serde(flatten)] + libraries_command: LibrariesCommands, + }, + /// People related commands #[serde(rename = "people")] People { @@ -78,6 +86,13 @@ pub(crate) enum AssetsCommands { }, } +#[derive(Serialize, Subcommand)] +pub(crate) enum LibrariesCommands { + /// Scan all libraries + #[serde(rename = "scan")] + Scan {}, +} + #[derive(Serialize, Subcommand)] pub(crate) enum PeopleCommands { /// Synchronises date of births from a vcard file diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 4b01565..5bc09d8 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,6 +1,7 @@ pub mod delete_assets; pub mod list_assets; pub mod missing_date_of_birth; +pub mod scan_libraries; pub mod server_features; pub mod server_version; pub mod sync_date_of_birth; diff --git a/src/commands/scan_libraries.rs b/src/commands/scan_libraries.rs new file mode 100644 index 0000000..d6e349b --- /dev/null +++ b/src/commands/scan_libraries.rs @@ -0,0 +1,17 @@ +use color_eyre::eyre::Result; +use log::*; + +use crate::actions::action::Action; +use crate::actions::scan_library::ScanLibrary; +use crate::{actions::fetch_all_libraries::FetchAllLibraries, context::Context}; + +pub async fn scan_libraries(ctx: Context) -> Result<()> { + let libraries = FetchAllLibraries::new(()).execute(&ctx).await?; + + for library in libraries { + debug!("Scanning {}", library.name); + ScanLibrary::new(library).execute(&ctx).await?; + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 8c22c03..2823308 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,13 +9,14 @@ mod tests { } use crate::args::Commands; -use args::{AssetsCommands, PeopleCommands, ServerCommands}; +use args::{AssetsCommands, LibrariesCommands, PeopleCommands, ServerCommands}; use clap::Parser; use color_eyre::eyre::{Result, WrapErr}; use color_eyre::Section; use commands::delete_assets::delete_assets; use commands::list_assets::list_assets; use commands::missing_date_of_birth::missing_date_of_birth; +use commands::scan_libraries::scan_libraries; use commands::server_features::server_features; use commands::server_version::server_version; use commands::sync_date_of_birth::sync_date_of_birth; @@ -79,6 +80,9 @@ async fn main() -> Result<()> { AssetsCommands::Delete { offline } => delete_assets(ctx, *offline).await, AssetsCommands::List { offline } => list_assets(ctx, *offline).await, }, + Commands::Libraries { libraries_command } => match libraries_command { + LibrariesCommands::Scan {} => scan_libraries(ctx).await, + }, Commands::People { people_command } => match people_command { PeopleCommands::MissingDateOfBirths {} => missing_date_of_birth(ctx).await, PeopleCommands::SyncDateOfBirths { vcard: _ } => { diff --git a/src/models/library.rs b/src/models/library.rs new file mode 100644 index 0000000..82b4439 --- /dev/null +++ b/src/models/library.rs @@ -0,0 +1,83 @@ +use chrono::{DateTime, Utc}; +use uuid::Uuid; + +use crate::types::LibraryResponseDto; + +#[derive(Debug, Clone)] +pub struct Library { + pub id: Uuid, + pub name: String, + pub asset_count: i64, + pub updated_at: DateTime, + pub refreshed_at: Option>, +} + +impl From for Library { + fn from(value: LibraryResponseDto) -> Self { + Self { + id: Uuid::parse_str(&value.id).expect("Unable to parse a library's UUID"), + name: value.name, + asset_count: value.asset_count, + updated_at: value.updated_at, + refreshed_at: value.refreshed_at, + } + } +} + +#[cfg(test)] +mod tests { + + use std::str::FromStr; + + use super::*; + + #[test] + fn from_library_response_dto_success() { + let dto = LibraryResponseDto { + id: String::from("123e4567-e89b-12d3-a456-426655440000"), + name: String::from("Christmas photos"), + asset_count: 1246, + exclusion_patterns: vec![], + import_paths: vec![], + owner_id: String::from("abc123"), + updated_at: DateTime::::from_str("2024-11-17T12:55:12Z").unwrap(), + created_at: DateTime::::from_str("2023-10-17T12:55:12Z").unwrap(), + refreshed_at: Some(DateTime::::from_str("2024-11-17T12:53:12Z").unwrap()), + }; + + let library = Library::from(dto); + + assert_eq!( + library.id, + Uuid::parse_str("123e4567-e89b-12d3-a456-426655440000").unwrap() + ); + assert_eq!(library.name, "Christmas photos".to_string()); + assert_eq!(library.asset_count, 1246); + assert_eq!( + library.updated_at, + DateTime::::from_str("2024-11-17T12:55:12Z").unwrap() + ); + assert_eq!( + library.refreshed_at, + Some(DateTime::::from_str("2024-11-17T12:53:12Z").unwrap()) + ); + } + + #[test] + #[should_panic] + fn from_library_response_dto_invalid_uuid_panics() { + let dto = LibraryResponseDto { + id: String::from("invalid_uuid"), + name: String::from("Christmas photos"), + asset_count: 1246, + exclusion_patterns: vec![], + import_paths: vec![], + owner_id: String::from("abc123"), + updated_at: DateTime::::from_str("2024-11-17T12:55:12Z").unwrap(), + created_at: DateTime::::from_str("2023-10-17T12:55:12Z").unwrap(), + refreshed_at: Some(DateTime::::from_str("2024-11-17T12:53:12Z").unwrap()), + }; + + let _ = Library::from(dto); + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 2bd864d..df967b2 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,4 @@ pub mod album; pub mod asset; +pub mod library; pub mod person; -- 2.50.1