dotnetdll 0.0.1

A framework for reading and writing .NET metadata files, such as C# library DLLs.
Documentation
use super::{attribute::Attribute, types, ResolvedDebug};
use crate::resolution::Resolution;
use std::borrow::Cow;

#[derive(Debug, Copy, Clone)]
pub enum Variance {
    Invariant,
    Covariant,
    Contravariant,
}

#[derive(Debug, Copy, Clone, Default)]
pub struct SpecialConstraint {
    pub reference_type: bool,
    pub value_type: bool,
    pub has_default_constructor: bool,
}
impl SpecialConstraint {
    pub fn is_empty(&self) -> bool {
        !(self.reference_type || self.value_type || self.has_default_constructor)
    }
}

#[derive(Debug, Clone)]
pub struct Constraint<'a, ConstraintType> {
    pub attributes: Vec<Attribute<'a>>,
    pub custom_modifiers: Vec<types::CustomTypeModifier>,
    pub constraint_type: ConstraintType,
}

#[derive(Debug, Clone)]
pub struct Generic<'a, ConstraintType> {
    pub attributes: Vec<Attribute<'a>>,
    pub name: Cow<'a, str>,
    pub variance: Variance,
    pub special_constraint: SpecialConstraint,
    pub type_constraints: Vec<Constraint<'a, ConstraintType>>,
}

impl<'a, T> Generic<'a, T> {
    pub fn new(name: impl Into<Cow<'a, str>>) -> Self {
        Self {
            attributes: vec![],
            name: name.into(),
            variance: Variance::Invariant,
            special_constraint: SpecialConstraint::default(),
            type_constraints: vec![],
        }
    }
}

pub type Type<'a> = Generic<'a, types::MemberType>;
pub type Method<'a> = Generic<'a, types::MethodType>;

impl<T: ResolvedDebug> ResolvedDebug for Vec<Generic<'_, T>> {
    fn show(&self, _: &Resolution) -> String {
        use std::fmt::Write;

        let mut buf = String::new();

        if !self.is_empty() {
            write!(
                buf,
                "<{}>",
                self.iter().map(|p| p.name.as_ref()).collect::<Vec<&str>>().join(", ")
            )
            .unwrap();
        }

        buf
    }
}

pub fn show_constraints<T: ResolvedDebug>(v: &[Generic<'_, T>], res: &Resolution) -> Option<String> {
    if v.iter()
        .any(|g| !(g.special_constraint.is_empty() && g.type_constraints.is_empty()))
    {
        Some(
            v.iter()
                .map(|g| {
                    let mut constraints = Vec::new();
                    if g.special_constraint.reference_type {
                        constraints.push("class".to_string());
                    }
                    if g.special_constraint.value_type {
                        constraints.push("struct".to_string());
                    }
                    if g.special_constraint.has_default_constructor {
                        constraints.push("new()".to_string());
                    }
                    constraints.extend(g.type_constraints.iter().map(|t| t.constraint_type.show(res)));

                    if constraints.is_empty() {
                        String::new()
                    } else {
                        format!("where {} : {}", g.name, constraints.join(", "))
                    }
                })
                .collect::<Vec<_>>()
                .join(" "),
        )
    } else {
        None
    }
}