Move interaction constructors from individual types to implementations on `InteractionEnvironment`

finchie
Aug 8, 2025, 4:49 AM
7YOM2QEFZ5HWVEISP3VIR2GKF2NNH4KKTLTWEMYMMWNYKICXWTZAC

Dependencies

  • [2] 5P3O2D3G Enable 'steady tick' on progress bars in `fluent_embed_interaction`
  • [3] KFFAQIZU Rename `InteractionEnvironment::print_message` to `emit_message`
  • [4] RUCC2HKZ Rename from `fluent_embed` to `l10n_embed`
  • [5] JUV7C6ET Create initial prototype of `fluent_embed_interaction`
  • [6] U2PHMYPD Return `String` directly instead of writing to buffer in `Localize::localize`
  • [7] NEBSVXIA Apply Clippy fixes
  • [8] QJC4IQIT Refactor `Localize` functions to infallibly return `String`
  • [9] NB7K77TZ Update formatting of `InteractionEnvironment::new()`
  • [10] BAH2JCJP Add progress bar to `fluent_embed_interaction`
  • [11] 3BUFFCHQ Return `Option<T>` instead of `T` from `interact()`
  • [12] IXBE5Q6T Implement `InteractionEnvironment::print_message`
  • [*] UKFEFT6L Create basic `Output` proc-macro

