getargs-derive-macros 0.1.0

Internal implementation detail of getargs-derive. Not meant for direct use; depend on getargs-derive instead.
Documentation
use syn::{GenericArgument, PathArguments, Type};

pub(super) fn is_bool_type(ty: &Type) -> bool {
    match ty {
        Type::Path(path) => path
            .path
            .segments
            .last()
            .map(|segment| segment.ident == "bool")
            .unwrap_or(false),
        _ => false,
    }
}

pub(super) fn option_inner_type(ty: &Type) -> Option<&Type> {
    let Type::Path(path) = ty else {
        return None;
    };
    if path.qself.is_some() {
        return None;
    }
    let segment = path.path.segments.last()?;
    if segment.ident != "Option" {
        return None;
    }
    let PathArguments::AngleBracketed(arguments) = &segment.arguments else {
        return None;
    };
    match arguments.args.first() {
        Some(GenericArgument::Type(inner)) if arguments.args.len() == 1 => Some(inner),
        _ => None,
    }
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum ValueKind {
    Str,
    Bytes,
    OsStr,
    Path,
    OsOwned,
    Parsed,
}

impl ValueKind {
    pub(crate) fn requires_std(self) -> bool {
        matches!(self, ValueKind::OsStr | ValueKind::Path)
    }
}

fn last_ident(ty: &Type) -> Option<&syn::Ident> {
    match ty {
        Type::Path(path) if path.qself.is_none() => {
            path.path.segments.last().map(|segment| &segment.ident)
        }
        _ => None,
    }
}

pub(super) fn value_kind(ty: &Type) -> ValueKind {
    match ty {
        Type::Reference(reference) => match reference.elem.as_ref() {
            Type::Slice(slice) if last_ident(&slice.elem).is_some_and(|ident| ident == "u8") => {
                ValueKind::Bytes
            }
            elem => match last_ident(elem) {
                Some(ident) if ident == "str" => ValueKind::Str,
                Some(ident) if ident == "OsStr" => ValueKind::OsStr,
                Some(ident) if ident == "Path" => ValueKind::Path,
                _ => ValueKind::Parsed,
            },
        },
        _ => match last_ident(ty) {
            Some(ident) if cfg!(feature = "std") && (ident == "PathBuf" || ident == "OsString") => {
                ValueKind::OsOwned
            }
            _ => ValueKind::Parsed,
        },
    }
}

const COUNT_TYPES: &[&str] = &[
    "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize",
];

pub(super) fn is_count_type(ty: &Type) -> bool {
    match ty {
        Type::Path(path) => {
            path.qself.is_none()
                && path.path.segments.last().is_some_and(|segment| {
                    segment.arguments.is_none()
                        && COUNT_TYPES.iter().any(|name| segment.ident == name)
                })
        }
        _ => false,
    }
}

const COLLECTION_TYPES: &[&str] = &["Values", "Vec"];

pub(super) fn collection_element(ty: &Type) -> Option<&Type> {
    let Type::Path(path) = ty else {
        return None;
    };
    if path.qself.is_some() {
        return None;
    }
    let segment = path.path.segments.last()?;
    if !COLLECTION_TYPES.iter().any(|name| segment.ident == name) {
        return None;
    }
    let PathArguments::AngleBracketed(arguments) = &segment.arguments else {
        return None;
    };
    match arguments.args.first() {
        Some(GenericArgument::Type(element)) => Some(element),
        _ => None,
    }
}