myd 0.1.1

An implementation of the rust module system
Documentation
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![deny(clippy::missing_docs_in_private_items)]

use std::collections::{HashMap, HashSet};

pub use cargo_metadata::CargoOpt;
use ego_tree::NodeId;
use syn::Item;

mod module;
pub mod parse;
pub mod platforms;
pub use module::Module;
mod module_information;
pub use module_information::ModuleInformation;
mod syn_helpers;

/// A list of [`syn::Item`]s
pub type Items = Vec<Item>;
/// A map of depends to their features
type Features = HashMap<String, HashSet<String>>;

/// A representation of an import
#[derive(Debug)]
struct Import {
    /// The path to the item of which this points
    pub item: syn::Path,
    /// The strength of the import
    pub strength: ImportStrength,
    /// The alias
    ///
    /// For example in the case of `use std::vec::Vec as List` this would be `List`
    pub az: Option<syn::Ident>,
}

impl Import {
    /// Returns the identifier of this Import
    ///
    /// This will be the `az` if present, elsewise the last
    /// identifier in the path.
    pub fn ident(&self) -> &syn::Ident {
        match &self.az {
            Some(az) => az,
            None => &self.item.segments.last().unwrap().ident,
        }
    }
}

impl Import {
    /// Creates a new Import
    pub fn new(item: syn::Path, strength: ImportStrength, az: Option<syn::Ident>) -> Self {
        Self { item, strength, az }
    }
}

/// Represents the 'strength' of an import.
///
/// This is used by the program to determine whether an import
/// is imported via a glob or not; if it is imported via a glob
/// then it can be safely overriden by another import that is not from a glob.
///
/// However, if two imports of the same strength are present then that is an error.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
enum ImportStrength {
    /// Strong - the user explicitely imported this
    Strong,
    /// Weak - this was resolved through a glob
    Weak,
}

/// A reference to an item on the tree.
///
/// Only useful with [`ModuleInformation`]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct ItemId {
    /// The Node that this points to
    node: NodeId,
    /// The position in the list of the module for the item
    item_id: usize,
}

impl ItemId {
    /// Gets the ID of the module this
    /// item is under.
    pub fn module_id(&self) -> NodeId {
        self.node
    }
}

/// A reference either fo a module or an item on the tree
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ModuleOrItem {
    /// A module
    ///
    /// Can be accessed through the `tree` field on [`ModuleInformation`]
    Module(NodeId),
    /// An item, can be accessed by [`ModuleInformation::get`]
    Item(ItemId),
}

impl ModuleOrItem {
    /// Unwraps a module in this
    ///
    /// # Panics
    ///
    /// Panics if this is not a module
    pub fn unwrap_module(self) -> NodeId {
        match self {
            ModuleOrItem::Module(x) => x,
            ModuleOrItem::Item(_) => panic!(
                "Tried to unwrap a Module from a ModuleOrItem::Item: {:?}",
                self
            ),
        }
    }

    /// Unwraps a item in this
    ///
    /// # Panics
    ///
    /// Panics if this is not a item
    pub fn unwrap_item(self) -> ItemId {
        match self {
            ModuleOrItem::Item(x) => x,
            ModuleOrItem::Module(_) => panic!(
                "Tried to unwrap an Item from a ModuleOrItem::Module: {:?}",
                self
            ),
        }
    }
}

impl From<ItemId> for ModuleOrItem {
    fn from(them: ItemId) -> Self {
        Self::Item(them)
    }
}
impl From<NodeId> for ModuleOrItem {
    fn from(them: NodeId) -> Self {
        Self::Module(them)
    }
}

/// An error occuring when resolving a path
/// with [`ModuleInformation::path`]
#[derive(thiserror::Error, Debug)]
pub enum ResolveError {
    /// The item the path points to does not exist,
    ///
    /// ie `std::collections::Bashmap`
    #[error("path not found")]
    NotFound,
    /// AN item is prematurely reached in the path
    ///
    /// ie `std::collections::HashMap::mything`
    // TODO: This should not be an error
    #[error("premature non module item")]
    PrematureItem,
    /// An item is found at the root
    ///
    /// ie `::Item` being a struct not a module
    #[error("items should not exist from the root in rust.")]
    RootItem,
    /// Conflicting imports
    ///
    /// ie:
    ///
    /// ```rust
    /// use a::*;
    /// use b::*;
    ///
    /// mod a {
    ///     pub struct Foo;
    /// }
    ///
    /// mod b {
    ///     pub struct Foo;
    /// }
    ///
    /// // Could refer to `a::Foo` or `b::Foo`
    /// Foo
    /// ```
    #[error("conflicting imports")]
    Conflicting,
    /// When the supers go out of the crate
    ///
    /// ie `std::super::super`
    #[error("too many supers!")]
    TooManySupers,
}