From 9ea03e0cc9e9aeae2129e12371adb999a8e67a4e Mon Sep 17 00:00:00 2001 From: Priec Date: Sat, 8 Aug 2026 22:42:32 +0200 Subject: [PATCH] mask optionally part of the data --- client | 2 +- common/proto/table_validation.proto | 16 +++++--- common/src/proto/descriptor.bin | Bin 150423 -> 150790 bytes common/src/proto/komp_ac.table_validation.rs | 37 ++++++++++++++++--- server | 2 +- tui-canvas | 2 +- tui-pages | 2 +- web/src/pages/add_validation/state.rs | 1 + 8 files changed, 47 insertions(+), 15 deletions(-) diff --git a/client b/client index 827e6a40..949b8c78 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit 827e6a408a9f3df5f306461d3ade22f08e957c62 +Subproject commit 949b8c784d40e8889fb08b44dc591786412236fd diff --git a/common/proto/table_validation.proto b/common/proto/table_validation.proto index f3278cc8..c6187bc1 100644 --- a/common/proto/table_validation.proto +++ b/common/proto/table_validation.proto @@ -13,7 +13,7 @@ package komp_ac.table_validation; // // Important split: // - limits / pattern / allowed_values / required are validation rules. -// - mask is presentation and input-shaping metadata for clients. +// - mask is presentation/input-shaping metadata plus an optional storage policy. // Request validation rules for a table message GetTableValidationRequest { @@ -44,7 +44,7 @@ message FieldValidation { // Client-side hint that this field participates in external/asynchronous validation UI. bool external_validation_enabled = 13; - // Client-side display mask metadata. The server stores raw data without mask literals. + // Client-side display mask metadata and persisted representation policy. DisplayMask mask = 3; // Field must be provided / treated as required by clients and server enforcement layers. @@ -76,14 +76,18 @@ message CharacterLimits { CountMode countMode = 4; // defaults to CHARS if unspecified } -// Mask for pretty display only. -// -// This is not a validation rule by itself. It exists so clients can render and -// navigate masked input while still storing raw values server-side. +enum MaskStorageMode { + MASK_STORAGE_MODE_RAW = 0; + MASK_STORAGE_MODE_FORMATTED = 1; +} + +// Mask metadata used for rendering, input navigation, and optional persistence +// of reached mask literals. Template placeholders are never persisted. message DisplayMask { string pattern = 1; // e.g., "(###) ###-####" or "####-##-##" string input_char = 2; // e.g., "#" optional string template_char = 3; // e.g., "_" + optional MaskStorageMode storage_mode = 4; // absent/default means raw } // Which positions a pattern rule applies to. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 9f6fc12065207ae02ba990f648d4cab28a5e1559..0077d896fbd8330e9a0a9a2e06d863dc4111f26e 100644 GIT binary patch delta 7399 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 delta 7056 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 diff --git a/common/src/proto/komp_ac.table_validation.rs b/common/src/proto/komp_ac.table_validation.rs index 08603730..e1f8cfa5 100644 --- a/common/src/proto/komp_ac.table_validation.rs +++ b/common/src/proto/komp_ac.table_validation.rs @@ -34,7 +34,7 @@ pub struct FieldValidation { /// Client-side hint that this field participates in external/asynchronous validation UI. #[prost(bool, tag = "13")] pub external_validation_enabled: bool, - /// Client-side display mask metadata. The server stores raw data without mask literals. + /// Client-side display mask metadata and persisted representation policy. #[prost(message, optional, tag = "3")] pub mask: ::core::option::Option, /// Field must be provided / treated as required by clients and server enforcement layers. @@ -63,10 +63,8 @@ pub struct CharacterLimits { #[prost(enumeration = "CountMode", tag = "4")] pub count_mode: i32, } -/// Mask for pretty display only. -/// -/// This is not a validation rule by itself. It exists so clients can render and -/// navigate masked input while still storing raw values server-side. +/// Mask metadata used for rendering, input navigation, and optional persistence +/// of reached mask literals. Template placeholders are never persisted. #[derive(serde::Serialize, serde::Deserialize)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct DisplayMask { @@ -79,6 +77,9 @@ pub struct DisplayMask { /// e.g., "\_" #[prost(string, optional, tag = "3")] pub template_char: ::core::option::Option<::prost::alloc::string::String>, + /// absent/default means raw + #[prost(enumeration = "MaskStorageMode", optional, tag = "4")] + pub storage_mode: ::core::option::Option, } /// Which positions a pattern rule applies to. /// This exists instead of a string syntax like "0-3" so the server can validate @@ -401,6 +402,32 @@ impl CountMode { } } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum MaskStorageMode { + Raw = 0, + Formatted = 1, +} +impl MaskStorageMode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Raw => "MASK_STORAGE_MODE_RAW", + Self::Formatted => "MASK_STORAGE_MODE_FORMATTED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "MASK_STORAGE_MODE_RAW" => Some(Self::Raw), + "MASK_STORAGE_MODE_FORMATTED" => Some(Self::Formatted), + _ => None, + } + } +} #[derive(serde::Serialize, serde::Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] diff --git a/server b/server index e8c579bf..65e67fe7 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit e8c579bf55b20fec338507542c2bc7466ddfec9a +Subproject commit 65e67fe785b76a647759ca7d7e3517d251dab235 diff --git a/tui-canvas b/tui-canvas index dbec3130..f9d2a7a3 160000 --- a/tui-canvas +++ b/tui-canvas @@ -1 +1 @@ -Subproject commit dbec3130b35b324737df56140d6b0005f65e9479 +Subproject commit f9d2a7a3121abe68aaa232624035539eaf029240 diff --git a/tui-pages b/tui-pages index 8c66dedc..50e1145a 160000 --- a/tui-pages +++ b/tui-pages @@ -1 +1 @@ -Subproject commit 8c66dedc33d4c384977982b1115cb23a1d39e16c +Subproject commit 50e1145a9ab5cb7374da1b812f243b14e75614d7 diff --git a/web/src/pages/add_validation/state.rs b/web/src/pages/add_validation/state.rs index b4a0d5a5..aea97327 100644 --- a/web/src/pages/add_validation/state.rs +++ b/web/src/pages/add_validation/state.rs @@ -163,6 +163,7 @@ impl ValidationForm { self.mask_input_char.clone() }, template_char: nonempty(&self.mask_template_char), + storage_mode: None, }); let pattern = parse_pattern_rules(&self.pattern_rules, &self.pattern_description)?; Ok(FieldValidation {