use indexmap::IndexMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Version {
pub major: u32,
pub minor: u32,
}
impl Version {
pub fn new(major: u32, minor: u32) -> Self {
Self { major, minor }
}
pub fn packed(&self) -> u16 {
((self.major as u16) << 8) | (self.minor as u16)
}
}
impl PartialOrd for Version {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Version {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.packed().cmp(&other.packed())
}
}
impl std::fmt::Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.major, self.minor)
}
}
#[derive(Debug, Clone)]
pub struct RawType {
pub name: String,
pub api: Option<String>,
pub category: String,
pub requires: Option<String>,
pub alias: Option<String>,
pub bitwidth: Option<u32>,
pub raw_c: String,
pub protect: Option<String>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct RawEnum {
pub name: String,
pub value: Option<String>,
pub api: Option<String>,
pub type_suffix: Option<String>,
pub alias: Option<String>,
pub comment: String,
pub parent_type: Option<String>,
}
#[derive(Debug, Clone)]
pub struct RawEnumGroup {
pub name: String,
pub bitwidth: Option<u32>,
pub values: Vec<RawEnum>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct RawParam {
pub name: String,
pub type_raw: String,
pub type_name: String,
pub api: Option<String>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct RawCommand {
pub name: String,
pub api: Option<String>,
pub return_type: String,
pub params: Vec<RawParam>,
pub alias: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommandScope {
Unknown,
Global,
Instance,
Device,
}
impl CommandScope {
pub fn c_name(self) -> &'static str {
match self {
Self::Unknown => "GloamCommandScopeUnknown",
Self::Global => "GloamCommandScopeGlobal",
Self::Instance => "GloamCommandScopeInstance",
Self::Device => "GloamCommandScopeDevice",
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Require {
pub api: Option<String>,
pub profile: Option<String>,
pub types: Vec<String>,
pub enums: Vec<String>,
pub commands: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct Remove {
pub profile: Option<String>,
pub commands: Vec<String>,
pub enums: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct RawFeature {
pub name: String,
pub api: String,
pub version: Version,
pub requires: Vec<Require>,
pub removes: Vec<Remove>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct RawExtension {
pub name: String,
pub supported: Vec<String>,
pub requires: Vec<Require>,
pub protect: Vec<String>,
pub number: Option<u32>,
}
#[derive(Debug)]
pub struct RawSpec {
pub spec_name: String,
#[allow(dead_code)]
pub platforms: IndexMap<String, String>,
pub types: Vec<RawType>,
pub enum_groups: Vec<RawEnumGroup>,
pub flat_enums: IndexMap<String, RawEnum>,
pub commands: IndexMap<String, RawCommand>,
pub features: Vec<RawFeature>,
pub extensions: Vec<RawExtension>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_ordering() {
assert!(Version::new(3, 3) > Version::new(3, 2));
assert!(Version::new(4, 0) > Version::new(3, 9));
assert!(Version::new(1, 1) > Version::new(1, 0));
assert_eq!(Version::new(2, 0), Version::new(2, 0));
}
#[test]
fn version_packed_encodes_correctly() {
assert_eq!(Version::new(3, 3).packed(), 0x0303);
assert_eq!(Version::new(1, 0).packed(), 0x0100);
assert_eq!(Version::new(4, 6).packed(), 0x0406);
}
#[test]
fn version_packed_ordering_matches_semantic_ordering() {
let pairs = [
(Version::new(1, 0), Version::new(1, 1)),
(Version::new(3, 2), Version::new(3, 3)),
(Version::new(3, 9), Version::new(4, 0)),
];
for (a, b) in &pairs {
assert!(a.packed() < b.packed(), "{a} < {b}");
}
}
#[test]
fn version_display() {
assert_eq!(Version::new(3, 3).to_string(), "3.3");
assert_eq!(Version::new(1, 0).to_string(), "1.0");
}
}