workshop_rs/convert.rs
1//! Raw Workshop locale conversion: parse -> canonical semantics -> emit.
2//!
3//! [`convert`] parses raw Workshop text in a source locale into
4//! locale-independent WIR, then emits it in a target locale. Canonical
5//! identities are locale-independent; only the spellings change. Missing
6//! target-locale mappings fail explicitly by default (an error, never a
7//! guess and never a silent passthrough of another locale's spelling);
8//! fallback is opt-in via [`ConvertOptions`] and recorded in
9//! [`Conversion::fallback_ids`].
10
11use crate::catalog::{Catalog, Locale};
12use crate::emitter::{self, EmitOptions};
13use crate::error::Result;
14use crate::parser;
15use crate::signatures::ExpectedDomain;
16
17/// Conversion options: opt-in fallback for missing target-locale mappings.
18#[derive(Debug, Clone, Default, PartialEq, Eq)]
19pub struct ConvertOptions {
20 /// When a canonical identity has no spelling for the target locale, its
21 /// spelling in this declared locale is used instead. `None` (the
22 /// default) keeps missing mappings failing explicitly. The fallback
23 /// choice is visible in [`Conversion::fallback_ids`].
24 pub fallback_locale: Option<Locale>,
25}
26
27/// The result of a raw Workshop locale conversion.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct Conversion {
30 /// The converted localized Workshop text.
31 pub text: String,
32 /// Canonical identities (and the `settings` marker) whose spelling came
33 /// from the opt-in fallback locale instead of the target locale. Empty
34 /// when no fallback occurred.
35 pub fallback_ids: Vec<String>,
36}
37
38/// Convert raw Workshop text from `from` to `to`, parsing with the catalog as
39/// the canonical signature context (expected enum domains resolve ambiguous
40/// bare members that the catalog documents).
41pub fn convert(
42 input: &str,
43 catalog: &Catalog,
44 from: &Locale,
45 to: &Locale,
46 options: &ConvertOptions,
47) -> Result<Conversion> {
48 convert_with_context(input, catalog, from, to, options, catalog)
49}
50
51/// The context-aware form of [`convert`]: `context` supplies the expected
52/// enum domains for argument positions the catalog does not document (e.g. a
53/// provider manifest chained with the catalog via
54/// [`crate::signatures::ChainedExpectedDomain`]).
55pub fn convert_with_context(
56 input: &str,
57 catalog: &Catalog,
58 from: &Locale,
59 to: &Locale,
60 options: &ConvertOptions,
61 context: &dyn ExpectedDomain,
62) -> Result<Conversion> {
63 let program = parser::parse_with_context(input, catalog, from, context)?;
64 let emit_options = EmitOptions {
65 fallback_locale: options.fallback_locale.clone(),
66 };
67 let output = emitter::emit_with_options_for_conversion(&program, catalog, to, &emit_options)?;
68 Ok(Conversion {
69 text: output.text,
70 fallback_ids: output.fallback_ids,
71 })
72}