1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use serde::{Deserialize, Serialize};
5use serde_json::{Map, Value};
6
7use super::{
8 ColumnFilter, ColumnId, ColumnPinningState, ColumnSizingInfoState, ColumnSizingState,
9 ColumnVisibilityState, ExpandingState, GroupedRowModel, GroupingState, PaginationState, RowKey,
10 RowModel, RowPinningState, SortSpec, TableState,
11};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct TanStackSortingSpec {
15 pub id: String,
16 pub desc: bool,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct TanStackColumnFilter {
21 pub id: String,
22 pub value: Value,
23}
24
25#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
26pub struct TanStackPaginationState {
27 #[serde(rename = "pageIndex")]
28 pub page_index: usize,
29 #[serde(rename = "pageSize")]
30 pub page_size: usize,
31}
32
33pub type TanStackColumnSizingState = BTreeMap<String, f32>;
34
35#[derive(Debug, Clone, Default, Serialize, Deserialize)]
36pub struct TanStackColumnSizingInfoState {
37 #[serde(default, rename = "columnSizingStart")]
38 pub column_sizing_start: Vec<(String, f32)>,
39 #[serde(default, rename = "deltaOffset")]
40 pub delta_offset: Option<f32>,
41 #[serde(default, rename = "deltaPercentage")]
42 pub delta_percentage: Option<f32>,
43 #[serde(default, rename = "isResizingColumn")]
44 pub is_resizing_column: Value,
45 #[serde(default, rename = "startOffset")]
46 pub start_offset: Option<f32>,
47 #[serde(default, rename = "startSize")]
48 pub start_size: Option<f32>,
49}
50
51#[derive(Debug, Clone, Default, Serialize, Deserialize)]
52pub struct TanStackColumnPinningState {
53 #[serde(default)]
54 pub left: Vec<String>,
55 #[serde(default)]
56 pub right: Vec<String>,
57}
58
59#[derive(Debug, Clone, Copy, Default)]
60struct TanStackStatePresence {
61 sorting: bool,
62 column_filters: bool,
63 global_filter: bool,
64 pagination: bool,
65 grouping: bool,
66 expanded: bool,
67 row_pinning: bool,
68 row_selection: bool,
69 column_pinning: bool,
70 column_order: bool,
71 column_visibility: bool,
72 column_sizing: bool,
73 column_sizing_info: bool,
74}
75
76impl TanStackStatePresence {
77 fn from_json(value: &Value) -> Self {
78 let mut out = Self::default();
79 let Some(map) = value.as_object() else {
80 return out;
81 };
82
83 out.sorting = map.contains_key("sorting");
84 out.column_filters = map.contains_key("columnFilters");
85 out.global_filter = map.contains_key("globalFilter");
86 out.pagination = map.contains_key("pagination");
87 out.grouping = map.contains_key("grouping");
88 out.expanded = map.contains_key("expanded");
89 out.row_pinning = map.contains_key("rowPinning");
90 out.row_selection = map.contains_key("rowSelection");
91 out.column_pinning = map.contains_key("columnPinning");
92 out.column_order = map.contains_key("columnOrder");
93 out.column_visibility = map.contains_key("columnVisibility");
94 out.column_sizing = map.contains_key("columnSizing");
95 out.column_sizing_info = map.contains_key("columnSizingInfo");
96 out
97 }
98}
99
100#[derive(Debug, Clone, Default, Serialize, Deserialize)]
101pub struct TanStackTableState {
102 #[serde(default, skip_serializing_if = "Vec::is_empty")]
103 pub sorting: Vec<TanStackSortingSpec>,
104 #[serde(
105 default,
106 rename = "columnFilters",
107 skip_serializing_if = "Vec::is_empty"
108 )]
109 pub column_filters: Vec<TanStackColumnFilter>,
110 #[serde(
111 default,
112 rename = "globalFilter",
113 skip_serializing_if = "Option::is_none"
114 )]
115 pub global_filter: Option<Value>,
116 #[serde(default, skip_serializing_if = "Option::is_none")]
117 pub pagination: Option<TanStackPaginationState>,
118 #[serde(default, skip_serializing_if = "Vec::is_empty")]
119 pub grouping: Vec<String>,
120 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub expanded: Option<TanStackExpandedState>,
122 #[serde(
123 default,
124 rename = "rowPinning",
125 skip_serializing_if = "Option::is_none"
126 )]
127 pub row_pinning: Option<TanStackRowPinningState>,
128 #[serde(
129 default,
130 rename = "rowSelection",
131 skip_serializing_if = "Option::is_none"
132 )]
133 pub row_selection: Option<BTreeMap<String, bool>>,
134 #[serde(
135 default,
136 rename = "columnPinning",
137 skip_serializing_if = "Option::is_none"
138 )]
139 pub column_pinning: Option<TanStackColumnPinningState>,
140 #[serde(default, rename = "columnOrder", skip_serializing_if = "Vec::is_empty")]
141 pub column_order: Vec<String>,
142 #[serde(
143 default,
144 rename = "columnVisibility",
145 skip_serializing_if = "Option::is_none"
146 )]
147 pub column_visibility: Option<BTreeMap<String, bool>>,
148 #[serde(
149 default,
150 rename = "columnSizing",
151 skip_serializing_if = "BTreeMap::is_empty"
152 )]
153 pub column_sizing: TanStackColumnSizingState,
154 #[serde(
155 default,
156 rename = "columnSizingInfo",
157 skip_serializing_if = "Option::is_none"
158 )]
159 pub column_sizing_info: Option<TanStackColumnSizingInfoState>,
160 #[serde(skip)]
161 presence: TanStackStatePresence,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
165#[serde(untagged)]
166pub enum TanStackExpandedState {
167 All(bool),
168 Map(BTreeMap<String, bool>),
169}
170
171#[derive(Debug, Clone, Default, Serialize, Deserialize)]
172pub struct TanStackRowPinningState {
173 #[serde(default)]
174 pub top: Vec<String>,
175 #[serde(default)]
176 pub bottom: Vec<String>,
177}
178
179#[derive(Debug, Clone, PartialEq, Eq)]
180pub enum TanStackStateError {
181 InvalidRowSelectionKey {
182 row_id: String,
183 },
184 InvalidExpandedKey {
185 row_id: String,
186 },
187 InvalidRowPinningKey {
188 row_id: String,
189 },
190 UnresolvedRowId {
191 field: &'static str,
192 row_id: String,
193 },
194 UnresolvedRowKey {
195 field: &'static str,
196 row_key: RowKey,
197 },
198 InvalidIsResizingColumnValue {
199 value: Value,
200 },
201}
202
203impl std::fmt::Display for TanStackStateError {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 match self {
206 Self::InvalidRowSelectionKey { row_id } => {
207 write!(
208 f,
209 "tanstack rowSelection key must parse as u64 (row_id={row_id})"
210 )
211 }
212 Self::InvalidExpandedKey { row_id } => {
213 write!(
214 f,
215 "tanstack expanded key must parse as u64 (row_id={row_id})"
216 )
217 }
218 Self::InvalidRowPinningKey { row_id } => {
219 write!(
220 f,
221 "tanstack rowPinning key must parse as u64 (row_id={row_id})"
222 )
223 }
224 Self::UnresolvedRowId { field, row_id } => {
225 write!(
226 f,
227 "tanstack row id must resolve via row model (field={field}, row_id={row_id})"
228 )
229 }
230 Self::UnresolvedRowKey { field, row_key } => {
231 write!(
232 f,
233 "tanstack row key must resolve via row model (field={field}, row_key={})",
234 row_key.0
235 )
236 }
237 Self::InvalidIsResizingColumnValue { value } => {
238 write!(
239 f,
240 "tanstack columnSizingInfo.isResizingColumn must be false|null|string (value={value:?})"
241 )
242 }
243 }
244 }
245}
246
247impl std::error::Error for TanStackStateError {}
248
249impl TanStackTableState {
250 pub fn from_json(value: &Value) -> serde_json::Result<Self> {
251 let mut out: Self = serde_json::from_value(value.clone())?;
252 out.presence = TanStackStatePresence::from_json(value);
253 Ok(out)
254 }
255
256 pub fn from_table_state(state: &TableState) -> Self {
257 let sorting: Vec<TanStackSortingSpec> = state
258 .sorting
259 .iter()
260 .map(|s| TanStackSortingSpec {
261 id: s.column.as_ref().to_string(),
262 desc: s.desc,
263 })
264 .collect();
265
266 let column_filters: Vec<TanStackColumnFilter> = state
267 .column_filters
268 .iter()
269 .map(|f| TanStackColumnFilter {
270 id: f.column.as_ref().to_string(),
271 value: f.value.clone(),
272 })
273 .collect();
274
275 let global_filter = state
276 .global_filter
277 .as_ref()
278 .and_then(|v| if v.is_null() { None } else { Some(v.clone()) });
279
280 let pagination = if state.pagination.page_index == 0 && state.pagination.page_size == 10 {
281 None
282 } else {
283 Some(TanStackPaginationState {
284 page_index: state.pagination.page_index,
285 page_size: state.pagination.page_size,
286 })
287 };
288
289 let grouping: Vec<String> = state
290 .grouping
291 .iter()
292 .map(|s| s.as_ref().to_string())
293 .collect();
294
295 let expanded = match &state.expanding {
296 ExpandingState::All => Some(TanStackExpandedState::All(true)),
297 ExpandingState::Keys(keys) if keys.is_empty() => None,
298 ExpandingState::Keys(keys) => Some(TanStackExpandedState::Map(
299 keys.iter().map(|k| (k.0.to_string(), true)).collect(),
300 )),
301 };
302
303 let row_pinning = if state.row_pinning.top.is_empty() && state.row_pinning.bottom.is_empty()
304 {
305 None
306 } else {
307 Some(TanStackRowPinningState {
308 top: state
309 .row_pinning
310 .top
311 .iter()
312 .map(|k| k.0.to_string())
313 .collect(),
314 bottom: state
315 .row_pinning
316 .bottom
317 .iter()
318 .map(|k| k.0.to_string())
319 .collect(),
320 })
321 };
322
323 let row_selection = if state.row_selection.is_empty() {
324 None
325 } else {
326 Some(
327 state
328 .row_selection
329 .iter()
330 .map(|k| (k.0.to_string(), true))
331 .collect(),
332 )
333 };
334
335 let column_pinning =
336 if state.column_pinning.left.is_empty() && state.column_pinning.right.is_empty() {
337 None
338 } else {
339 Some(TanStackColumnPinningState {
340 left: state
341 .column_pinning
342 .left
343 .iter()
344 .map(|s| s.as_ref().to_string())
345 .collect(),
346 right: state
347 .column_pinning
348 .right
349 .iter()
350 .map(|s| s.as_ref().to_string())
351 .collect(),
352 })
353 };
354
355 let column_order: Vec<String> = state
356 .column_order
357 .iter()
358 .map(|s| s.as_ref().to_string())
359 .collect();
360
361 let column_visibility = if state.column_visibility.is_empty() {
362 None
363 } else {
364 Some(
365 state
366 .column_visibility
367 .iter()
368 .map(|(k, v)| (k.as_ref().to_string(), *v))
369 .collect(),
370 )
371 };
372
373 let column_sizing: TanStackColumnSizingState = state
374 .column_sizing
375 .iter()
376 .map(|(k, v)| (k.as_ref().to_string(), *v))
377 .collect();
378
379 let column_sizing_info = {
380 let info = &state.column_sizing_info;
381 let is_default = info.column_sizing_start.is_empty()
382 && info.delta_offset.is_none()
383 && info.delta_percentage.is_none()
384 && info.is_resizing_column.is_none()
385 && info.start_offset.is_none()
386 && info.start_size.is_none();
387 if is_default {
388 None
389 } else {
390 Some(TanStackColumnSizingInfoState {
391 column_sizing_start: info
392 .column_sizing_start
393 .iter()
394 .map(|(id, size)| (id.as_ref().to_string(), *size))
395 .collect(),
396 delta_offset: info.delta_offset,
397 delta_percentage: info.delta_percentage,
398 is_resizing_column: match info.is_resizing_column.as_ref() {
399 None => Value::Bool(false),
400 Some(id) => Value::String(id.as_ref().to_string()),
401 },
402 start_offset: info.start_offset,
403 start_size: info.start_size,
404 })
405 }
406 };
407
408 let presence = TanStackStatePresence {
409 sorting: !sorting.is_empty(),
410 column_filters: !column_filters.is_empty(),
411 global_filter: global_filter.is_some(),
412 pagination: pagination.is_some(),
413 grouping: !grouping.is_empty(),
414 expanded: expanded.is_some(),
415 row_pinning: row_pinning.is_some(),
416 row_selection: row_selection.is_some(),
417 column_pinning: column_pinning.is_some(),
418 column_order: !column_order.is_empty(),
419 column_visibility: column_visibility.is_some(),
420 column_sizing: !column_sizing.is_empty(),
421 column_sizing_info: column_sizing_info.is_some(),
422 };
423
424 Self {
425 sorting,
426 column_filters,
427 global_filter,
428 pagination,
429 grouping,
430 expanded,
431 row_pinning,
432 row_selection,
433 column_pinning,
434 column_order,
435 column_visibility,
436 column_sizing,
437 column_sizing_info,
438 presence,
439 }
440 }
441
442 pub fn to_json(&self) -> serde_json::Result<Value> {
443 let mut out = Map::new();
444
445 if self.presence.sorting || !self.sorting.is_empty() {
446 out.insert("sorting".to_string(), serde_json::to_value(&self.sorting)?);
447 }
448
449 if self.presence.column_filters || !self.column_filters.is_empty() {
450 out.insert(
451 "columnFilters".to_string(),
452 serde_json::to_value(&self.column_filters)?,
453 );
454 }
455
456 if self.presence.global_filter || self.global_filter.is_some() {
457 out.insert(
458 "globalFilter".to_string(),
459 self.global_filter.clone().unwrap_or(Value::Null),
460 );
461 }
462
463 if self.presence.pagination || self.pagination.is_some() {
464 out.insert(
465 "pagination".to_string(),
466 match self.pagination.as_ref() {
467 Some(v) => serde_json::to_value(v)?,
468 None => Value::Null,
469 },
470 );
471 }
472
473 if self.presence.grouping || !self.grouping.is_empty() {
474 out.insert(
475 "grouping".to_string(),
476 serde_json::to_value(&self.grouping)?,
477 );
478 }
479
480 if self.presence.expanded || self.expanded.is_some() {
481 out.insert(
482 "expanded".to_string(),
483 match self.expanded.as_ref() {
484 Some(v) => serde_json::to_value(v)?,
485 None => Value::Null,
486 },
487 );
488 }
489
490 if self.presence.row_pinning || self.row_pinning.is_some() {
491 out.insert(
492 "rowPinning".to_string(),
493 match self.row_pinning.as_ref() {
494 Some(v) => serde_json::to_value(v)?,
495 None => Value::Null,
496 },
497 );
498 }
499
500 if self.presence.row_selection || self.row_selection.is_some() {
501 out.insert(
502 "rowSelection".to_string(),
503 match self.row_selection.as_ref() {
504 Some(v) => serde_json::to_value(v)?,
505 None => Value::Null,
506 },
507 );
508 }
509
510 if self.presence.column_pinning || self.column_pinning.is_some() {
511 out.insert(
512 "columnPinning".to_string(),
513 match self.column_pinning.as_ref() {
514 Some(v) => serde_json::to_value(v)?,
515 None => Value::Null,
516 },
517 );
518 }
519
520 if self.presence.column_order || !self.column_order.is_empty() {
521 out.insert(
522 "columnOrder".to_string(),
523 serde_json::to_value(&self.column_order)?,
524 );
525 }
526
527 if self.presence.column_visibility || self.column_visibility.is_some() {
528 out.insert(
529 "columnVisibility".to_string(),
530 match self.column_visibility.as_ref() {
531 Some(v) => serde_json::to_value(v)?,
532 None => Value::Null,
533 },
534 );
535 }
536
537 if self.presence.column_sizing || !self.column_sizing.is_empty() {
538 out.insert(
539 "columnSizing".to_string(),
540 serde_json::to_value(&self.column_sizing)?,
541 );
542 }
543
544 if self.presence.column_sizing_info || self.column_sizing_info.is_some() {
545 out.insert(
546 "columnSizingInfo".to_string(),
547 match self.column_sizing_info.as_ref() {
548 Some(v) => serde_json::to_value(v)?,
549 None => Value::Null,
550 },
551 );
552 }
553
554 Ok(Value::Object(out))
555 }
556
557 fn with_presence_hint(mut self, source: &Self) -> Self {
558 self.presence = source.presence;
559
560 if source.presence.expanded && self.expanded.is_none() {
561 self.expanded = source.expanded.clone();
562 }
563 if source.presence.row_pinning && self.row_pinning.is_none() {
564 self.row_pinning = source.row_pinning.clone();
565 }
566 if source.presence.row_selection && self.row_selection.is_none() {
567 self.row_selection = source.row_selection.clone();
568 }
569 if source.presence.column_pinning && self.column_pinning.is_none() {
570 self.column_pinning = source.column_pinning.clone();
571 }
572 if source.presence.column_visibility && self.column_visibility.is_none() {
573 self.column_visibility = source.column_visibility.clone();
574 }
575 if source.presence.column_sizing_info && self.column_sizing_info.is_none() {
576 self.column_sizing_info = source.column_sizing_info.clone();
577 }
578 if source.presence.pagination && self.pagination.is_none() {
579 self.pagination = source.pagination;
580 }
581 if source.presence.global_filter && self.global_filter.is_none() {
582 self.global_filter = source.global_filter.clone();
583 }
584
585 self
586 }
587
588 pub fn from_table_state_with_shape(state: &TableState, source: &Self) -> Self {
589 Self::from_table_state(state).with_presence_hint(source)
590 }
591
592 fn resolve_row_id_for_key<'a, TData>(
593 core_row_model: &RowModel<'a, TData>,
594 grouped_row_model: Option<&GroupedRowModel>,
595 field: &'static str,
596 row_key: RowKey,
597 ) -> Result<String, TanStackStateError> {
598 if let Some(grouped) = grouped_row_model
599 && let Some(index) = grouped.row_by_key(row_key)
600 && let Some(row) = grouped.row(index)
601 {
602 return Ok(row.id.as_str().to_string());
603 }
604
605 let index = core_row_model
606 .row_by_key(row_key)
607 .ok_or(TanStackStateError::UnresolvedRowKey { field, row_key })?;
608 let row = core_row_model
609 .row(index)
610 .ok_or(TanStackStateError::UnresolvedRowKey { field, row_key })?;
611 Ok(row.id.as_str().to_string())
612 }
613
614 pub fn from_table_state_with_row_models<'a, TData>(
615 state: &TableState,
616 core_row_model: &RowModel<'a, TData>,
617 grouped_row_model: Option<&GroupedRowModel>,
618 ) -> Result<Self, TanStackStateError> {
619 let mut out = Self::from_table_state(state);
620
621 out.expanded = match &state.expanding {
622 ExpandingState::All => Some(TanStackExpandedState::All(true)),
623 ExpandingState::Keys(keys) if keys.is_empty() => None,
624 ExpandingState::Keys(keys) => Some(TanStackExpandedState::Map(
625 keys.iter()
626 .map(|row_key| {
627 let row_id = Self::resolve_row_id_for_key(
628 core_row_model,
629 grouped_row_model,
630 "expanded",
631 *row_key,
632 )?;
633 Ok((row_id, true))
634 })
635 .collect::<Result<BTreeMap<_, _>, TanStackStateError>>()?,
636 )),
637 };
638
639 out.row_pinning = if state.row_pinning.top.is_empty() && state.row_pinning.bottom.is_empty()
640 {
641 None
642 } else {
643 Some(TanStackRowPinningState {
644 top: state
645 .row_pinning
646 .top
647 .iter()
648 .map(|row_key| {
649 Self::resolve_row_id_for_key(
650 core_row_model,
651 grouped_row_model,
652 "rowPinning.top",
653 *row_key,
654 )
655 })
656 .collect::<Result<Vec<_>, _>>()?,
657 bottom: state
658 .row_pinning
659 .bottom
660 .iter()
661 .map(|row_key| {
662 Self::resolve_row_id_for_key(
663 core_row_model,
664 grouped_row_model,
665 "rowPinning.bottom",
666 *row_key,
667 )
668 })
669 .collect::<Result<Vec<_>, _>>()?,
670 })
671 };
672
673 out.row_selection = if state.row_selection.is_empty() {
674 None
675 } else {
676 Some(
677 state
678 .row_selection
679 .iter()
680 .map(|row_key| {
681 let row_id = Self::resolve_row_id_for_key(
682 core_row_model,
683 grouped_row_model,
684 "rowSelection",
685 *row_key,
686 )?;
687 Ok((row_id, true))
688 })
689 .collect::<Result<BTreeMap<_, _>, TanStackStateError>>()?,
690 )
691 };
692
693 out.presence.expanded = out.expanded.is_some();
694 out.presence.row_pinning = out.row_pinning.is_some();
695 out.presence.row_selection = out.row_selection.is_some();
696
697 Ok(out)
698 }
699
700 pub fn from_table_state_with_row_model<'a, TData>(
701 state: &TableState,
702 core_row_model: &RowModel<'a, TData>,
703 ) -> Result<Self, TanStackStateError> {
704 Self::from_table_state_with_row_models(state, core_row_model, None)
705 }
706
707 pub fn from_table_state_with_row_models_and_shape<'a, TData>(
708 state: &TableState,
709 core_row_model: &RowModel<'a, TData>,
710 grouped_row_model: Option<&GroupedRowModel>,
711 source: &Self,
712 ) -> Result<Self, TanStackStateError> {
713 Ok(
714 Self::from_table_state_with_row_models(state, core_row_model, grouped_row_model)?
715 .with_presence_hint(source),
716 )
717 }
718
719 pub fn from_table_state_with_row_model_and_shape<'a, TData>(
720 state: &TableState,
721 core_row_model: &RowModel<'a, TData>,
722 source: &Self,
723 ) -> Result<Self, TanStackStateError> {
724 Self::from_table_state_with_row_models_and_shape(state, core_row_model, None, source)
725 }
726
727 pub fn to_table_state(&self) -> Result<TableState, TanStackStateError> {
728 let mut out = TableState::default();
729
730 out.sorting = self
731 .sorting
732 .iter()
733 .map(|s| SortSpec {
734 column: Arc::<str>::from(s.id.as_str()),
735 desc: s.desc,
736 })
737 .collect();
738
739 out.column_filters = self
740 .column_filters
741 .iter()
742 .map(|f| {
743 Ok(ColumnFilter {
744 column: Arc::<str>::from(f.id.as_str()),
745 value: f.value.clone(),
746 })
747 })
748 .collect::<Result<_, _>>()?;
749
750 out.global_filter = match self.global_filter.as_ref() {
751 None | Some(Value::Null) => None,
752 Some(v) => Some(v.clone()),
753 };
754
755 if let Some(p) = self.pagination {
756 out.pagination = PaginationState {
757 page_index: p.page_index,
758 page_size: p.page_size,
759 };
760 }
761
762 if !self.grouping.is_empty() {
763 out.grouping = self
764 .grouping
765 .iter()
766 .map(|s| ColumnId::from(s.as_str()))
767 .collect::<GroupingState>();
768 }
769
770 if let Some(expanded) = self.expanded.as_ref() {
771 out.expanding = match expanded {
772 TanStackExpandedState::All(true) => ExpandingState::All,
773 TanStackExpandedState::All(false) => ExpandingState::default(),
774 TanStackExpandedState::Map(map) => ExpandingState::from_iter(
775 map.iter()
776 .filter_map(|(k, v)| {
777 if !*v {
778 return None;
779 }
780 let row_id = k.parse::<u64>().ok()?;
781 Some(RowKey(row_id))
782 })
783 .collect::<Vec<_>>(),
784 ),
785 };
786
787 if let TanStackExpandedState::Map(map) = expanded {
789 for (k, v) in map {
790 if !*v {
791 continue;
792 }
793 if k.parse::<u64>().is_err() {
794 return Err(TanStackStateError::InvalidExpandedKey { row_id: k.clone() });
795 }
796 }
797 }
798 }
799
800 if let Some(pinning) = self.row_pinning.as_ref() {
801 let mut next = RowPinningState::default();
802 for k in &pinning.top {
803 let row_id = k
804 .parse::<u64>()
805 .map_err(|_| TanStackStateError::InvalidRowPinningKey { row_id: k.clone() })?;
806 next.top.push(RowKey(row_id));
807 }
808 for k in &pinning.bottom {
809 let row_id = k
810 .parse::<u64>()
811 .map_err(|_| TanStackStateError::InvalidRowPinningKey { row_id: k.clone() })?;
812 next.bottom.push(RowKey(row_id));
813 }
814 out.row_pinning = next;
815 }
816
817 if let Some(sel) = self.row_selection.as_ref() {
818 for (k, v) in sel {
819 if !*v {
820 continue;
821 }
822 let row_id =
823 k.parse::<u64>()
824 .map_err(|_| TanStackStateError::InvalidRowSelectionKey {
825 row_id: k.clone(),
826 })?;
827 out.row_selection.insert(super::RowKey(row_id));
828 }
829 }
830
831 if let Some(pinning) = self.column_pinning.as_ref() {
832 out.column_pinning = ColumnPinningState {
833 left: pinning
834 .left
835 .iter()
836 .map(|s| ColumnId::from(s.as_str()))
837 .collect(),
838 right: pinning
839 .right
840 .iter()
841 .map(|s| ColumnId::from(s.as_str()))
842 .collect(),
843 };
844 }
845
846 if let Some(vis) = self.column_visibility.as_ref() {
847 let mut next: ColumnVisibilityState = ColumnVisibilityState::default();
848 for (k, v) in vis {
849 if *v {
850 continue;
851 }
852 next.insert(ColumnId::from(k.as_str()), false);
853 }
854 out.column_visibility = next;
855 }
856
857 if !self.column_sizing.is_empty() {
858 let mut next: ColumnSizingState = ColumnSizingState::default();
859 for (k, v) in &self.column_sizing {
860 next.insert(ColumnId::from(k.as_str()), *v);
861 }
862 out.column_sizing = next;
863 }
864
865 if let Some(info) = self.column_sizing_info.as_ref() {
866 let is_resizing_column: Option<ColumnId> = match &info.is_resizing_column {
867 Value::Bool(false) | Value::Null => None,
868 Value::String(s) => Some(ColumnId::from(s.as_str())),
869 v => {
870 return Err(TanStackStateError::InvalidIsResizingColumnValue {
871 value: v.clone(),
872 });
873 }
874 };
875
876 out.column_sizing_info = ColumnSizingInfoState {
877 column_sizing_start: info
878 .column_sizing_start
879 .iter()
880 .map(|(id, size)| (ColumnId::from(id.as_str()), *size))
881 .collect(),
882 delta_offset: info.delta_offset,
883 delta_percentage: info.delta_percentage,
884 is_resizing_column,
885 start_offset: info.start_offset,
886 start_size: info.start_size,
887 };
888 }
889
890 out.column_order = self
891 .column_order
892 .iter()
893 .map(|s| ColumnId::from(s.as_str()))
894 .collect();
895
896 Ok(out)
897 }
898
899 pub fn to_table_state_with_row_models<'a, TData>(
900 &self,
901 core_row_model: &RowModel<'a, TData>,
902 grouped_row_model: Option<&GroupedRowModel>,
903 ) -> Result<TableState, TanStackStateError> {
904 fn resolve_row_key<'a, TData>(
905 core: &RowModel<'a, TData>,
906 grouped: Option<&GroupedRowModel>,
907 field: &'static str,
908 row_id: &str,
909 ) -> Result<RowKey, TanStackStateError> {
910 if let Some(grouped) = grouped
911 && let Some(index) = grouped.row_by_id(row_id)
912 {
913 let row =
914 grouped
915 .row(index)
916 .ok_or_else(|| TanStackStateError::UnresolvedRowId {
917 field,
918 row_id: row_id.to_string(),
919 })?;
920 return Ok(row.key);
921 }
922 if let Some(index) = core.row_by_id(row_id) {
923 let row = core
924 .row(index)
925 .ok_or_else(|| TanStackStateError::UnresolvedRowId {
926 field,
927 row_id: row_id.to_string(),
928 })?;
929 return Ok(row.key);
930 }
931 if let Ok(parsed) = row_id.parse::<u64>() {
932 return Ok(RowKey(parsed));
933 }
934 Err(TanStackStateError::UnresolvedRowId {
935 field,
936 row_id: row_id.to_string(),
937 })
938 }
939
940 let mut base = self.clone();
941 base.expanded = None;
942 base.row_pinning = None;
943 base.row_selection = None;
944
945 let mut out = base.to_table_state()?;
946
947 if let Some(sel) = self.row_selection.as_ref() {
948 out.row_selection = sel
949 .iter()
950 .filter(|(_k, v)| **v)
951 .map(|(k, _)| resolve_row_key(core_row_model, grouped_row_model, "rowSelection", k))
952 .collect::<Result<_, _>>()?;
953 }
954
955 if let Some(expanded) = self.expanded.as_ref() {
956 out.expanding = match expanded {
957 TanStackExpandedState::All(true) => ExpandingState::All,
958 TanStackExpandedState::All(false) => ExpandingState::default(),
959 TanStackExpandedState::Map(map) => ExpandingState::from_iter(
960 map.iter()
961 .filter(|(_k, v)| **v)
962 .map(|(k, _)| {
963 resolve_row_key(core_row_model, grouped_row_model, "expanded", k)
964 })
965 .collect::<Result<Vec<_>, _>>()?,
966 ),
967 };
968 }
969
970 if let Some(pinning) = self.row_pinning.as_ref() {
971 let mut next = RowPinningState::default();
972 for id in &pinning.top {
973 next.top.push(resolve_row_key(
974 core_row_model,
975 grouped_row_model,
976 "rowPinning.top",
977 id,
978 )?);
979 }
980 for id in &pinning.bottom {
981 next.bottom.push(resolve_row_key(
982 core_row_model,
983 grouped_row_model,
984 "rowPinning.bottom",
985 id,
986 )?);
987 }
988 out.row_pinning = next;
989 }
990
991 Ok(out)
992 }
993
994 pub fn to_table_state_with_row_model<'a, TData>(
995 &self,
996 core_row_model: &RowModel<'a, TData>,
997 ) -> Result<TableState, TanStackStateError> {
998 self.to_table_state_with_row_models(core_row_model, None)
999 }
1000}