test-shisho-datasource 0.1.0

This is the utility for Shisho Cloud Policy SDK.
Documentation
use std::str::FromStr;

use crate::gqlgen::deprecation::DeprecationStrategy;
use crate::gqlgen::normalization::Normalization;

const DEPRECATION_ERROR: &str = "deprecated must be one of 'allow', 'deny', or 'warn'";
const NORMALIZATION_ERROR: &str = "normalization must be one of 'none' or 'rust'";

/// The `shisho_graphql` attribute as a `syn::Path`.
fn path_to_match() -> syn::Path {
    syn::parse_str("shisho_graphql").expect("`shisho_graphql` is a valid path")
}

/// Extract an configuration parameter specified in the `shisho_graphql` attribute.
pub fn extract_attr(ast: &syn::DeriveInput, attr: &str) -> Result<String, syn::Error> {
    let attributes = &ast.attrs;
    let graphql_path = path_to_match();
    let attribute = attributes
        .iter()
        .find(|attr| attr.path == graphql_path)
        .ok_or_else(|| syn::Error::new_spanned(ast, "The shisho_graphql attribute is missing"))?;
    if let syn::Meta::List(items) = &attribute.parse_meta().expect("Attribute is well formatted") {
        for item in items.nested.iter() {
            if let syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) = item {
                let syn::MetaNameValue { path, lit, .. } = name_value;
                if let Some(ident) = path.get_ident() {
                    if ident == attr {
                        if let syn::Lit::Str(lit) = lit {
                            return Ok(lit.value());
                        }
                    }
                }
            }
        }
    }

    Err(syn::Error::new_spanned(
        &ast,
        format!("Attribute `{}` not found", attr),
    ))
}

/// Extract a list of configuration parameter values specified in the `shisho_graphql` attribute.
pub fn extract_attr_list(ast: &syn::DeriveInput, attr: &str) -> Result<Vec<String>, syn::Error> {
    let attributes = &ast.attrs;
    let graphql_path = path_to_match();
    let attribute = attributes
        .iter()
        .find(|attr| attr.path == graphql_path)
        .ok_or_else(|| syn::Error::new_spanned(ast, "The shisho_graphql is missing"))?;
    if let syn::Meta::List(items) = &attribute.parse_meta().expect("Attribute is well formatted") {
        for item in items.nested.iter() {
            if let syn::NestedMeta::Meta(syn::Meta::List(value_list)) = item {
                if let Some(ident) = value_list.path.get_ident() {
                    if ident == attr {
                        return value_list
                            .nested
                            .iter()
                            .map(|lit| {
                                if let syn::NestedMeta::Lit(syn::Lit::Str(lit)) = lit {
                                    Ok(lit.value())
                                } else {
                                    Err(syn::Error::new_spanned(
                                        lit,
                                        "Attribute inside value list must be a literal",
                                    ))
                                }
                            })
                            .collect();
                    }
                }
            }
        }
    }

    Err(syn::Error::new_spanned(ast, "Attribute not found"))
}

/// Get the deprecation from a struct attribute in the derive case.
pub fn extract_deprecation_strategy(
    ast: &syn::DeriveInput,
) -> Result<DeprecationStrategy, syn::Error> {
    extract_attr(ast, "deprecated")?
        .to_lowercase()
        .as_str()
        .parse()
        .map_err(|_| syn::Error::new_spanned(ast, DEPRECATION_ERROR.to_owned()))
}

/// Get the deprecation from a struct attribute in the derive case.
pub fn extract_normalization(ast: &syn::DeriveInput) -> Result<Normalization, syn::Error> {
    extract_attr(ast, "normalization")?
        .to_lowercase()
        .as_str()
        .parse()
        .map_err(|_| syn::Error::new_spanned(ast, NORMALIZATION_ERROR))
}

pub fn extract_fragments_other_variant(ast: &syn::DeriveInput) -> bool {
    extract_attr(ast, "fragments_other_variant")
        .ok()
        .and_then(|s| FromStr::from_str(s.as_str()).ok())
        .unwrap_or(false)
}