From c706803eeeb8fa8e5b2457e2620ceb92f3835f7d Mon Sep 17 00:00:00 2001 From: Teakowa Date: Mon, 19 Oct 2026 11:21:08 +0800 Subject: [PATCH 1/2] refactor: share workshop-rs's built-in catalog cache workshop-rs 1.17.0 exposes Catalog::builtin_ref(), which parses and indexes the built-in catalog once per process. Drop opy-rs's private OnceLock wrapper in enums.rs, store a &'static Catalog in CompilerContract, and borrow the shared catalog in the opy-compat binary instead of cloning it per run. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- crates/opy-cli/src/bin/opy-compat.rs | 6 +++--- crates/opy-cli/src/bin/opy-compat/probe.rs | 12 ++++++------ crates/opy-rs/src/compiler/integration.rs | 6 +++--- crates/opy-rs/src/compiler/mod.rs | 2 +- crates/opy-rs/src/compiler/reconstruct.rs | 2 +- crates/opy-rs/src/enums.rs | 12 ------------ crates/opy-rs/src/lookup.rs | 6 +++--- crates/opy-rs/src/lower.rs | 6 +++--- 10 files changed, 23 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0edf00a..c91324bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,9 +717,9 @@ dependencies = [ [[package]] name = "workshop-rs" -version = "1.16.9" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9639b6b427daf88774ac33acc15574a7614c7502d70162e3a6e361cf89e88b" +checksum = "48d21f920ec376c50c960878e1d5b2775bdef6901d6375cfde098a5b52ab340c" dependencies = [ "aho-corasick", "serde", diff --git a/Cargo.toml b/Cargo.toml index f2b8aa12..99d14af9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ libquickjs-ng-sys = "0.13.0" serde = "1" serde_json = "1" sha2 = "0.11" -workshop-rs = "^1.13.0" +workshop-rs = "1.17.0" [workspace.lints.rust] unsafe_op_in_unsafe_fn = "deny" diff --git a/crates/opy-cli/src/bin/opy-compat.rs b/crates/opy-cli/src/bin/opy-compat.rs index d4dd6556..d49147eb 100644 --- a/crates/opy-cli/src/bin/opy-compat.rs +++ b/crates/opy-cli/src/bin/opy-compat.rs @@ -219,12 +219,12 @@ fn compile_and_parse( &Locale::new("en-US"), ) .map_err(|error| error.to_string())?; - let catalog = Catalog::builtin().map_err(|error| error.to_string())?; - let context = CompatibilityExpectedDomain { catalog: &catalog }; + let catalog = Catalog::builtin_ref().map_err(|error| error.to_string())?; + let context = CompatibilityExpectedDomain { catalog }; let parse = |text: &str| { workshop_rs::parser::parse_with_context( &strip_workshop_comments(text), - &catalog, + catalog, &Locale::new("en-US"), &context, ) diff --git a/crates/opy-cli/src/bin/opy-compat/probe.rs b/crates/opy-cli/src/bin/opy-compat/probe.rs index dd37d494..3704fae6 100644 --- a/crates/opy-cli/src/bin/opy-compat/probe.rs +++ b/crates/opy-cli/src/bin/opy-compat/probe.rs @@ -152,10 +152,10 @@ struct Reference { pub(super) fn generate() -> Result<(), String> { let manifest = Manifest::builtin().map_err(|error| error.to_string())?; - let catalog = Catalog::builtin().map_err(|error| error.to_string())?; + let catalog = Catalog::builtin_ref().map_err(|error| error.to_string())?; let mut probes = Vec::new(); for function in manifest.functions() { - let calls = calls(&catalog, function); + let calls = calls(catalog, function); if calls.is_empty() { // Functions without a valid sample call would otherwise never // reach the oracle; a name probe still verifies the declared @@ -205,7 +205,7 @@ pub(super) fn generate() -> Result<(), String> { // Every enum-member spelling the vocabulary lookup reports is an // accepted OPY spelling by contract; probe each against the oracle so a // catalog id or other upstream-rejected spelling cannot be advertised. - for (domain, spelling) in enum_spellings(manifest, &catalog) { + for (domain, spelling) in enum_spellings(manifest, catalog) { probes.push(Probe { id: format!("default:{domain}.{spelling}:spelling"), source: source("", "eachPlayer", &format!("g = {domain}.{spelling}")), @@ -462,13 +462,13 @@ pub(super) fn compare(probes: &Path, references: &Path) -> Result<(), String> { let references: BTreeMap = serde_json::from_str(&read(references)?) .map_err(|error| format!("cannot parse references: {error}"))?; let compiler = Compiler::new().map_err(|error| error.to_string())?; - let catalog = Catalog::builtin().map_err(|error| error.to_string())?; - let context = CompatibilityExpectedDomain { catalog: &catalog }; + let catalog = Catalog::builtin_ref().map_err(|error| error.to_string())?; + let context = CompatibilityExpectedDomain { catalog }; let locale = Locale::new("en-US"); let parse = |text: &str| { workshop_rs::parser::parse_with_context( &strip_workshop_comments(text), - &catalog, + catalog, &locale, &context, ) diff --git a/crates/opy-rs/src/compiler/integration.rs b/crates/opy-rs/src/compiler/integration.rs index b6109eba..4f69ef00 100644 --- a/crates/opy-rs/src/compiler/integration.rs +++ b/crates/opy-rs/src/compiler/integration.rs @@ -11,7 +11,7 @@ pub struct LinkReport { } pub(crate) struct CompilerContract { - pub(super) catalog: Catalog, + pub(super) catalog: &'static Catalog, pub(super) manifest: &'static Manifest, pub(super) links: LinkReport, } @@ -95,11 +95,11 @@ pub(crate) fn cross_check_manifest( pub(crate) fn load_compiler_contract() -> Result<&'static CompilerContract, IntegrationError> { COMPILER_CONTRACT .get_or_init(|| { - let catalog = Catalog::builtin() + let catalog = Catalog::builtin_ref() .map_err(|error| IntegrationError::new("catalog-load", error.to_string(), None))?; let manifest = Manifest::builtin() .map_err(|error| IntegrationError::new("manifest-load", error.to_string(), None))?; - let links = cross_check_manifest(manifest, &catalog)?; + let links = cross_check_manifest(manifest, catalog)?; Ok(CompilerContract { catalog, manifest, diff --git a/crates/opy-rs/src/compiler/mod.rs b/crates/opy-rs/src/compiler/mod.rs index e74de399..e971dbaf 100644 --- a/crates/opy-rs/src/compiler/mod.rs +++ b/crates/opy-rs/src/compiler/mod.rs @@ -458,7 +458,7 @@ impl Compiler { pub fn new() -> Result { let contract = load_compiler_contract()?; Ok(Self { - catalog: &contract.catalog, + catalog: contract.catalog, manifest: contract.manifest, links: contract.links, }) diff --git a/crates/opy-rs/src/compiler/reconstruct.rs b/crates/opy-rs/src/compiler/reconstruct.rs index fcdf5c98..ff0efbb1 100644 --- a/crates/opy-rs/src/compiler/reconstruct.rs +++ b/crates/opy-rs/src/compiler/reconstruct.rs @@ -63,7 +63,7 @@ pub fn reconstruct(program: &Program) -> Result { }); } }; - let catalog = match crate::enums::builtin_catalog() { + let catalog = match Catalog::builtin_ref() { Ok(catalog) => catalog, Err(error) => { return Err(ReconstructError { diff --git a/crates/opy-rs/src/enums.rs b/crates/opy-rs/src/enums.rs index 12cda621..4d16bb9b 100644 --- a/crates/opy-rs/src/enums.rs +++ b/crates/opy-rs/src/enums.rs @@ -18,18 +18,6 @@ pub(crate) fn en_us() -> Locale { Locale::new("en-US") } -/// The bundled Workshop catalog, loaded once — `Catalog::builtin` -/// reparses the data on every call, which a per-id `resolveIds` request or -/// repeated `check`/`compile` runs cannot afford. The error is the load -/// failure's message. -pub(crate) fn builtin_catalog() -> Result<&'static Catalog, &'static str> { - static CATALOG: std::sync::OnceLock> = std::sync::OnceLock::new(); - CATALOG - .get_or_init(|| Catalog::builtin().map_err(|error| error.to_string())) - .as_ref() - .map_err(String::as_str) -} - /// The OPY source domain spellings that rename a canonical catalog domain /// whose own name is not a reference source spelling: upstream's enum is /// named `Clip`, so `Clipping.DO_NOT_CLIP` does not compile upstream even diff --git a/crates/opy-rs/src/lookup.rs b/crates/opy-rs/src/lookup.rs index 158c4605..35382da6 100644 --- a/crates/opy-rs/src/lookup.rs +++ b/crates/opy-rs/src/lookup.rs @@ -479,7 +479,7 @@ pub fn lookup(query: &LookupQuery) -> LookupOutcome { ); } } - let Ok(catalog) = crate::enums::builtin_catalog() else { + let Ok(catalog) = Catalog::builtin_ref() else { return unsupported("vocabulary data (manifest or catalog) is not bundled"); }; let Ok(manifest) = Manifest::builtin() else { @@ -614,7 +614,7 @@ const SYNTAX_SPELLINGS: &[(&str, &str)] = &[ /// unknown. pub fn call_spelling(canonical_id: &str) -> Option<&'static str> { let manifest = Manifest::builtin().ok()?; - let catalog = crate::enums::builtin_catalog().ok()?; + let catalog = Catalog::builtin_ref().ok()?; // A manifest entry's canonical identity is its `catalogId`, or — when // it declares none — its own `id` when that id is a canonical call // (`startRule`); a `catalogId`-free source name that is not a canonical @@ -648,7 +648,7 @@ pub fn call_spelling(canonical_id: &str) -> Option<&'static str> { /// and members the reference does not spell in source, and for OPY-only /// members with no canonical id. pub fn enum_member_spelling<'a>(domain: &'a str, member: &'a str) -> Option<&'a str> { - let catalog = crate::enums::builtin_catalog().ok()?; + let catalog = Catalog::builtin_ref().ok()?; let enum_domain = catalog.enum_domain(domain)?; if !enum_domain .members diff --git a/crates/opy-rs/src/lower.rs b/crates/opy-rs/src/lower.rs index db102a9f..ef5c278c 100644 --- a/crates/opy-rs/src/lower.rs +++ b/crates/opy-rs/src/lower.rs @@ -168,7 +168,7 @@ pub fn lower_with_preprocessing( )); } }; - let catalog = crate::enums::builtin_catalog().map_err(|error| { + let catalog = Catalog::builtin_ref().map_err(|error| { OpyError::new( "catalog-error", format!("cannot load the Workshop catalog: {error}"), @@ -468,7 +468,7 @@ impl SettingsLowerer { format!("cannot load the OPY semantic compatibility manifest: {error}"), ) })?; - let catalog = crate::enums::builtin_catalog().map_err(|error| { + let catalog = Catalog::builtin_ref().map_err(|error| { OpyError::new( "catalog-error", format!("cannot load the Workshop catalog: {error}"), @@ -2308,7 +2308,7 @@ mod tests { vec![], &PreprocessingState::default(), manifest, - crate::enums::builtin_catalog().expect("the builtin catalog loads"), + Catalog::builtin_ref().expect("the builtin catalog loads"), ) } From 24a9c400fe98ce41bced3efdddd8fd8396c132ad Mon Sep 17 00:00:00 2001 From: Teakowa Date: Mon, 19 Oct 2026 13:02:38 +0800 Subject: [PATCH 2/2] refactor: share workshop-rs's built-in gameplay catalog cache workshop-rs 1.18.0 exposes gameplay::data::builtin_ref(), which loads and validates the checked-in gameplay dataset once per process. Drop opy-rs's private OnceLock in settings.rs and borrow the shared catalog instead of caching a clone. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- crates/opy-rs/src/compiler/settings.rs | 6 +----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c91324bd..bd824e75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,9 +717,9 @@ dependencies = [ [[package]] name = "workshop-rs" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d21f920ec376c50c960878e1d5b2775bdef6901d6375cfde098a5b52ab340c" +checksum = "f47ea4c984fe44f18f636a1b35fa4ecf0767ba64dfdcf2fd199e739c4d2135c3" dependencies = [ "aho-corasick", "serde", diff --git a/Cargo.toml b/Cargo.toml index 99d14af9..3d3511c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ libquickjs-ng-sys = "0.13.0" serde = "1" serde_json = "1" sha2 = "0.11" -workshop-rs = "1.17.0" +workshop-rs = "1.18.0" [workspace.lints.rust] unsafe_op_in_unsafe_fn = "deny" diff --git a/crates/opy-rs/src/compiler/settings.rs b/crates/opy-rs/src/compiler/settings.rs index d2c378bf..a51aeeb5 100644 --- a/crates/opy-rs/src/compiler/settings.rs +++ b/crates/opy-rs/src/compiler/settings.rs @@ -1094,11 +1094,7 @@ fn workshop_hero_name(text: &str, locale: &Locale) -> Option { } fn gameplay_catalog() -> &'static workshop_rs::gameplay::GameplayCatalog { - static CATALOG: std::sync::OnceLock = - std::sync::OnceLock::new(); - CATALOG.get_or_init(|| { - workshop_rs::gameplay::data::builtin().expect("builtin gameplay catalog parses") - }) + workshop_rs::gameplay::data::builtin_ref().expect("builtin gameplay catalog parses") } fn convert_settings_span(span: HirSpan) -> WorkshopSpan {