cloud_terrastodon_azure 0.36.0

Helpers for interacting with Azure for the Cloud Terrastodon project
use crate::get_resource_group_choices;
use crate::get_role_assignment_choices;
use crate::get_security_group_choices;
use cloud_terrastodon_azure_types::AzureTenantId;
use cloud_terrastodon_azure_types::Scope;
use cloud_terrastodon_hcl_types::HclImportBlock;
use cloud_terrastodon_hcl_types::HclProviderBlock;
use cloud_terrastodon_hcl_types::HclProviderReference;
use cloud_terrastodon_hcl_types::ProviderKind;
use cloud_terrastodon_hcl_types::Sanitizable;
use cloud_terrastodon_hcl_types::edit::structure::Block;
use cloud_terrastodon_hcl_types::edit::structure::Body;
use cloud_terrastodon_user_input::Choice;
use cloud_terrastodon_user_input::PickerTui;
use std::collections::HashSet;
use std::convert::TryFrom;
use strum::VariantArray;
use tracing::info;

#[derive(strum::VariantArray, Debug, Clone, Copy)]
pub enum HclImportable {
    ResourceGroup,
    SecurityGroup,
    RoleAssignment,
}
impl std::fmt::Display for HclImportable {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{self:?}")
    }
}
impl HclImportable {
    pub async fn try_into_import_blocks(
        &self,
        tenant_id: AzureTenantId,
    ) -> eyre::Result<Vec<Choice<(HclImportBlock, Option<HclProviderBlock>)>>> {
        let rtn: Vec<Choice<(HclImportBlock, Option<HclProviderBlock>)>> = match self {
            HclImportable::ResourceGroup => {
                info!("Fetching resource groups");
                get_resource_group_choices(tenant_id)
                    .await?
                    .into_iter()
                    .map(|Choice { key, value: rg }| {
                        let provider_block = Some(HclProviderBlock::AzureRM {
                            alias: Some(rg.subscription_name.sanitize()),
                            subscription_id: Some(rg.id.subscription_id.short_form().to_owned()),
                        });
                        let import_block = {
                            let name = rg.subscription_name.to_string();
                            let mut import_block: HclImportBlock = rg.into();
                            import_block.provider = HclProviderReference::Alias {
                                kind: ProviderKind::AzureRM,
                                name,
                            };
                            import_block
                        };
                        Choice {
                            key,
                            value: (import_block, provider_block),
                        }
                    })
                    .collect()
            }
            HclImportable::SecurityGroup => get_security_group_choices(tenant_id)
                .await?
                .into_iter()
                .map(|choice| Choice {
                    key: choice.key,
                    value: (choice.value.into(), None),
                })
                .collect(),
            HclImportable::RoleAssignment => get_role_assignment_choices(tenant_id)
                .await?
                .into_iter()
                .map(|choice| Choice {
                    key: choice.key,
                    value: (choice.value.into(), None),
                })
                .collect(),
        };
        Ok(rtn)
    }
    pub fn pick() -> eyre::Result<HclImportable> {
        Ok(PickerTui::new()
            .set_header("Pick the kind of thing to import")
            .pick_one(HclImportable::VARIANTS.iter().copied().map(|x| Choice {
                key: x.to_string(),
                value: x,
            }))?)
    }
    pub async fn pick_into_body(self, tenant_id: AzureTenantId) -> eyre::Result<Body> {
        let import_blocks = self.try_into_import_blocks(tenant_id).await?;
        let import_blocks = PickerTui::new()
            .set_header("Pick the resources to import")
            .pick_many(import_blocks)?;
        let providers = import_blocks
            .iter()
            .filter_map(|(_, provider)| provider.clone())
            .collect::<HashSet<_>>();
        let body = Body::builder().blocks(providers).blocks(
            import_blocks
                .into_iter()
                .map(|(import_block, _)| Block::try_from(import_block))
                .collect::<Result<Vec<Block>, _>>()?,
        );

        let body = body.build();
        Ok(body)
    }
}