use async_trait::async_trait;
use futures::stream::{Stream, TryStreamExt};
use super::super::common::{KeyPairRef, Refresh, ResourceIterator, ResourceQuery};
use super::super::session::Session;
use super::super::utils::Query;
use super::super::{Error, ErrorKind, Result};
use super::{api, protocol};
#[derive(Clone, Debug)]
pub struct KeyPair {
session: Session,
inner: protocol::KeyPair,
}
#[derive(Clone, Debug)]
pub struct KeyPairQuery {
session: Session,
query: Query,
can_paginate: bool,
}
#[derive(Clone, Debug)]
pub struct NewKeyPair {
session: Session,
inner: protocol::KeyPairCreate,
}
impl KeyPair {
pub(crate) async fn new<Id: AsRef<str>>(session: Session, id: Id) -> Result<KeyPair> {
let inner = api::get_keypair(&session, id).await?;
Ok(KeyPair { session, inner })
}
pub async fn delete(self) -> Result<()> {
api::delete_keypair(&self.session, &self.inner.name).await
}
transparent_property! {
#[doc = "Key pair fingerprint."]
fingerprint: ref String
}
transparent_property! {
#[doc = "Key pair type, if available."]
key_type: Option<protocol::KeyPairType>
}
transparent_property! {
#[doc = "Key pair name."]
name: ref String
}
transparent_property! {
#[doc = "Public key."]
public_key: ref String
}
}
#[async_trait]
impl Refresh for KeyPair {
async fn refresh(&mut self) -> Result<()> {
self.inner = api::get_keypair(&self.session, &self.inner.name).await?;
Ok(())
}
}
impl KeyPairQuery {
pub(crate) fn new(session: Session) -> KeyPairQuery {
KeyPairQuery {
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 into_stream(self) -> impl Stream<Item = Result<KeyPair>> {
debug!("Fetching key pairs with {:?}", self.query);
ResourceIterator::new(self).into_stream()
}
pub async fn all(self) -> Result<Vec<KeyPair>> {
self.into_stream().try_collect().await
}
pub async fn one(mut self) -> Result<KeyPair> {
debug!("Fetching one key pair with {:?}", self.query);
if self.can_paginate {
self.query.push("limit", 2);
}
ResourceIterator::new(self).one().await
}
}
impl NewKeyPair {
pub(crate) fn new(session: Session, name: String) -> NewKeyPair {
NewKeyPair {
session,
inner: protocol::KeyPairCreate::new(name),
}
}
pub async fn create(self) -> Result<KeyPair> {
if self.inner.public_key.is_none() {
return Err(Error::new(
ErrorKind::InvalidInput,
"Public key contents is required",
));
};
let keypair = api::create_keypair(&self.session, self.inner).await?;
Ok(KeyPair {
session: self.session,
inner: keypair,
})
}
pub async fn generate(mut self) -> Result<(KeyPair, String)> {
self.inner.public_key = None;
let mut keypair = api::create_keypair(&self.session, self.inner).await?;
if let Some(private_key) = keypair.private_key.take() {
let result = KeyPair {
session: self.session,
inner: keypair,
};
Ok((result, private_key))
} else {
Err(Error::new(
ErrorKind::InvalidResponse,
"Missing private key in the response",
))
}
}
creation_inner_field! {
#[doc = "Set type of the key pair."]
set_key_type, with_key_type -> key_type: optional protocol::KeyPairType
}
creation_inner_field! {
#[doc = "Set name of the key pair."]
set_name, with_name -> name: String
}
creation_inner_field! {
#[doc = "Set name of the key pair."]
set_public_key, with_public_key -> public_key: optional String
}
}
#[async_trait]
impl ResourceQuery for KeyPairQuery {
type Item = KeyPair;
const DEFAULT_LIMIT: usize = 50;
async fn can_paginate(&self) -> Result<bool> {
if self.can_paginate {
api::supports_keypair_pagination(&self.session).await
} else {
Ok(false)
}
}
fn extract_marker(&self, resource: &Self::Item) -> String {
resource.name().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_keypairs(&self.session, &query)
.await?
.into_iter()
.map(|item| KeyPair {
session: self.session.clone(),
inner: item,
})
.collect())
}
}
impl From<KeyPair> for KeyPairRef {
fn from(value: KeyPair) -> KeyPairRef {
KeyPairRef::new_verified(value.inner.name)
}
}
#[cfg(feature = "compute")]
impl KeyPairRef {
pub(crate) async fn into_verified(self, session: &Session) -> Result<KeyPairRef> {
Ok(if self.verified {
self
} else {
KeyPairRef::new_verified(api::get_keypair(session, &self.value).await?.name)
})
}
}