use std::collections::HashMap;
use async_trait::async_trait;
use futures::stream::{Stream, TryStreamExt};
use osauth::common::IdAndName;
use super::super::common::{FlavorRef, Refresh, ResourceIterator, ResourceQuery};
use super::super::session::Session;
use super::super::utils::Query;
use super::super::Result;
use super::{api, protocol};
#[derive(Clone, Debug)]
pub struct Flavor {
session: Session,
inner: protocol::Flavor,
extra_specs: HashMap<String, String>,
}
#[derive(Clone, Debug)]
pub struct FlavorSummary {
session: Session,
inner: IdAndName,
}
#[derive(Clone, Debug)]
pub struct FlavorQuery {
session: Session,
query: Query,
can_paginate: bool,
}
#[derive(Clone, Debug)]
pub struct DetailedFlavorQuery {
inner: FlavorQuery,
}
impl Flavor {
pub(crate) async fn new(session: Session, mut inner: protocol::Flavor) -> Result<Flavor> {
let extra_specs = match inner.extra_specs.take() {
Some(es) => es,
None => api::get_extra_specs_by_flavor_id(&session, &inner.id).await?,
};
Ok(Flavor {
session,
inner,
extra_specs,
})
}
pub(crate) async fn load<Id: AsRef<str>>(session: Session, id: Id) -> Result<Flavor> {
let inner = api::get_flavor(&session, id).await?;
Flavor::new(session, inner).await
}
pub fn description(&self) -> &Option<String> {
&self.inner.description
}
pub fn emphemeral_size(&self) -> u64 {
self.inner.ephemeral
}
pub fn extra_specs(&self) -> &HashMap<String, String> {
&self.extra_specs
}
pub fn id(&self) -> &String {
&self.inner.id
}
pub fn is_public(&self) -> bool {
self.inner.is_public
}
pub fn name(&self) -> &String {
&self.inner.name
}
pub fn ram_size(&self) -> u64 {
self.inner.ram
}
pub fn rxtx_factor(&self) -> f32 {
self.inner.rxtx_factor
}
pub fn root_size(&self) -> u64 {
self.inner.disk
}
pub fn swap_size(&self) -> u64 {
self.inner.swap
}
pub fn vcpu_count(&self) -> u32 {
self.inner.vcpus
}
}
#[async_trait]
impl Refresh for Flavor {
async fn refresh(&mut self) -> Result<()> {
self.inner = api::get_flavor_by_id(&self.session, &self.inner.id).await?;
Ok(())
}
}
impl FlavorSummary {
pub fn id(&self) -> &String {
&self.inner.id
}
pub fn name(&self) -> &String {
&self.inner.name
}
pub async fn details(&self) -> Result<Flavor> {
Flavor::load(self.session.clone(), &self.inner.id).await
}
}
impl FlavorQuery {
pub(crate) fn new(session: Session) -> FlavorQuery {
FlavorQuery {
session,
query: Query::new(),
can_paginate: true,
}
}
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
}
pub fn detailed(self) -> DetailedFlavorQuery {
DetailedFlavorQuery { inner: self }
}
pub fn into_stream(self) -> impl Stream<Item = Result<FlavorSummary>> {
debug!("Fetching flavors with {:?}", self.query);
ResourceIterator::new(self).into_stream()
}
pub async fn all(self) -> Result<Vec<FlavorSummary>> {
self.into_stream().try_collect().await
}
pub async fn one(mut self) -> Result<FlavorSummary> {
debug!("Fetching one flavor with {:?}", self.query);
if self.can_paginate {
self.query.push("limit", 2);
}
ResourceIterator::new(self).one().await
}
}
#[async_trait]
impl ResourceQuery for FlavorQuery {
type Item = FlavorSummary;
const DEFAULT_LIMIT: usize = 100;
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_flavors(&self.session, &query)
.await?
.into_iter()
.map(|item| FlavorSummary {
session: self.session.clone(),
inner: item,
})
.collect())
}
}
impl DetailedFlavorQuery {
pub fn into_stream(self) -> impl Stream<Item = Result<Flavor>> {
debug!("Fetching detailed flavors with {:?}", self.inner.query);
ResourceIterator::new(self).into_stream()
}
}
#[async_trait]
impl ResourceQuery for DetailedFlavorQuery {
type Item = Flavor;
const DEFAULT_LIMIT: usize = 50;
async fn can_paginate(&self) -> Result<bool> {
Ok(self.inner.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.inner.query.with_marker_and_limit(limit, marker);
let flavors = api::list_flavors_detail(&self.inner.session, &query).await?;
let mut result = Vec::with_capacity(flavors.len());
for item in flavors {
result.push(Flavor::new(self.inner.session.clone(), item).await?);
}
Ok(result)
}
}
impl From<Flavor> for FlavorRef {
fn from(value: Flavor) -> FlavorRef {
FlavorRef::new_verified(value.inner.id)
}
}
impl From<FlavorSummary> for FlavorRef {
fn from(value: FlavorSummary) -> FlavorRef {
FlavorRef::new_verified(value.inner.id)
}
}
#[cfg(feature = "compute")]
impl FlavorRef {
pub(crate) async fn into_verified(self, session: &Session) -> Result<FlavorRef> {
Ok(if self.verified {
self
} else {
FlavorRef::new_verified(api::get_flavor(session, &self.value).await?.id)
})
}
}
impl From<Flavor> for protocol::ServerFlavor {
fn from(value: Flavor) -> protocol::ServerFlavor {
protocol::ServerFlavor {
ephemeral_size: value.inner.ephemeral,
extra_specs: Some(value.extra_specs),
original_name: value.inner.name,
ram_size: value.inner.ram,
root_size: value.inner.disk,
swap_size: value.inner.swap,
vcpu_count: value.inner.vcpus,
}
}
}