Change contents

  • edit in Cargo.lock at line 843
    [4.3564]
    [4.3564]
    "icu_locale",
  • replacement in l10n_embed_interaction/src/prompt/select.rs at line 1
    [5.109][5.110:178]()
    use super::macros::{impl_new, impl_with_default, impl_with_prompt};
    [5.109]
    [5.0]
    use super::macros::{impl_with_default, impl_with_prompt};
  • replacement in l10n_embed_interaction/src/prompt/select.rs at line 5
    [5.308][5.308:347]()
    #[derive(Default)]
    pub struct Select {
    [5.308]
    [5.347]
    pub struct Select<'environment> {
    environment: &'environment InteractionEnvironment,
  • edit in l10n_embed_interaction/src/prompt/select.rs at line 12
    [5.438][5.438:457]()
    impl_new!(Select);
  • replacement in l10n_embed_interaction/src/prompt/select.rs at line 15
    [5.520][5.520:534]()
    impl Select {
    [5.520]
    [5.29]
    impl<'environment> Select<'environment> {
    pub(crate) fn new_from_environment(environment: &'environment InteractionEnvironment) -> Self {
    Self {
    environment,
    default: None,
    items: None,
    prompt: None,
    }
    }
  • replacement in l10n_embed_interaction/src/prompt/select.rs at line 28
    [5.685][5.0:37]()
    .map(Localize::localize)
    [5.685]
    [5.98]
    .map(|item| item.message_for_locale(&self.environment.locale))
  • replacement in l10n_embed_interaction/src/prompt/select.rs at line 35
    [5.1149][5.56:188](),[5.188][5.1250:1286](),[5.1250][5.1250:1286]()
    pub fn interact(
    self,
    environment: &InteractionEnvironment,
    ) -> Result<Option<usize>, InteractionError> {
    match environment.context {
    [5.1149]
    [5.1286]
    pub fn interact(self) -> Result<Option<usize>, InteractionError> {
    match self.environment.context {
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 1
    [5.2012][5.2013:2083]()
    use super::macros::{impl_new, impl_with_prompt, impl_with_validator};
    [5.2012]
    [5.303]
    use super::macros::{impl_with_prompt, impl_with_validator};
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 10
    [5.2294][5.2294:2335]()
    #[derive(Default)]
    pub struct Password {
    [5.2294]
    [5.2335]
    pub struct Password<'environment> {
    environment: &'environment InteractionEnvironment,
  • edit in l10n_embed_interaction/src/prompt/password.rs at line 17
    [5.2481][5.2481:2502]()
    impl_new!(Password);
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 20
    [5.2564][5.2564:2580]()
    impl Password {
    [5.2564]
    [5.2580]
    impl<'environment> Password<'environment> {
    pub(crate) fn new_from_environment(environment: &'environment InteractionEnvironment) -> Self {
    Self {
    environment,
    confirmation: None,
    prompt: None,
    validator: None,
    }
    }
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 35
    [5.197][5.197:315]()
    let confirmation_text = confirmation.localize();
    let mismatch_error_text = mismatch_error.localize();
    [5.197]
    [5.2895]
    let confirmation_text = confirmation.message_for_locale(&self.environment.locale);
    let mismatch_error_text = mismatch_error.message_for_locale(&self.environment.locale);
  • replacement in l10n_embed_interaction/src/prompt/password.rs at line 46
    [5.3224][5.3224:3305](),[5.3305][5.359:411](),[5.411][5.3349:3385](),[5.3349][5.3349:3385]()
    pub fn interact(
    self,
    environment: &InteractionEnvironment,
    ) -> Result<Option<String>, InteractionError> {
    match environment.context {
    [5.3224]
    [5.3385]
    pub fn interact(self) -> Result<Option<String>, InteractionError> {
    match self.environment.context {
  • edit in l10n_embed_interaction/src/prompt/macros.rs at line 1
    [5.4406][5.4407:4481](),[5.4481][5.0:47](),[5.47][5.4516:4582](),[5.4516][5.4516:4582]()
    macro_rules! impl_new {
    ($newtype:ident) => {
    impl $newtype {
    #[must_use] pub fn new() -> Self {
    Self::default()
    }
    }
    };
    }
  • replacement in l10n_embed_interaction/src/prompt/macros.rs at line 3
    [5.4657][5.4657:4681](),[5.4681][5.48:140]()
    impl $newtype {
    #[must_use] pub const fn with_default(mut self, default: $field_type) -> Self {
    [5.4657]
    [5.4755]
    impl<'environment> $newtype<'environment> {
    #[must_use]
    pub const fn with_default(mut self, default: $field_type) -> Self {
  • replacement in l10n_embed_interaction/src/prompt/macros.rs at line 15
    [5.4914][5.4914:4938]()
    impl $newtype {
    [5.4914]
    [4.88]
    impl<'environment> $newtype<'environment> {
  • replacement in l10n_embed_interaction/src/prompt/macros.rs at line 17
    [4.175][5.419:475](),[5.419][5.419:475]()
    let localized_text = prompt.localize();
    [4.175]
    [5.5244]
    let localized_text = prompt.message_for_locale(&self.environment.locale);
  • replacement in l10n_embed_interaction/src/prompt/macros.rs at line 28
    [5.5417][5.5417:5441]()
    impl $newtype {
    [5.5417]
    [5.498]
    impl<'environment> $newtype<'environment> {
  • edit in l10n_embed_interaction/src/prompt/macros.rs at line 36
    [5.5612]
    [5.5612]
    let locale = self.environment.locale.clone();
  • replacement in l10n_embed_interaction/src/prompt/macros.rs at line 42
    [5.5892][5.651:703]()
    Err(message.localize())
    [5.5892]
    [5.6188]
    Err(message.message_for_locale(&locale))
  • replacement in l10n_embed_interaction/src/prompt/macros.rs at line 54
    [5.6369][5.6369:6454]()
    pub(crate) use {impl_new, impl_with_default, impl_with_prompt, impl_with_validator};
    [5.6369]
    pub(crate) use {impl_with_default, impl_with_prompt, impl_with_validator};
  • replacement in l10n_embed_interaction/src/prompt/input.rs at line 1
    [5.6488][5.6489:6559]()
    use super::macros::{impl_new, impl_with_prompt, impl_with_validator};
    [5.6488]
    [5.526]
    use super::macros::{impl_with_prompt, impl_with_validator};
  • replacement in l10n_embed_interaction/src/prompt/input.rs at line 5
    [5.6689][5.6689:6727]()
    #[derive(Default)]
    pub struct Input {
    [5.6689]
    [5.6727]
    pub struct Input<'environment> {
    environment: &'environment InteractionEnvironment,
  • edit in l10n_embed_interaction/src/prompt/input.rs at line 12
    [5.6854][5.6854:6872]()
    impl_new!(Input);
  • replacement in l10n_embed_interaction/src/prompt/input.rs at line 15
    [5.6928][5.6928:6941]()
    impl Input {
    [5.6928]
    [5.733]
    impl<'environment> Input<'environment> {
    pub(crate) fn new_from_environment(environment: &'environment InteractionEnvironment) -> Self {
    Self {
    environment,
    default: None,
    prompt: None,
    validator: None,
    }
    }
  • replacement in l10n_embed_interaction/src/prompt/input.rs at line 26
    [5.802][5.802:851]()
    let localized_text = default.localize();
    [5.802]
    [5.7171]
    let localized_text = default.message_for_locale(&self.environment.locale);
  • replacement in l10n_embed_interaction/src/prompt/input.rs at line 32
    [5.7241][5.7241:7322](),[5.7322][5.582:634](),[5.634][5.7366:7402](),[5.7366][5.7366:7402]()
    pub fn interact(
    self,
    environment: &InteractionEnvironment,
    ) -> Result<Option<String>, InteractionError> {
    match environment.context {
    [5.7241]
    [5.7402]
    pub fn interact(self) -> Result<Option<String>, InteractionError> {
    match self.environment.context {
  • replacement in l10n_embed_interaction/src/prompt/confirm.rs at line 1
    [5.8140][5.8141:8209]()
    use super::macros::{impl_new, impl_with_default, impl_with_prompt};
    [5.8140]
    [5.749]
    use super::macros::{impl_with_default, impl_with_prompt};
  • replacement in l10n_embed_interaction/src/prompt/confirm.rs at line 4
    [5.8339][5.8339:8379]()
    #[derive(Default)]
    pub struct Confirm {
    [5.8339]
    [5.8379]
    pub struct Confirm<'environment> {
    environment: &'environment InteractionEnvironment,
  • edit in l10n_embed_interaction/src/prompt/confirm.rs at line 10
    [5.8437][5.8437:8457]()
    impl_new!(Confirm);
  • replacement in l10n_embed_interaction/src/prompt/confirm.rs at line 13
    [5.8521][5.8521:8536](),[5.8536][5.805:936](),[5.936][5.8636:8672](),[5.8636][5.8636:8672]()
    impl Confirm {
    pub fn interact(
    self,
    environment: &InteractionEnvironment,
    ) -> Result<Option<bool>, InteractionError> {
    match environment.context {
    [5.8521]
    [5.8672]
    impl<'environment> Confirm<'environment> {
    pub(crate) fn new_from_environment(environment: &'environment InteractionEnvironment) -> Self {
    Self {
    environment,
    default: None,
    prompt: None,
    }
    }
    pub fn interact(self) -> Result<Option<bool>, InteractionError> {
    match self.environment.context {
  • edit in l10n_embed_interaction/src/progress.rs at line 2
    [5.549][4.245:271]()
    use l10n_embed::Localize;
  • edit in l10n_embed_interaction/src/progress.rs at line 3
    [5.628]
    [5.628]
    use l10n_embed::Localize;
  • replacement in l10n_embed_interaction/src/progress.rs at line 17
    [5.938][5.938:1033](),[5.1033][2.26:140]()
    impl ProgressBar {
    pub fn new(environment: &InteractionEnvironment, length: u64) -> Self {
    let progress_bar =
    indicatif::ProgressBar::new(length).with_style(PROGRESS_TEMPLATE.clone());
    [5.938]
    [2.140]
    impl<'environment> ProgressBar {
    pub(crate) fn new_from_environment<L: Localize>(
    environment: &'environment InteractionEnvironment,
    message: L,
    length: u64,
    ) -> Self {
    let localized_text = message.message_for_locale(&environment.locale);
    let progress_bar = indicatif::ProgressBar::new(length)
    .with_style(PROGRESS_TEMPLATE.clone())
    .with_message(localized_text);
  • edit in l10n_embed_interaction/src/progress.rs at line 33
    [5.1224][5.1224:1231](),[5.1231][5.895:964](),[5.964][5.964:1013](),[5.380][5.1461:1538](),[5.1013][5.1461:1538](),[5.1461][5.1461:1538](),[5.1538][5.1014:1027]()
    }
    pub fn with_message<L: Localize>(mut self, message: L) -> Self {
    let localized_text = message.localize();
    self.progress_bar = self.progress_bar.with_message(localized_text);
    self
  • edit in l10n_embed_interaction/src/lib.rs at line 5
    [5.9295]
    [4.272]
    use icu_locale::Locale;
    use indicatif::MultiProgress;
  • edit in l10n_embed_interaction/src/lib.rs at line 8
    [5.28][5.1735:1765](),[4.298][5.1735:1765](),[5.9295][5.1735:1765]()
    use indicatif::MultiProgress;
  • edit in l10n_embed_interaction/src/lib.rs at line 35
    [5.10055]
    [5.1850]
    locale: Locale,
  • replacement in l10n_embed_interaction/src/lib.rs at line 41
    [5.45][5.45:89]()
    pub fn new(interactive: bool) -> Self {
    [5.45]
    [5.10132]
    pub fn new(locale: Locale, interactive: bool) -> Self {
  • edit in l10n_embed_interaction/src/lib.rs at line 47
    [5.261]
    [5.1885]
    locale,
  • replacement in l10n_embed_interaction/src/lib.rs at line 53
    [3.88][5.96:151](),[5.96][5.96:151]()
    self.progress_bars.println(message.localize())
    [3.88]
    [5.10328]
    self.progress_bars
    .println(message.message_for_locale(&self.locale))
    }
    }
    macro_rules! impl_constructor {
    ($prompt_name:ident, $prompt_type:ident) => {
    impl InteractionEnvironment {
    #[must_use]
    pub fn $prompt_name<'environment>(&'environment self) -> $prompt_type<'environment> {
    $prompt_type::new_from_environment(self)
    }
    }
    };
    }
    // Create constructors on InteractionEnvironment for different interaction types
    impl_constructor!(new_confirm, Confirm);
    impl_constructor!(new_editor, Editor);
    impl_constructor!(new_input, Input);
    impl_constructor!(new_password, Password);
    impl_constructor!(new_select, Select);
    // Special implementation for progress bars as the signature is different
    impl InteractionEnvironment {
    pub fn new_progress<'environment, L: Localize>(
    &'environment self,
    message: L,
    length: u64,
    ) -> ProgressBar {
    ProgressBar::new_from_environment(self, message, length)
  • replacement in l10n_embed_interaction/src/editor.rs at line 3
    [5.10480][5.10480:10500]()
    pub struct Editor {
    [5.10480]
    [5.10500]
    pub struct Editor<'environment> {
    environment: &'environment InteractionEnvironment,
  • replacement in l10n_embed_interaction/src/editor.rs at line 8
    [5.377][5.377:450]()
    impl Default for Editor {
    fn default() -> Self {
    Self::new()
    [5.377]
    [5.450]
    impl<'environment> Editor<'environment> {
    pub(crate) fn new_from_environment(environment: &'environment InteractionEnvironment) -> Self {
    Self {
    environment,
    extension: None,
    }
  • edit in l10n_embed_interaction/src/editor.rs at line 15
    [5.456][5.10531:10533](),[5.10531][5.10531:10533]()
    }
  • edit in l10n_embed_interaction/src/editor.rs at line 16
    [5.10534][5.10534:10548]()
    impl Editor {
  • edit in l10n_embed_interaction/src/editor.rs at line 17
    [5.1143][5.1143:1176](),[5.502][5.10575:10615](),[5.1176][5.10575:10615](),[5.10575][5.10575:10615](),[5.10615][5.1177:1193]()
    pub const fn new() -> Self {
    Self { extension: None }
    }
    #[must_use]
  • replacement in l10n_embed_interaction/src/editor.rs at line 22
    [5.10752][5.10752:10938]()
    pub fn edit(
    &self,
    environment: &InteractionEnvironment,
    text: &str,
    ) -> Result<Option<String>, InteractionError> {
    match environment.context {
    [5.10752]
    [5.10938]
    pub fn edit(self, text: &str) -> Result<Option<String>, InteractionError> {
    match self.environment.context {
  • edit in l10n_embed_interaction/Cargo.toml at line 20
    [5.11514]
    [5.1935]
    icu_locale.workspace = true