validation1 for the form
This commit is contained in:
@@ -8,7 +8,7 @@ license.workspace = true
|
|||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
async-trait = "0.1.88"
|
async-trait = "0.1.88"
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
canvas = { path = "../canvas", features = ["gui", "suggestions", "cursor-style", "keymap"] }
|
canvas = { path = "../canvas", features = ["gui", "suggestions", "cursor-style", "keymap", "validation"] }
|
||||||
|
|
||||||
ratatui = { workspace = true }
|
ratatui = { workspace = true }
|
||||||
crossterm = { workspace = true }
|
crossterm = { workspace = true }
|
||||||
@@ -30,8 +30,9 @@ unicode-segmentation = "1.12.0"
|
|||||||
unicode-width.workspace = true
|
unicode-width.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["validation"]
|
||||||
ui-debug = []
|
ui-debug = []
|
||||||
|
validation = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest = "0.25.0"
|
rstest = "0.25.0"
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
// src/pages/forms/state.rs
|
// src/pages/forms/state.rs
|
||||||
|
|
||||||
use canvas::{DataProvider, AppMode};
|
use canvas::{DataProvider, AppMode};
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
use canvas::{CharacterLimits, ValidationConfig, ValidationConfigBuilder};
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
use canvas::validation::limits::CountMode;
|
||||||
use common::proto::komp_ac::search::search_response::Hit;
|
use common::proto::komp_ac::search::search_response::Hit;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -39,6 +43,18 @@ pub struct FormState {
|
|||||||
pub autocomplete_loading: bool,
|
pub autocomplete_loading: bool,
|
||||||
pub link_display_map: HashMap<usize, String>,
|
pub link_display_map: HashMap<usize, String>,
|
||||||
pub app_mode: AppMode,
|
pub app_mode: AppMode,
|
||||||
|
// Validation 1 (character limits) per field. None = no validation for that field.
|
||||||
|
// Leave room for future rules (patterns, masks, etc.).
|
||||||
|
pub char_limits: Vec<Option<CharLimitsRule>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CharLimitsRule {
|
||||||
|
pub min: Option<usize>,
|
||||||
|
pub max: Option<usize>,
|
||||||
|
pub warn_at: Option<usize>,
|
||||||
|
pub count_mode: CountMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormState {
|
impl FormState {
|
||||||
@@ -56,6 +72,7 @@ impl FormState {
|
|||||||
fields: Vec<FieldDefinition>,
|
fields: Vec<FieldDefinition>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let values = vec![String::new(); fields.len()];
|
let values = vec![String::new(); fields.len()];
|
||||||
|
let len = values.len();
|
||||||
FormState {
|
FormState {
|
||||||
id: 0,
|
id: 0,
|
||||||
profile_name,
|
profile_name,
|
||||||
@@ -73,6 +90,7 @@ impl FormState {
|
|||||||
autocomplete_loading: false,
|
autocomplete_loading: false,
|
||||||
link_display_map: HashMap::new(),
|
link_display_map: HashMap::new(),
|
||||||
app_mode: canvas::AppMode::Edit,
|
app_mode: canvas::AppMode::Edit,
|
||||||
|
char_limits: vec![None; len],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +274,24 @@ impl FormState {
|
|||||||
pub fn set_current_cursor_pos(&mut self, pos: usize) {
|
pub fn set_current_cursor_pos(&mut self, pos: usize) {
|
||||||
self.current_cursor_pos = pos;
|
self.current_cursor_pos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
pub fn set_character_limits_rules(
|
||||||
|
&mut self,
|
||||||
|
rules: Vec<Option<CharLimitsRule>>,
|
||||||
|
) {
|
||||||
|
if rules.len() == self.fields.len() {
|
||||||
|
self.char_limits = rules;
|
||||||
|
} else {
|
||||||
|
tracing::warn!(
|
||||||
|
"Character limits count {} != field count {} for {}.{}",
|
||||||
|
rules.len(),
|
||||||
|
self.fields.len(),
|
||||||
|
self.profile_name,
|
||||||
|
self.table_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Implement DataProvider for FormState
|
// Step 2: Implement DataProvider for FormState
|
||||||
@@ -282,4 +318,26 @@ impl DataProvider for FormState {
|
|||||||
fn supports_suggestions(&self, field_index: usize) -> bool {
|
fn supports_suggestions(&self, field_index: usize) -> bool {
|
||||||
self.fields.get(field_index).map(|f| f.is_link).unwrap_or(false)
|
self.fields.get(field_index).map(|f| f.is_link).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validation 1: Provide character-limit-based validation to canvas
|
||||||
|
// Only compiled when the "validation" feature is enabled on canvas.
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
fn validation_config(&self, index: usize) -> Option<ValidationConfig> {
|
||||||
|
let rule = self.char_limits.get(index)?.as_ref()?;
|
||||||
|
let mut limits = match (rule.min, rule.max) {
|
||||||
|
(Some(min), Some(max)) => CharacterLimits::new_range(min, max),
|
||||||
|
(None, Some(max)) => CharacterLimits::new(max),
|
||||||
|
(Some(min), None) => CharacterLimits::new_range(min, usize::MAX),
|
||||||
|
(None, None) => CharacterLimits::new(usize::MAX),
|
||||||
|
};
|
||||||
|
limits = limits.with_count_mode(rule.count_mode);
|
||||||
|
if let Some(warn) = rule.warn_at {
|
||||||
|
limits = limits.with_warning_threshold(warn);
|
||||||
|
}
|
||||||
|
Some(
|
||||||
|
ValidationConfigBuilder::new()
|
||||||
|
.with_character_limits(limits)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,14 @@ use common::proto::komp_ac::tables_data::{
|
|||||||
};
|
};
|
||||||
use crate::search::SearchGrpc;
|
use crate::search::SearchGrpc;
|
||||||
use common::proto::komp_ac::search::SearchResponse;
|
use common::proto::komp_ac::search::SearchResponse;
|
||||||
|
use common::proto::komp_ac::table_validation::{
|
||||||
|
table_validation_service_client::TableValidationServiceClient,
|
||||||
|
GetTableValidationRequest,
|
||||||
|
TableValidationResponse,
|
||||||
|
CountMode as PbCountMode,
|
||||||
|
FieldValidation as PbFieldValidation,
|
||||||
|
CharacterLimits as PbCharacterLimits,
|
||||||
|
};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tonic::transport::{Channel, Endpoint};
|
use tonic::transport::{Channel, Endpoint};
|
||||||
@@ -40,6 +48,7 @@ pub struct GrpcClient {
|
|||||||
table_script_client: TableScriptClient<Channel>,
|
table_script_client: TableScriptClient<Channel>,
|
||||||
tables_data_client: TablesDataClient<Channel>,
|
tables_data_client: TablesDataClient<Channel>,
|
||||||
search_client: SearchGrpc,
|
search_client: SearchGrpc,
|
||||||
|
table_validation_client: TableValidationServiceClient<Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GrpcClient {
|
impl GrpcClient {
|
||||||
@@ -63,6 +72,8 @@ impl GrpcClient {
|
|||||||
let table_script_client = TableScriptClient::new(channel.clone());
|
let table_script_client = TableScriptClient::new(channel.clone());
|
||||||
let tables_data_client = TablesDataClient::new(channel.clone());
|
let tables_data_client = TablesDataClient::new(channel.clone());
|
||||||
let search_client = SearchGrpc::new(channel.clone());
|
let search_client = SearchGrpc::new(channel.clone());
|
||||||
|
let table_validation_client =
|
||||||
|
TableValidationServiceClient::new(channel.clone());
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
channel,
|
channel,
|
||||||
@@ -71,6 +82,7 @@ impl GrpcClient {
|
|||||||
table_script_client,
|
table_script_client,
|
||||||
tables_data_client,
|
tables_data_client,
|
||||||
search_client,
|
search_client,
|
||||||
|
table_validation_client,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +91,24 @@ impl GrpcClient {
|
|||||||
self.channel.clone()
|
self.channel.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch validation rules for a table. Absence of a field in response = no validation.
|
||||||
|
pub async fn get_table_validation(
|
||||||
|
&mut self,
|
||||||
|
profile_name: String,
|
||||||
|
table_name: String,
|
||||||
|
) -> Result<TableValidationResponse> {
|
||||||
|
let req = GetTableValidationRequest {
|
||||||
|
profile_name,
|
||||||
|
table_name,
|
||||||
|
};
|
||||||
|
let resp = self
|
||||||
|
.table_validation_client
|
||||||
|
.get_table_validation(tonic::Request::new(req))
|
||||||
|
.await
|
||||||
|
.context("gRPC GetTableValidation call failed")?;
|
||||||
|
Ok(resp.into_inner())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_table_structure(
|
pub async fn get_table_structure(
|
||||||
&mut self,
|
&mut self,
|
||||||
profile_name: String,
|
profile_name: String,
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ use crate::pages::admin_panel::add_logic::state::AddLogicState;
|
|||||||
use crate::pages::forms::logic::SaveOutcome;
|
use crate::pages::forms::logic::SaveOutcome;
|
||||||
use crate::utils::columns::filter_user_columns;
|
use crate::utils::columns::filter_user_columns;
|
||||||
use crate::pages::forms::{FieldDefinition, FormState};
|
use crate::pages::forms::{FieldDefinition, FormState};
|
||||||
|
use common::proto::komp_ac::table_validation::CountMode as PbCountMode;
|
||||||
|
use canvas::validation::limits::CountMode;
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -314,4 +316,60 @@ impl UiService {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch and apply "Validation 1" (character limits) rules for this form.
|
||||||
|
pub async fn apply_validation1_for_form(
|
||||||
|
grpc_client: &mut GrpcClient,
|
||||||
|
app_state: &mut AppState,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
let (profile, table) = path
|
||||||
|
.split_once('/')
|
||||||
|
.context("Invalid form path for validation")?;
|
||||||
|
|
||||||
|
let resp = grpc_client
|
||||||
|
.get_table_validation(profile.to_string(), table.to_string())
|
||||||
|
.await
|
||||||
|
.context("Failed to fetch table validation")?;
|
||||||
|
|
||||||
|
if let Some(fs) = app_state.form_state_for_path(path) {
|
||||||
|
let mut rules: Vec<Option<crate::pages::forms::state::CharLimitsRule>> =
|
||||||
|
vec![None; fs.fields.len()];
|
||||||
|
|
||||||
|
for f in resp.fields {
|
||||||
|
if let Some(idx) = fs.fields.iter().position(|fd| fd.data_key == f.data_key) {
|
||||||
|
if let Some(limits) = f.limits {
|
||||||
|
let has_any =
|
||||||
|
limits.min != 0 || limits.max != 0 || limits.warn_at.is_some();
|
||||||
|
if has_any {
|
||||||
|
let cm = match PbCountMode::from_i32(limits.count_mode) {
|
||||||
|
Some(PbCountMode::Unspecified) | None => CountMode::Characters, // protobuf default → fallback
|
||||||
|
Some(PbCountMode::Chars) => CountMode::Characters,
|
||||||
|
Some(PbCountMode::Bytes) => CountMode::Bytes,
|
||||||
|
Some(PbCountMode::DisplayWidth) => CountMode::DisplayWidth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let min = if limits.min == 0 { None } else { Some(limits.min as usize) };
|
||||||
|
let max = if limits.max == 0 { None } else { Some(limits.max as usize) };
|
||||||
|
let warn_at = limits.warn_at.map(|w| w as usize);
|
||||||
|
|
||||||
|
rules[idx] = Some(crate::pages::forms::state::CharLimitsRule {
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
warn_at,
|
||||||
|
count_mode: cm,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fs.set_character_limits_rules(rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(editor) = app_state.editor_for_path(path) {
|
||||||
|
editor.set_validation_enabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,6 +123,10 @@ pub async fn run_ui() -> Result<()> {
|
|||||||
app_state.ensure_form_editor(&path, &config, || {
|
app_state.ensure_form_editor(&path, &config, || {
|
||||||
FormState::new(initial_profile.clone(), initial_table.clone(), initial_field_defs)
|
FormState::new(initial_profile.clone(), initial_table.clone(), initial_field_defs)
|
||||||
});
|
});
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
UiService::apply_validation1_for_form(&mut grpc_client, &mut app_state, &path)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
buffer_state.update_history(AppView::Form(path.clone()));
|
buffer_state.update_history(AppView::Form(path.clone()));
|
||||||
router.navigate(Page::Form(path.clone()));
|
router.navigate(Page::Form(path.clone()));
|
||||||
|
|
||||||
@@ -516,6 +520,21 @@ pub async fn run_ui() -> Result<()> {
|
|||||||
prev_view_profile_name = current_view_profile;
|
prev_view_profile_name = current_view_profile;
|
||||||
prev_view_table_name = current_view_table;
|
prev_view_table_name = current_view_table;
|
||||||
table_just_switched = true;
|
table_just_switched = true;
|
||||||
|
// Apply character-limit validation for the new form
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
if let (Some(prof), Some(tbl)) = (
|
||||||
|
app_state.current_view_profile_name.as_ref(),
|
||||||
|
app_state.current_view_table_name.as_ref(),
|
||||||
|
) {
|
||||||
|
let p = format!("{}/{}", prof, tbl);
|
||||||
|
UiService::apply_validation1_for_form(
|
||||||
|
&mut grpc_client,
|
||||||
|
&mut app_state,
|
||||||
|
&p,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
app_state.update_dialog_content(
|
app_state.update_dialog_content(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
"proto/table_script.proto",
|
"proto/table_script.proto",
|
||||||
"proto/search.proto",
|
"proto/search.proto",
|
||||||
"proto/search2.proto",
|
"proto/search2.proto",
|
||||||
|
"proto/table_validation.proto",
|
||||||
],
|
],
|
||||||
&["proto"],
|
&["proto"],
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
56
common/proto/table_validation.proto
Normal file
56
common/proto/table_validation.proto
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// common/proto/table_validation.proto
|
||||||
|
syntax = "proto3";
|
||||||
|
package komp_ac.table_validation;
|
||||||
|
|
||||||
|
// Request validation rules for a table
|
||||||
|
message GetTableValidationRequest {
|
||||||
|
string profile_name = 1;
|
||||||
|
string table_name = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response with field-level validations; if a field is omitted,
|
||||||
|
// no validation is applied (default unspecified).
|
||||||
|
message TableValidationResponse {
|
||||||
|
repeated FieldValidation fields = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field-level validation (extensible for future kinds)
|
||||||
|
message FieldValidation {
|
||||||
|
// MUST match your frontend FormState.data_key for the column
|
||||||
|
string data_key = 1;
|
||||||
|
|
||||||
|
// Current: only CharacterLimits. More rules can be added later.
|
||||||
|
CharacterLimits limits = 10;
|
||||||
|
// Future expansion:
|
||||||
|
// PatternRules pattern = 11;
|
||||||
|
// DisplayMask mask = 12;
|
||||||
|
// ExternalValidation external = 13;
|
||||||
|
// CustomFormatter formatter = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Character length counting mode
|
||||||
|
enum CountMode {
|
||||||
|
COUNT_MODE_UNSPECIFIED = 0; // default: same as CHARS
|
||||||
|
CHARS = 1;
|
||||||
|
BYTES = 2;
|
||||||
|
DISPLAY_WIDTH = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Character limit validation (Validation 1)
|
||||||
|
message CharacterLimits {
|
||||||
|
// When zero, the field is considered "not set". If both min/max are zero,
|
||||||
|
// the server should avoid sending this FieldValidation (no validation).
|
||||||
|
uint32 min = 1;
|
||||||
|
uint32 max = 2;
|
||||||
|
|
||||||
|
// Optional warning threshold; when unset, no warning threshold is applied.
|
||||||
|
optional uint32 warn_at = 3;
|
||||||
|
|
||||||
|
CountMode count_mode = 4; // defaults to CHARS if unspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service to fetch validations for a table
|
||||||
|
service TableValidationService {
|
||||||
|
rpc GetTableValidation(GetTableValidationRequest)
|
||||||
|
returns (TableValidationResponse);
|
||||||
|
}
|
||||||
@@ -34,6 +34,9 @@ pub mod proto {
|
|||||||
pub mod search2 {
|
pub mod search2 {
|
||||||
include!("proto/komp_ac.search2.rs");
|
include!("proto/komp_ac.search2.rs");
|
||||||
}
|
}
|
||||||
|
pub mod table_validation {
|
||||||
|
include!("proto/komp_ac.table_validation.rs");
|
||||||
|
}
|
||||||
pub const FILE_DESCRIPTOR_SET: &[u8] =
|
pub const FILE_DESCRIPTOR_SET: &[u8] =
|
||||||
include_bytes!("proto/descriptor.bin");
|
include_bytes!("proto/descriptor.bin");
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
395
common/src/proto/komp_ac.table_validation.rs
Normal file
395
common/src/proto/komp_ac.table_validation.rs
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
// This file is @generated by prost-build.
|
||||||
|
/// Request validation rules for a table
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct GetTableValidationRequest {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub profile_name: ::prost::alloc::string::String,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub table_name: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// Response with field-level validations; if a field is omitted,
|
||||||
|
/// no validation is applied (default unspecified).
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct TableValidationResponse {
|
||||||
|
#[prost(message, repeated, tag = "1")]
|
||||||
|
pub fields: ::prost::alloc::vec::Vec<FieldValidation>,
|
||||||
|
}
|
||||||
|
/// Field-level validation (extensible for future kinds)
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FieldValidation {
|
||||||
|
/// MUST match your frontend FormState.data_key for the column
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub data_key: ::prost::alloc::string::String,
|
||||||
|
/// Current: only CharacterLimits. More rules can be added later.
|
||||||
|
///
|
||||||
|
/// Future expansion:
|
||||||
|
/// PatternRules pattern = 11;
|
||||||
|
/// DisplayMask mask = 12;
|
||||||
|
/// ExternalValidation external = 13;
|
||||||
|
/// CustomFormatter formatter = 14;
|
||||||
|
#[prost(message, optional, tag = "10")]
|
||||||
|
pub limits: ::core::option::Option<CharacterLimits>,
|
||||||
|
}
|
||||||
|
/// Character limit validation (Validation 1)
|
||||||
|
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||||
|
pub struct CharacterLimits {
|
||||||
|
/// When zero, the field is considered "not set". If both min/max are zero,
|
||||||
|
/// the server should avoid sending this FieldValidation (no validation).
|
||||||
|
#[prost(uint32, tag = "1")]
|
||||||
|
pub min: u32,
|
||||||
|
#[prost(uint32, tag = "2")]
|
||||||
|
pub max: u32,
|
||||||
|
/// Optional warning threshold; when unset, no warning threshold is applied.
|
||||||
|
#[prost(uint32, optional, tag = "3")]
|
||||||
|
pub warn_at: ::core::option::Option<u32>,
|
||||||
|
/// defaults to CHARS if unspecified
|
||||||
|
#[prost(enumeration = "CountMode", tag = "4")]
|
||||||
|
pub count_mode: i32,
|
||||||
|
}
|
||||||
|
/// Character length counting mode
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum CountMode {
|
||||||
|
/// default: same as CHARS
|
||||||
|
Unspecified = 0,
|
||||||
|
Chars = 1,
|
||||||
|
Bytes = 2,
|
||||||
|
DisplayWidth = 3,
|
||||||
|
}
|
||||||
|
impl CountMode {
|
||||||
|
/// 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::Unspecified => "COUNT_MODE_UNSPECIFIED",
|
||||||
|
Self::Chars => "CHARS",
|
||||||
|
Self::Bytes => "BYTES",
|
||||||
|
Self::DisplayWidth => "DISPLAY_WIDTH",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||||
|
match value {
|
||||||
|
"COUNT_MODE_UNSPECIFIED" => Some(Self::Unspecified),
|
||||||
|
"CHARS" => Some(Self::Chars),
|
||||||
|
"BYTES" => Some(Self::Bytes),
|
||||||
|
"DISPLAY_WIDTH" => Some(Self::DisplayWidth),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Generated client implementations.
|
||||||
|
pub mod table_validation_service_client {
|
||||||
|
#![allow(
|
||||||
|
unused_variables,
|
||||||
|
dead_code,
|
||||||
|
missing_docs,
|
||||||
|
clippy::wildcard_imports,
|
||||||
|
clippy::let_unit_value,
|
||||||
|
)]
|
||||||
|
use tonic::codegen::*;
|
||||||
|
use tonic::codegen::http::Uri;
|
||||||
|
/// Service to fetch validations for a table
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TableValidationServiceClient<T> {
|
||||||
|
inner: tonic::client::Grpc<T>,
|
||||||
|
}
|
||||||
|
impl TableValidationServiceClient<tonic::transport::Channel> {
|
||||||
|
/// Attempt to create a new client by connecting to a given endpoint.
|
||||||
|
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||||
|
where
|
||||||
|
D: TryInto<tonic::transport::Endpoint>,
|
||||||
|
D::Error: Into<StdError>,
|
||||||
|
{
|
||||||
|
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||||
|
Ok(Self::new(conn))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> TableValidationServiceClient<T>
|
||||||
|
where
|
||||||
|
T: tonic::client::GrpcService<tonic::body::Body>,
|
||||||
|
T::Error: Into<StdError>,
|
||||||
|
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
|
||||||
|
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
|
||||||
|
{
|
||||||
|
pub fn new(inner: T) -> Self {
|
||||||
|
let inner = tonic::client::Grpc::new(inner);
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||||
|
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
pub fn with_interceptor<F>(
|
||||||
|
inner: T,
|
||||||
|
interceptor: F,
|
||||||
|
) -> TableValidationServiceClient<InterceptedService<T, F>>
|
||||||
|
where
|
||||||
|
F: tonic::service::Interceptor,
|
||||||
|
T::ResponseBody: Default,
|
||||||
|
T: tonic::codegen::Service<
|
||||||
|
http::Request<tonic::body::Body>,
|
||||||
|
Response = http::Response<
|
||||||
|
<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
<T as tonic::codegen::Service<
|
||||||
|
http::Request<tonic::body::Body>,
|
||||||
|
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||||
|
{
|
||||||
|
TableValidationServiceClient::new(
|
||||||
|
InterceptedService::new(inner, interceptor),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/// Compress requests with the given encoding.
|
||||||
|
///
|
||||||
|
/// This requires the server to support it otherwise it might respond with an
|
||||||
|
/// error.
|
||||||
|
#[must_use]
|
||||||
|
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||||
|
self.inner = self.inner.send_compressed(encoding);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Enable decompressing responses.
|
||||||
|
#[must_use]
|
||||||
|
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||||
|
self.inner = self.inner.accept_compressed(encoding);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Limits the maximum size of a decoded message.
|
||||||
|
///
|
||||||
|
/// Default: `4MB`
|
||||||
|
#[must_use]
|
||||||
|
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||||
|
self.inner = self.inner.max_decoding_message_size(limit);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Limits the maximum size of an encoded message.
|
||||||
|
///
|
||||||
|
/// Default: `usize::MAX`
|
||||||
|
#[must_use]
|
||||||
|
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||||
|
self.inner = self.inner.max_encoding_message_size(limit);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub async fn get_table_validation(
|
||||||
|
&mut self,
|
||||||
|
request: impl tonic::IntoRequest<super::GetTableValidationRequest>,
|
||||||
|
) -> std::result::Result<
|
||||||
|
tonic::Response<super::TableValidationResponse>,
|
||||||
|
tonic::Status,
|
||||||
|
> {
|
||||||
|
self.inner
|
||||||
|
.ready()
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
tonic::Status::unknown(
|
||||||
|
format!("Service was not ready: {}", e.into()),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let codec = tonic::codec::ProstCodec::default();
|
||||||
|
let path = http::uri::PathAndQuery::from_static(
|
||||||
|
"/komp_ac.table_validation.TableValidationService/GetTableValidation",
|
||||||
|
);
|
||||||
|
let mut req = request.into_request();
|
||||||
|
req.extensions_mut()
|
||||||
|
.insert(
|
||||||
|
GrpcMethod::new(
|
||||||
|
"komp_ac.table_validation.TableValidationService",
|
||||||
|
"GetTableValidation",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
self.inner.unary(req, path, codec).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Generated server implementations.
|
||||||
|
pub mod table_validation_service_server {
|
||||||
|
#![allow(
|
||||||
|
unused_variables,
|
||||||
|
dead_code,
|
||||||
|
missing_docs,
|
||||||
|
clippy::wildcard_imports,
|
||||||
|
clippy::let_unit_value,
|
||||||
|
)]
|
||||||
|
use tonic::codegen::*;
|
||||||
|
/// Generated trait containing gRPC methods that should be implemented for use with TableValidationServiceServer.
|
||||||
|
#[async_trait]
|
||||||
|
pub trait TableValidationService: std::marker::Send + std::marker::Sync + 'static {
|
||||||
|
async fn get_table_validation(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<super::GetTableValidationRequest>,
|
||||||
|
) -> std::result::Result<
|
||||||
|
tonic::Response<super::TableValidationResponse>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
/// Service to fetch validations for a table
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TableValidationServiceServer<T> {
|
||||||
|
inner: Arc<T>,
|
||||||
|
accept_compression_encodings: EnabledCompressionEncodings,
|
||||||
|
send_compression_encodings: EnabledCompressionEncodings,
|
||||||
|
max_decoding_message_size: Option<usize>,
|
||||||
|
max_encoding_message_size: Option<usize>,
|
||||||
|
}
|
||||||
|
impl<T> TableValidationServiceServer<T> {
|
||||||
|
pub fn new(inner: T) -> Self {
|
||||||
|
Self::from_arc(Arc::new(inner))
|
||||||
|
}
|
||||||
|
pub fn from_arc(inner: Arc<T>) -> Self {
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
accept_compression_encodings: Default::default(),
|
||||||
|
send_compression_encodings: Default::default(),
|
||||||
|
max_decoding_message_size: None,
|
||||||
|
max_encoding_message_size: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_interceptor<F>(
|
||||||
|
inner: T,
|
||||||
|
interceptor: F,
|
||||||
|
) -> InterceptedService<Self, F>
|
||||||
|
where
|
||||||
|
F: tonic::service::Interceptor,
|
||||||
|
{
|
||||||
|
InterceptedService::new(Self::new(inner), interceptor)
|
||||||
|
}
|
||||||
|
/// Enable decompressing requests with the given encoding.
|
||||||
|
#[must_use]
|
||||||
|
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||||
|
self.accept_compression_encodings.enable(encoding);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Compress responses with the given encoding, if the client supports it.
|
||||||
|
#[must_use]
|
||||||
|
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||||
|
self.send_compression_encodings.enable(encoding);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Limits the maximum size of a decoded message.
|
||||||
|
///
|
||||||
|
/// Default: `4MB`
|
||||||
|
#[must_use]
|
||||||
|
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||||
|
self.max_decoding_message_size = Some(limit);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Limits the maximum size of an encoded message.
|
||||||
|
///
|
||||||
|
/// Default: `usize::MAX`
|
||||||
|
#[must_use]
|
||||||
|
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||||
|
self.max_encoding_message_size = Some(limit);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T, B> tonic::codegen::Service<http::Request<B>>
|
||||||
|
for TableValidationServiceServer<T>
|
||||||
|
where
|
||||||
|
T: TableValidationService,
|
||||||
|
B: Body + std::marker::Send + 'static,
|
||||||
|
B::Error: Into<StdError> + std::marker::Send + 'static,
|
||||||
|
{
|
||||||
|
type Response = http::Response<tonic::body::Body>;
|
||||||
|
type Error = std::convert::Infallible;
|
||||||
|
type Future = BoxFuture<Self::Response, Self::Error>;
|
||||||
|
fn poll_ready(
|
||||||
|
&mut self,
|
||||||
|
_cx: &mut Context<'_>,
|
||||||
|
) -> Poll<std::result::Result<(), Self::Error>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
fn call(&mut self, req: http::Request<B>) -> Self::Future {
|
||||||
|
match req.uri().path() {
|
||||||
|
"/komp_ac.table_validation.TableValidationService/GetTableValidation" => {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
struct GetTableValidationSvc<T: TableValidationService>(pub Arc<T>);
|
||||||
|
impl<
|
||||||
|
T: TableValidationService,
|
||||||
|
> tonic::server::UnaryService<super::GetTableValidationRequest>
|
||||||
|
for GetTableValidationSvc<T> {
|
||||||
|
type Response = super::TableValidationResponse;
|
||||||
|
type Future = BoxFuture<
|
||||||
|
tonic::Response<Self::Response>,
|
||||||
|
tonic::Status,
|
||||||
|
>;
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
request: tonic::Request<super::GetTableValidationRequest>,
|
||||||
|
) -> Self::Future {
|
||||||
|
let inner = Arc::clone(&self.0);
|
||||||
|
let fut = async move {
|
||||||
|
<T as TableValidationService>::get_table_validation(
|
||||||
|
&inner,
|
||||||
|
request,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
};
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let accept_compression_encodings = self.accept_compression_encodings;
|
||||||
|
let send_compression_encodings = self.send_compression_encodings;
|
||||||
|
let max_decoding_message_size = self.max_decoding_message_size;
|
||||||
|
let max_encoding_message_size = self.max_encoding_message_size;
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
let fut = async move {
|
||||||
|
let method = GetTableValidationSvc(inner);
|
||||||
|
let codec = tonic::codec::ProstCodec::default();
|
||||||
|
let mut grpc = tonic::server::Grpc::new(codec)
|
||||||
|
.apply_compression_config(
|
||||||
|
accept_compression_encodings,
|
||||||
|
send_compression_encodings,
|
||||||
|
)
|
||||||
|
.apply_max_message_size_config(
|
||||||
|
max_decoding_message_size,
|
||||||
|
max_encoding_message_size,
|
||||||
|
);
|
||||||
|
let res = grpc.unary(method, req).await;
|
||||||
|
Ok(res)
|
||||||
|
};
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut response = http::Response::new(
|
||||||
|
tonic::body::Body::default(),
|
||||||
|
);
|
||||||
|
let headers = response.headers_mut();
|
||||||
|
headers
|
||||||
|
.insert(
|
||||||
|
tonic::Status::GRPC_STATUS,
|
||||||
|
(tonic::Code::Unimplemented as i32).into(),
|
||||||
|
);
|
||||||
|
headers
|
||||||
|
.insert(
|
||||||
|
http::header::CONTENT_TYPE,
|
||||||
|
tonic::metadata::GRPC_CONTENT_TYPE,
|
||||||
|
);
|
||||||
|
Ok(response)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> Clone for TableValidationServiceServer<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let inner = self.inner.clone();
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
accept_compression_encodings: self.accept_compression_encodings,
|
||||||
|
send_compression_encodings: self.send_compression_encodings,
|
||||||
|
max_decoding_message_size: self.max_decoding_message_size,
|
||||||
|
max_encoding_message_size: self.max_encoding_message_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Generated gRPC service name
|
||||||
|
pub const SERVICE_NAME: &str = "komp_ac.table_validation.TableValidationService";
|
||||||
|
impl<T> tonic::server::NamedService for TableValidationServiceServer<T> {
|
||||||
|
const NAME: &'static str = SERVICE_NAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user