Add search/metadata API endpoint
- Add MetadataSearchRequest, SearchResponse, SearchAssetResult, SearchAlbumResult, SearchFacet, SearchFacetCount models - Create SearchApi with SearchMetadataBuilder supporting 35+ filters - Support filtering by location, dates, camera info, favorites, tags, people, albums, text search, and more - Integrate into Client with client.search().metadata() API
This commit is contained in:
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
pub mod albums;
|
pub mod albums;
|
||||||
pub mod assets;
|
pub mod assets;
|
||||||
|
pub mod search;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod timeline;
|
pub mod timeline;
|
||||||
|
|
||||||
// Re-export main API modules
|
// Re-export main API modules
|
||||||
pub use albums::AlbumsApi;
|
pub use albums::AlbumsApi;
|
||||||
pub use assets::AssetsApi;
|
pub use assets::AssetsApi;
|
||||||
|
pub use search::SearchApi;
|
||||||
pub use server::ServerApi;
|
pub use server::ServerApi;
|
||||||
pub use timeline::TimelineApi;
|
pub use timeline::TimelineApi;
|
||||||
|
|||||||
395
src/apis/search.rs
Normal file
395
src/apis/search.rs
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
//! Search API - Search for assets and albums by metadata
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Client,
|
||||||
|
error::Result,
|
||||||
|
models::{
|
||||||
|
AssetId, AssetOrder, AssetType, AssetVisibility, MetadataSearchRequest, SearchResponse,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
/// API for searching assets and albums
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SearchApi {
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SearchApi {
|
||||||
|
/// Create a new search API instance
|
||||||
|
pub const fn new(client: Client) -> Self {
|
||||||
|
Self { client }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start a metadata search with the builder pattern
|
||||||
|
pub fn metadata(&self) -> SearchMetadataBuilder {
|
||||||
|
SearchMetadataBuilder::new(self.client.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builder for the metadata search endpoint
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SearchMetadataBuilder {
|
||||||
|
client: Client,
|
||||||
|
album_ids: Vec<AssetId>,
|
||||||
|
checksum: Option<String>,
|
||||||
|
city: Option<String>,
|
||||||
|
country: Option<String>,
|
||||||
|
created_after: Option<DateTime<Utc>>,
|
||||||
|
created_before: Option<DateTime<Utc>>,
|
||||||
|
description: Option<String>,
|
||||||
|
device_asset_id: Option<String>,
|
||||||
|
device_id: Option<String>,
|
||||||
|
is_favorite: Option<bool>,
|
||||||
|
is_motion: Option<bool>,
|
||||||
|
is_not_in_album: Option<bool>,
|
||||||
|
is_offline: Option<bool>,
|
||||||
|
lens_model: Option<String>,
|
||||||
|
library_id: Option<AssetId>,
|
||||||
|
make: Option<String>,
|
||||||
|
model: Option<String>,
|
||||||
|
ocr: Option<String>,
|
||||||
|
order: Option<AssetOrder>,
|
||||||
|
original_file_name: Option<String>,
|
||||||
|
page: Option<u32>,
|
||||||
|
person_ids: Vec<AssetId>,
|
||||||
|
rating: Option<i32>,
|
||||||
|
size: Option<u32>,
|
||||||
|
state: Option<String>,
|
||||||
|
tag_ids: Vec<AssetId>,
|
||||||
|
taken_after: Option<DateTime<Utc>>,
|
||||||
|
taken_before: Option<DateTime<Utc>>,
|
||||||
|
asset_type: Option<AssetType>,
|
||||||
|
updated_after: Option<DateTime<Utc>>,
|
||||||
|
updated_before: Option<DateTime<Utc>>,
|
||||||
|
visibility: Option<AssetVisibility>,
|
||||||
|
with_deleted: Option<bool>,
|
||||||
|
with_exif: Option<bool>,
|
||||||
|
with_people: Option<bool>,
|
||||||
|
with_stacked: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SearchMetadataBuilder {
|
||||||
|
/// Create a new metadata search builder
|
||||||
|
const fn new(client: Client) -> Self {
|
||||||
|
Self {
|
||||||
|
client,
|
||||||
|
album_ids: Vec::new(),
|
||||||
|
checksum: None,
|
||||||
|
city: None,
|
||||||
|
country: None,
|
||||||
|
created_after: None,
|
||||||
|
created_before: None,
|
||||||
|
description: None,
|
||||||
|
device_asset_id: None,
|
||||||
|
device_id: None,
|
||||||
|
is_favorite: None,
|
||||||
|
is_motion: None,
|
||||||
|
is_not_in_album: None,
|
||||||
|
is_offline: None,
|
||||||
|
lens_model: None,
|
||||||
|
library_id: None,
|
||||||
|
make: None,
|
||||||
|
model: None,
|
||||||
|
ocr: None,
|
||||||
|
order: None,
|
||||||
|
original_file_name: None,
|
||||||
|
page: None,
|
||||||
|
person_ids: Vec::new(),
|
||||||
|
rating: None,
|
||||||
|
size: None,
|
||||||
|
state: None,
|
||||||
|
tag_ids: Vec::new(),
|
||||||
|
taken_after: None,
|
||||||
|
taken_before: None,
|
||||||
|
asset_type: None,
|
||||||
|
updated_after: None,
|
||||||
|
updated_before: None,
|
||||||
|
visibility: None,
|
||||||
|
with_deleted: None,
|
||||||
|
with_exif: None,
|
||||||
|
with_people: None,
|
||||||
|
with_stacked: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by album ID (can be called multiple times to filter by multiple albums)
|
||||||
|
pub fn album_id(mut self, id: AssetId) -> Self {
|
||||||
|
self.album_ids.push(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by file checksum
|
||||||
|
pub fn checksum(mut self, checksum: impl Into<String>) -> Self {
|
||||||
|
self.checksum = Some(checksum.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by city name
|
||||||
|
pub fn city(mut self, city: impl Into<String>) -> Self {
|
||||||
|
self.city = Some(city.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by country name
|
||||||
|
pub fn country(mut self, country: impl Into<String>) -> Self {
|
||||||
|
self.country = Some(country.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by creation date after
|
||||||
|
pub fn created_after(mut self, after: DateTime<Utc>) -> Self {
|
||||||
|
self.created_after = Some(after);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by creation date before
|
||||||
|
pub fn created_before(mut self, before: DateTime<Utc>) -> Self {
|
||||||
|
self.created_before = Some(before);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by description text
|
||||||
|
pub fn description(mut self, desc: impl Into<String>) -> Self {
|
||||||
|
self.description = Some(desc.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by device asset ID
|
||||||
|
pub fn device_asset_id(mut self, id: impl Into<String>) -> Self {
|
||||||
|
self.device_asset_id = Some(id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by device ID
|
||||||
|
pub fn device_id(mut self, id: impl Into<String>) -> Self {
|
||||||
|
self.device_id = Some(id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by favorite status
|
||||||
|
pub fn favorite(mut self, is_favorite: bool) -> Self {
|
||||||
|
self.is_favorite = Some(is_favorite);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by motion photo status
|
||||||
|
pub fn motion(mut self, is_motion: bool) -> Self {
|
||||||
|
self.is_motion = Some(is_motion);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter assets not in any album
|
||||||
|
pub fn not_in_album(mut self) -> Self {
|
||||||
|
self.is_not_in_album = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by offline status
|
||||||
|
pub fn offline(mut self, is_offline: bool) -> Self {
|
||||||
|
self.is_offline = Some(is_offline);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by lens model
|
||||||
|
pub fn lens_model(mut self, model: impl Into<String>) -> Self {
|
||||||
|
self.lens_model = Some(model.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by library ID
|
||||||
|
pub fn library_id(mut self, id: AssetId) -> Self {
|
||||||
|
self.library_id = Some(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by camera make
|
||||||
|
pub fn make(mut self, make: impl Into<String>) -> Self {
|
||||||
|
self.make = Some(make.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by camera model
|
||||||
|
pub fn model(mut self, model: impl Into<String>) -> Self {
|
||||||
|
self.model = Some(model.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by OCR text content
|
||||||
|
pub fn ocr(mut self, text: impl Into<String>) -> Self {
|
||||||
|
self.ocr = Some(text.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set sort order
|
||||||
|
pub fn order(mut self, order: AssetOrder) -> Self {
|
||||||
|
self.order = Some(order);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by original file name
|
||||||
|
pub fn file_name(mut self, name: impl Into<String>) -> Self {
|
||||||
|
self.original_file_name = Some(name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set page number
|
||||||
|
pub fn page(mut self, page: u32) -> Self {
|
||||||
|
self.page = Some(page);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by person ID (can be called multiple times to filter by multiple people)
|
||||||
|
pub fn person_id(mut self, id: AssetId) -> Self {
|
||||||
|
self.person_ids.push(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by rating (1-5, or -1 deprecated)
|
||||||
|
pub fn rating(mut self, rating: i32) -> Self {
|
||||||
|
self.rating = Some(rating);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set number of results (1-1000, default 100)
|
||||||
|
pub fn size(mut self, size: u32) -> Self {
|
||||||
|
self.size = Some(size);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by state/province name
|
||||||
|
pub fn state(mut self, state: impl Into<String>) -> Self {
|
||||||
|
self.state = Some(state.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by tag ID (can be called multiple times to filter by multiple tags)
|
||||||
|
pub fn tag_id(mut self, id: AssetId) -> Self {
|
||||||
|
self.tag_ids.push(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by taken date after
|
||||||
|
pub fn taken_after(mut self, after: DateTime<Utc>) -> Self {
|
||||||
|
self.taken_after = Some(after);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by taken date before
|
||||||
|
pub fn taken_before(mut self, before: DateTime<Utc>) -> Self {
|
||||||
|
self.taken_before = Some(before);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by asset type
|
||||||
|
pub fn asset_type(mut self, asset_type: AssetType) -> Self {
|
||||||
|
self.asset_type = Some(asset_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by update date after
|
||||||
|
pub fn updated_after(mut self, after: DateTime<Utc>) -> Self {
|
||||||
|
self.updated_after = Some(after);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by update date before
|
||||||
|
pub fn updated_before(mut self, before: DateTime<Utc>) -> Self {
|
||||||
|
self.updated_before = Some(before);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter by visibility
|
||||||
|
pub fn visibility(mut self, visibility: AssetVisibility) -> Self {
|
||||||
|
self.visibility = Some(visibility);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Include deleted assets in results
|
||||||
|
pub fn with_deleted(mut self) -> Self {
|
||||||
|
self.with_deleted = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Include EXIF data in response
|
||||||
|
pub fn with_exif(mut self) -> Self {
|
||||||
|
self.with_exif = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Include people data in response
|
||||||
|
pub fn with_people(mut self) -> Self {
|
||||||
|
self.with_people = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Include stacked assets
|
||||||
|
pub fn with_stacked(mut self) -> Self {
|
||||||
|
self.with_stacked = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the request and execute it
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error if the HTTP request fails, if the response cannot be deserialized,
|
||||||
|
/// or if authentication is missing/invalid
|
||||||
|
pub async fn execute(self) -> Result<SearchResponse> {
|
||||||
|
let request = MetadataSearchRequest {
|
||||||
|
album_ids: if self.album_ids.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.album_ids)
|
||||||
|
},
|
||||||
|
checksum: self.checksum,
|
||||||
|
city: self.city,
|
||||||
|
country: self.country,
|
||||||
|
created_after: self.created_after,
|
||||||
|
created_before: self.created_before,
|
||||||
|
description: self.description,
|
||||||
|
device_asset_id: self.device_asset_id,
|
||||||
|
device_id: self.device_id,
|
||||||
|
is_favorite: self.is_favorite,
|
||||||
|
is_motion: self.is_motion,
|
||||||
|
is_not_in_album: self.is_not_in_album,
|
||||||
|
is_offline: self.is_offline,
|
||||||
|
lens_model: self.lens_model,
|
||||||
|
library_id: self.library_id,
|
||||||
|
make: self.make,
|
||||||
|
model: self.model,
|
||||||
|
ocr: self.ocr,
|
||||||
|
order: self.order,
|
||||||
|
original_file_name: self.original_file_name,
|
||||||
|
page: self.page,
|
||||||
|
person_ids: if self.person_ids.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.person_ids)
|
||||||
|
},
|
||||||
|
rating: self.rating,
|
||||||
|
size: self.size,
|
||||||
|
state: self.state,
|
||||||
|
tag_ids: if self.tag_ids.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.tag_ids)
|
||||||
|
},
|
||||||
|
taken_after: self.taken_after,
|
||||||
|
taken_before: self.taken_before,
|
||||||
|
asset_type: self.asset_type,
|
||||||
|
updated_after: self.updated_after,
|
||||||
|
updated_before: self.updated_before,
|
||||||
|
visibility: self.visibility,
|
||||||
|
with_deleted: self.with_deleted,
|
||||||
|
with_exif: self.with_exif,
|
||||||
|
with_people: self.with_people,
|
||||||
|
with_stacked: self.with_stacked,
|
||||||
|
};
|
||||||
|
|
||||||
|
let req = self.client.post("/search/metadata").json(&request);
|
||||||
|
let response = self.client.execute(req.build()?).await?;
|
||||||
|
let result: SearchResponse = response.json().await?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Client for interacting with the Immich API
|
//! Client for interacting with the Immich API
|
||||||
|
|
||||||
use crate::apis::{AlbumsApi, AssetsApi, ServerApi, TimelineApi};
|
use crate::apis::{AlbumsApi, AssetsApi, SearchApi, ServerApi, TimelineApi};
|
||||||
use crate::error::{ImmichError, Result};
|
use crate::error::{ImmichError, Result};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -201,6 +201,11 @@ impl Client {
|
|||||||
AssetsApi::new(self.clone())
|
AssetsApi::new(self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access the search API
|
||||||
|
pub fn search(&self) -> SearchApi {
|
||||||
|
SearchApi::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
/// Access the server API
|
/// Access the server API
|
||||||
pub fn server(&self) -> ServerApi {
|
pub fn server(&self) -> ServerApi {
|
||||||
ServerApi::new(self.clone())
|
ServerApi::new(self.clone())
|
||||||
|
|||||||
@@ -344,6 +344,180 @@ pub struct TimeBucketAssetResponse {
|
|||||||
pub visibility: Vec<AssetVisibility>,
|
pub visibility: Vec<AssetVisibility>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Metadata search request for filtering assets
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct MetadataSearchRequest {
|
||||||
|
/// Filter by album IDs
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub album_ids: Option<Vec<AssetId>>,
|
||||||
|
/// Filter by file checksum
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub checksum: Option<String>,
|
||||||
|
/// Filter by city name
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub city: Option<String>,
|
||||||
|
/// Filter by country name
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub country: Option<String>,
|
||||||
|
/// Filter by creation date (after)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub created_after: Option<DateTime<Utc>>,
|
||||||
|
/// Filter by creation date (before)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub created_before: Option<DateTime<Utc>>,
|
||||||
|
/// Filter by description text
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// Filter by device asset ID
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub device_asset_id: Option<String>,
|
||||||
|
/// Device ID to filter by
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub device_id: Option<String>,
|
||||||
|
/// Filter by favorite status
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub is_favorite: Option<bool>,
|
||||||
|
/// Filter by motion photo status
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub is_motion: Option<bool>,
|
||||||
|
/// Filter assets not in any album
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub is_not_in_album: Option<bool>,
|
||||||
|
/// Filter by offline status
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub is_offline: Option<bool>,
|
||||||
|
/// Filter by lens model
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub lens_model: Option<String>,
|
||||||
|
/// Library ID to filter by
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub library_id: Option<AssetId>,
|
||||||
|
/// Filter by camera make
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub make: Option<String>,
|
||||||
|
/// Filter by camera model
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub model: Option<String>,
|
||||||
|
/// Filter by OCR text content
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ocr: Option<String>,
|
||||||
|
/// Sort order
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub order: Option<AssetOrder>,
|
||||||
|
/// Filter by original file name
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub original_file_name: Option<String>,
|
||||||
|
/// Page number (default: 1)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub page: Option<u32>,
|
||||||
|
/// Filter by person IDs
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub person_ids: Option<Vec<AssetId>>,
|
||||||
|
/// Filter by rating [1-5], or null for unrated (-1 deprecated)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub rating: Option<i32>,
|
||||||
|
/// Number of results to return (default: 100)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub size: Option<u32>,
|
||||||
|
/// Filter by state/province name
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub state: Option<String>,
|
||||||
|
/// Filter by tag IDs
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tag_ids: Option<Vec<AssetId>>,
|
||||||
|
/// Filter by taken date (after)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub taken_after: Option<DateTime<Utc>>,
|
||||||
|
/// Filter by taken date (before)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub taken_before: Option<DateTime<Utc>>,
|
||||||
|
/// Asset type filter
|
||||||
|
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub asset_type: Option<AssetType>,
|
||||||
|
/// Filter by update date (after)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub updated_after: Option<DateTime<Utc>>,
|
||||||
|
/// Filter by update date (before)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub updated_before: Option<DateTime<Utc>>,
|
||||||
|
/// Filter by visibility
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub visibility: Option<AssetVisibility>,
|
||||||
|
/// Include deleted assets
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub with_deleted: Option<bool>,
|
||||||
|
/// Include EXIF data in response
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub with_exif: Option<bool>,
|
||||||
|
/// Include people data in response
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub with_people: Option<bool>,
|
||||||
|
/// Include stacked assets
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub with_stacked: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a facet value count
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SearchFacetCount {
|
||||||
|
/// Number of assets with this facet value
|
||||||
|
pub count: i64,
|
||||||
|
/// Facet value
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a search facet
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SearchFacet {
|
||||||
|
/// Facet counts
|
||||||
|
pub counts: Vec<SearchFacetCount>,
|
||||||
|
/// Facet field name
|
||||||
|
pub field_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Paginated asset search results
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SearchAssetResult {
|
||||||
|
/// Number of assets in this page
|
||||||
|
pub count: i64,
|
||||||
|
/// Facet information
|
||||||
|
pub facets: Vec<SearchFacet>,
|
||||||
|
/// The actual assets
|
||||||
|
pub items: Vec<AssetResponse>,
|
||||||
|
/// Next page token
|
||||||
|
pub next_page: Option<String>,
|
||||||
|
/// Total number of matching assets
|
||||||
|
pub total: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Paginated album search results
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SearchAlbumResult {
|
||||||
|
/// Number of albums in this page
|
||||||
|
pub count: i64,
|
||||||
|
/// Facet information
|
||||||
|
pub facets: Vec<SearchFacet>,
|
||||||
|
/// The actual albums
|
||||||
|
pub items: Vec<AlbumResponse>,
|
||||||
|
/// Total number of matching albums
|
||||||
|
pub total: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Combined search response
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SearchResponse {
|
||||||
|
/// Album results
|
||||||
|
pub albums: SearchAlbumResult,
|
||||||
|
/// Asset results
|
||||||
|
pub assets: SearchAssetResult,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user