pmhelp-internal 0.0.2

Internals that will be used by `pmhelp`. This should not be used directly.
Documentation
use alloc::vec::Vec;
use syn::{
    parse::{Parse, ParseBuffer, Peek},
    GenericArgument, Ident, Path, PathArguments, PathSegment, Type, TypePath,
};

#[derive(PartialEq, Clone, Copy)]
enum OptionCheckerState {
    Unknown = 1,
    StdOrCore = 2,
    OptionMod = 3,
    Final = 4,
}

impl PartialEq<PathSegment> for OptionCheckerState {
    fn eq(&self, PathSegment { ident, arguments }: &PathSegment) -> bool {
        match self {
            Self::StdOrCore => {
                (ident == "std" || ident == "core") && matches!(arguments, PathArguments::None)
            }
            Self::OptionMod => ident == "option" && matches!(arguments, PathArguments::None),
            Self::Final => ident == "Option" && !matches!(arguments, PathArguments::None),
            Self::Unknown => true,
        }
    }
}

impl OptionCheckerState {
    fn next_states(&self) -> &[Self] {
        match self {
            Self::Final => &[],
            Self::OptionMod => &[Self::Final],
            Self::StdOrCore => &[Self::OptionMod],
            Self::Unknown => &[Self::OptionMod, Self::Final, Self::StdOrCore],
        }
    }
}

pub trait OptionTypeExt {
    fn is_option(&self) -> bool;
}

impl OptionTypeExt for Path {
    fn is_option(&self) -> bool {
        let mut current_state = if self.leading_colon.is_some() {
            OptionCheckerState::StdOrCore
        } else {
            OptionCheckerState::Unknown
        };
        for segment in self.segments.iter() {
            let mut state_changed = false;
            for state in current_state.next_states() {
                if state == segment {
                    current_state = *state;
                    state_changed = true;
                    break;
                }
            }
            if !state_changed {
                return false;
            }
        }
        current_state == OptionCheckerState::Final
    }
}

impl OptionTypeExt for Type {
    fn is_option(&self) -> bool {
        if let Self::Path(TypePath { path, qself: None }) = &self {
            path.is_option()
        } else {
            false
        }
    }
}

#[derive(PartialEq, Clone, Copy)]
enum ResultCheckerState {
    Unknown = 1,
    StdOrCore = 2,
    ResultMod = 3,
    Final = 4,
}

impl PartialEq<PathSegment> for ResultCheckerState {
    fn eq(&self, PathSegment { ident, arguments }: &PathSegment) -> bool {
        match self {
            Self::StdOrCore => {
                (ident == "std" || ident == "core") && matches!(arguments, PathArguments::None)
            }
            Self::ResultMod => ident == "result" && matches!(arguments, PathArguments::None),
            Self::Final => ident == "Result" && !matches!(arguments, PathArguments::None),
            Self::Unknown => true,
        }
    }
}

impl ResultCheckerState {
    fn next_states(&self) -> &[Self] {
        match self {
            Self::Final => &[],
            Self::ResultMod => &[Self::Final],
            Self::StdOrCore => &[Self::ResultMod],
            Self::Unknown => &[Self::ResultMod, Self::Final, Self::StdOrCore],
        }
    }
}

pub trait ResultTypeExt {
    fn is_result(&self) -> bool;
}

impl ResultTypeExt for Path {
    fn is_result(&self) -> bool {
        let mut current_state = if self.leading_colon.is_some() {
            ResultCheckerState::StdOrCore
        } else {
            ResultCheckerState::Unknown
        };
        for segment in self.segments.iter() {
            let mut state_changed = false;
            for state in current_state.next_states() {
                if state == segment {
                    current_state = *state;
                    state_changed = true;
                    break;
                }
            }
            if !state_changed {
                return false;
            }
        }
        current_state == ResultCheckerState::Final
    }
}

impl ResultTypeExt for Type {
    fn is_result(&self) -> bool {
        if let Self::Path(TypePath { path, qself: None }) = &self {
            path.is_result()
        } else {
            false
        }
    }
}