use async_trait::async_trait;
use chrono::{DateTime, FixedOffset};
use futures::stream::{Stream, TryStreamExt};
use super::super::common::{ImageRef, Refresh, ResourceIterator, ResourceQuery};
use super::super::session::Session;
use super::super::utils::Query;
use super::super::{Result, Sort};
use super::{api, protocol};
#[derive(Clone, Debug)]
pub struct ImageQuery {
session: Session,
query: Query,
can_paginate: bool,
sort: Vec<String>,
}
#[derive(Clone, Debug)]
pub struct Image {
session: Session,
inner: protocol::Image,
}
impl Image {
pub(crate) async fn new<Id: AsRef<str>>(session: Session, id: Id) -> Result<Image> {
let inner = api::get_image(&session, id).await?;
Ok(Image { session, inner })
}
transparent_property! {
#[doc = "Image architecture."]
architecture: ref Option<String>
}
transparent_property! {
#[doc = "Checksum of the image."]
checksum: ref Option<String>
}
transparent_property! {
#[doc = "Container format."]
container_format: Option<protocol::ImageContainerFormat>
}
transparent_property! {
#[doc = "Creating date and time."]
created_at: DateTime<FixedOffset>
}
transparent_property! {
#[doc = "Disk format."]
disk_format: Option<protocol::ImageDiskFormat>
}
transparent_property! {
#[doc = "Unique ID."]
id: ref String
}
pub fn minimum_required_disk(&self) -> u32 {
self.inner.min_disk
}
pub fn minimum_required_ram(&self) -> u32 {
self.inner.min_ram
}
transparent_property! {
#[doc = "Image name."]
name: ref String
}
transparent_property! {
#[doc = "Image size in bytes."]
size: Option<u64>
}
transparent_property! {
#[doc = "Image status."]
status: protocol::ImageStatus
}
transparent_property! {
#[doc = "Last update date and time."]
updated_at: DateTime<FixedOffset>
}
transparent_property! {
#[doc = "Virtual size of the image."]
virtual_size: Option<u64>
}
transparent_property! {
#[doc = "Image visibility."]
visibility: protocol::ImageVisibility
}
}
#[async_trait]
impl Refresh for Image {
async fn refresh(&mut self) -> Result<()> {
self.inner = api::get_image_by_id(&self.session, &self.inner.id).await?;
Ok(())
}
}
impl ImageQuery {
pub(crate) fn new(session: Session) -> ImageQuery {
ImageQuery {
session,
query: Query::new(),
can_paginate: true,
sort: Vec::new(),
}
}
pub fn sort_by(mut self, sort: Sort<protocol::ImageSortKey>) -> Self {
let (field, direction) = sort.into();
self.sort.push(format!("{field}:{direction}"));
self
}
pub fn with_marker<T: Into<String>>(mut self, marker: T) -> Self {
self.can_paginate = false;
self.query.push_str("marker", marker);
self
}
pub fn with_limit(mut self, limit: usize) -> Self {
self.can_paginate = false;
self.query.push("limit", limit);
self
}
query_filter! {
#[doc = "Filter by image name."]
with_name -> name
}
query_filter! {
#[doc = "Filter by image status."]
with_status -> status: protocol::ImageStatus
}
query_filter! {
#[doc = "Filter by visibility."]
with_visibility -> visibility: protocol::ImageVisibility
}
pub fn into_stream(
mut self,
) -> impl Stream<Item = Result<<ImageQuery as ResourceQuery>::Item>> {
if !self.sort.is_empty() {
self.query.push_str("sort", self.sort.join(","));
}
debug!("Fetching images with {:?}", self.query);
ResourceIterator::new(self).into_stream()
}
pub async fn all(self) -> Result<Vec<Image>> {
self.into_stream().try_collect().await
}
pub async fn one(mut self) -> Result<Image> {
debug!("Fetching one image with {:?}", self.query);
if self.can_paginate {
self.query.push("limit", 2);
}
ResourceIterator::new(self).one().await
}
}
#[async_trait]
impl ResourceQuery for ImageQuery {
type Item = Image;
const DEFAULT_LIMIT: usize = 50;
async fn can_paginate(&self) -> Result<bool> {
Ok(self.can_paginate)
}
fn extract_marker(&self, resource: &Self::Item) -> String {
resource.id().clone()
}
async fn fetch_chunk(
&self,
limit: Option<usize>,
marker: Option<String>,
) -> Result<Vec<Self::Item>> {
let query = self.query.with_marker_and_limit(limit, marker);
Ok(api::list_images(&self.session, &query)
.await?
.into_iter()
.map(|item| Image {
session: self.session.clone(),
inner: item,
})
.collect())
}
}
impl From<Image> for ImageRef {
fn from(value: Image) -> ImageRef {
ImageRef::new_verified(value.inner.id)
}
}
#[cfg(feature = "image")]
impl ImageRef {
pub(crate) async fn into_verified(self, session: &Session) -> Result<ImageRef> {
Ok(if self.verified {
self
} else {
ImageRef::new_verified(api::get_image(session, &self.value).await?.id)
})
}
}