wowlab_engine/
composition.rs1use wowlab_engine_ports::{ContentCatalog, ContentCatalogError, content_catalog};
4
5#[derive(Clone, Copy, Debug)]
7#[doc(alias = "simulation engine", alias = "WoW simulator")]
8pub struct EngineComposition {
9 catalog: &'static ContentCatalog,
10}
11
12impl EngineComposition {
13 pub fn initialize() -> Result<Self, ContentCatalogError> {
18 let linked_specs = wowlab_engine_content::force_link_generated_specs();
19
20 std::hint::black_box(linked_specs);
21
22 Self::from_catalog(content_catalog()?)
23 }
24
25 fn from_catalog(catalog: &'static ContentCatalog) -> Result<Self, ContentCatalogError> {
26 catalog.validate()?;
27
28 Ok(Self { catalog })
29 }
30
31 #[must_use]
33 pub const fn catalog(self) -> &'static ContentCatalog {
34 self.catalog
35 }
36
37 #[must_use]
39 pub const fn build_metadata(self) -> EngineBuildMetadata {
40 EngineBuildMetadata::CURRENT
41 }
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub struct EngineBuildMetadata {
47 version: &'static str,
48 git_hash: &'static str,
49}
50
51impl EngineBuildMetadata {
52 pub const CURRENT: Self = Self {
54 version: env!("CARGO_PKG_VERSION"),
55 git_hash: env!("GIT_HASH"),
56 };
57
58 #[must_use]
60 pub const fn version(self) -> &'static str {
61 self.version
62 }
63
64 #[must_use]
66 pub const fn git_hash(self) -> &'static str {
67 self.git_hash
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use std::error::Error as _;
74
75 use googletest::prelude::*;
76 use wowlab_engine_ports::ContentCatalog;
77
78 use super::{EngineBuildMetadata, EngineComposition};
79
80 static EMPTY_CATALOG: ContentCatalog = ContentCatalog::new(&[], &[], &[]);
81 const EMPTY_CATALOG_MESSAGE: &str = "engine-content catalog contains no spec descriptors";
83
84 #[gtest]
85 fn initialization_exposes_the_single_validated_catalog() -> Result<()> {
86 let composition = EngineComposition::initialize().or_fail()?;
87
88 verify_that!(composition.catalog().descriptors().len(), eq(27))?;
89 verify_true!(std::ptr::eq(
90 composition.catalog(),
91 EngineComposition::initialize().or_fail()?.catalog(),
92 ))?;
93
94 Ok(())
95 }
96
97 #[gtest]
98 fn invalid_catalog_returns_the_typed_error_without_erasing_it() -> Result<()> {
99 let error = EngineComposition::from_catalog(&EMPTY_CATALOG)
100 .err()
101 .or_fail()?;
102
103 verify_that!(error.to_string(), eq(EMPTY_CATALOG_MESSAGE))?;
104 verify_that!(
105 error.source().map(ToString::to_string).as_deref(),
106 eq(Some(EMPTY_CATALOG_MESSAGE))
107 )?;
108
109 Ok(())
110 }
111
112 #[gtest]
113 fn build_metadata_is_copyable_exact_and_nonempty() -> Result<()> {
114 let metadata = EngineBuildMetadata::CURRENT;
115
116 verify_that!(metadata, eq(metadata))?;
117 verify_that!(metadata.version(), eq(env!("CARGO_PKG_VERSION")))?;
118 verify_that!(metadata.git_hash(), eq(env!("GIT_HASH")))?;
119 verify_true!(!metadata.version().is_empty())?;
120 verify_true!(!metadata.git_hash().is_empty())?;
121
122 Ok(())
123 }
124}