use crate::metadata::token::Token;
use crossbeam_skiplist::SkipMap;
use std::sync::Arc;
mod builder;
mod loader;
mod owned;
mod raw;
mod reader;
mod writer;
pub use builder::*;
pub(crate) use loader::*;
pub use owned::*;
pub use raw::*;
pub type GenericParamMap = SkipMap<Token, GenericParamRc>;
pub type GenericParamList = Arc<boxcar::Vec<GenericParamRc>>;
pub type GenericParamRc = Arc<GenericParam>;
metadata_flags! {
pub struct GenericParamAttributes(u32);
}
impl GenericParamAttributes {
pub const VARIANCE_MASK: Self = Self(0x0003);
pub const COVARIANT: Self = Self(0x0001);
pub const CONTRAVARIANT: Self = Self(0x0002);
pub const SPECIAL_CONSTRAINT_MASK: Self = Self(0x003C);
pub const REFERENCE_TYPE_CONSTRAINT: Self = Self(0x0004);
pub const NOT_NULLABLE_VALUE_TYPE_CONSTRAINT: Self = Self(0x0008);
pub const DEFAULT_CONSTRUCTOR_CONSTRAINT: Self = Self(0x0010);
pub const ALLOW_BY_REF_LIKE: Self = Self(0x0020);
pub const RESERVED_MASK: Self = Self(0xFFC0);
#[inline]
#[must_use]
pub const fn variance(self) -> GenericParamVariance {
match self.0 & Self::VARIANCE_MASK.0 {
0x0001 => GenericParamVariance::Covariant,
0x0002 => GenericParamVariance::Contravariant,
_ => GenericParamVariance::Invariant,
}
}
#[inline]
#[must_use]
pub const fn special_constraint(self) -> Self {
Self(self.0 & Self::SPECIAL_CONSTRAINT_MASK.0)
}
#[must_use]
pub fn variance_keyword(self) -> &'static str {
match self.variance() {
GenericParamVariance::Covariant => "+",
GenericParamVariance::Contravariant => "-",
GenericParamVariance::Invariant => "",
}
}
#[must_use]
pub fn constraint_keywords(self) -> String {
let mut parts = Vec::new();
if self.contains(Self::REFERENCE_TYPE_CONSTRAINT) {
parts.push("class");
}
if self.contains(Self::NOT_NULLABLE_VALUE_TYPE_CONSTRAINT) {
parts.push("valuetype");
}
if self.contains(Self::DEFAULT_CONSTRUCTOR_CONSTRAINT) {
parts.push(".ctor");
}
if self.contains(Self::ALLOW_BY_REF_LIKE) {
parts.push("allow(byreflike)");
}
parts.join(" ")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GenericParamVariance {
Invariant,
Covariant,
Contravariant,
}
impl GenericParamVariance {
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
GenericParamVariance::Invariant => "invariant",
GenericParamVariance::Covariant => "covariant",
GenericParamVariance::Contravariant => "contravariant",
}
}
}
impl std::fmt::Display for GenericParamVariance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generic_param_attributes_variance() {
assert_eq!(
GenericParamAttributes::new(0).variance(),
GenericParamVariance::Invariant
);
assert_eq!(
GenericParamAttributes::COVARIANT.variance(),
GenericParamVariance::Covariant
);
assert_eq!(
GenericParamAttributes::CONTRAVARIANT.variance(),
GenericParamVariance::Contravariant
);
assert_eq!(
GenericParamAttributes::new(0x0003).variance(),
GenericParamVariance::Invariant
);
let with_constraints = GenericParamAttributes::COVARIANT
| GenericParamAttributes::REFERENCE_TYPE_CONSTRAINT
| GenericParamAttributes::DEFAULT_CONSTRUCTOR_CONSTRAINT;
assert_eq!(with_constraints.variance(), GenericParamVariance::Covariant);
}
#[test]
fn test_generic_param_variance_stable_strings() {
assert_eq!(GenericParamVariance::Invariant.as_str(), "invariant");
assert_eq!(GenericParamVariance::Covariant.as_str(), "covariant");
assert_eq!(
GenericParamVariance::Contravariant.as_str(),
"contravariant"
);
assert_eq!(format!("{}", GenericParamVariance::Invariant), "invariant");
assert_eq!(format!("{}", GenericParamVariance::Covariant), "covariant");
assert_eq!(
format!("{}", GenericParamVariance::Contravariant),
"contravariant"
);
}
#[test]
fn test_variance_keyword_after_enum_migration() {
assert_eq!(GenericParamAttributes::new(0).variance_keyword(), "");
assert_eq!(GenericParamAttributes::COVARIANT.variance_keyword(), "+");
assert_eq!(
GenericParamAttributes::CONTRAVARIANT.variance_keyword(),
"-"
);
}
}