appcore_filemaker/
source_table.rs1use serde::{Deserialize, Serialize};
14
15use crate::source::StyleSource;
16use crate::source_style::convert_style;
17use crate::{
18 ErrorCode, FileMakerError, Length, ResourceLimits, Result, TableColumn, TableIr, TableSpec,
19 TableStyleRule,
20};
21
22#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
24#[serde(deny_unknown_fields)]
25pub struct TableStyleRuleSource {
26 pub when: String,
28 #[serde(default)]
30 pub style: StyleSource,
31}
32
33#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
35#[serde(deny_unknown_fields)]
36pub struct TableSource {
37 pub columns: Vec<TableColumn>,
39 #[serde(default = "default_true")]
41 pub repeat_header: bool,
42 #[serde(default)]
44 pub group_by: Option<String>,
45 #[serde(default)]
47 pub total_fields: Vec<String>,
48 #[serde(default)]
50 pub conditional_styles: Vec<TableStyleRuleSource>,
51 #[serde(default = "default_auto_samples")]
53 pub auto_sample_rows: usize,
54 #[serde(default)]
56 pub max_rows: Option<u64>,
57 #[serde(default)]
59 pub max_row_fields: Option<usize>,
60 #[serde(default)]
62 pub max_cell_bytes: Option<usize>,
63 #[serde(default = "default_header_height")]
65 pub header_height: Length,
66 #[serde(default)]
68 pub row_height: Option<Length>,
69}
70
71pub(crate) fn convert_table(source: &TableSource, limits: &ResourceLimits) -> Result<TableIr> {
72 if matches!(source.header_height, Length::Auto) {
73 return Err(table_source_error("table header height cannot be auto"));
74 }
75 reject_raised_limit(source.max_rows, limits.max_rows, "table row")?;
76 reject_raised_limit(
77 source.max_row_fields,
78 limits.max_elements,
79 "table row field",
80 )?;
81 reject_raised_limit(
82 source.max_cell_bytes,
83 limits.max_text_bytes,
84 "table cell byte",
85 )?;
86 let spec = TableSpec {
87 columns: source.columns.clone(),
88 repeat_header: source.repeat_header,
89 group_by: source.group_by.clone(),
90 total_fields: source.total_fields.clone(),
91 conditional_styles: source
92 .conditional_styles
93 .iter()
94 .map(|rule| {
95 Ok(TableStyleRule {
96 when: rule.when.clone(),
97 style: convert_style(&rule.style)?,
98 })
99 })
100 .collect::<Result<Vec<_>>>()?,
101 style_expression_steps: limits.max_expression_steps,
102 auto_sample_rows: source.auto_sample_rows,
103 max_rows: source.max_rows.unwrap_or(limits.max_rows),
104 max_row_fields: source.max_row_fields.unwrap_or(limits.max_elements),
105 max_cell_bytes: source.max_cell_bytes.unwrap_or(limits.max_text_bytes),
106 };
107 spec.validate()?;
108 Ok(TableIr {
109 spec,
110 header_height: source.header_height,
111 row_height: source.row_height,
112 rows: Vec::new(),
113 })
114}
115
116const fn default_true() -> bool {
117 true
118}
119
120const fn default_auto_samples() -> usize {
121 16
122}
123
124fn default_header_height() -> Length {
125 Length::Absolute(crate::Unit::from_raw(18_000_000))
126}
127
128fn table_source_error(message: impl Into<String>) -> FileMakerError {
129 FileMakerError::new(ErrorCode::SchemaField, message)
130}
131
132fn reject_raised_limit<T>(requested: Option<T>, global: T, label: &str) -> Result<()>
133where
134 T: Copy + PartialOrd,
135{
136 if requested.is_some_and(|value| value > global) {
137 return Err(FileMakerError::new(
138 ErrorCode::LimitExceeded,
139 format!("{label} limit cannot exceed the compiler limit"),
140 ));
141 }
142 Ok(())
143}