1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Structured element keys — building and routing `prefix:index` keys.
//!
//! Keys are plain strings, and per-item widgets conventionally encode
//! the item's identity into them with a `:` separator: a photo grid
//! keys its cells `thumb:0`, `thumb:1`, …; a per-row dropdown keys its
//! triggers `profile:7`; the stock select widget routes option clicks
//! as `{key}:option:{value}`. This module owns the two halves of that
//! convention so apps stop hand-rolling them:
//!
//! - **Building**: [`indexed`] formats `prefix:index`.
//! - **Routing**: [`suffix`] / [`index`] take a routed key apart again
//! — and unlike the hand-rolled `strip_prefix` + `parse().ok()`
//! chain, a prefix match whose index fails to parse logs a warning
//! instead of silently swallowing the event. A dropped click that
//! "does nothing" almost always means a stale option list or a
//! key-shape mismatch; the log line says so.
//!
//! [`UiEvent::route_suffix`](crate::UiEvent::route_suffix) and
//! [`UiEvent::route_index`](crate::UiEvent::route_index) wrap these
//! for the common dispatch shape:
//!
//! ```
//! use damascene_core::prelude::*;
//! use damascene_core::key;
//!
//! // Build: one keyed cell per item.
//! fn cell(i: usize) -> El {
//! button("Open").key(key::indexed("thumb", i))
//! }
//!
//! // Event: route the click back to the item.
//! fn on_event(event: &UiEvent) -> Option<usize> {
//! event.route_index::<usize>("thumb")
//! }
//!
//! let event = UiEvent::synthetic_click(key::indexed("thumb", 42));
//! assert_eq!(on_event(&event), Some(42));
//! ```
// Lock in full per-item documentation for this module (issue #73).
use Display;
use FromStr;
/// Build the key `prefix:index` — the conventional shape for per-item
/// element keys (`thumb:42`, `row:7`). The index can be anything
/// `Display` (integer ids, uuids, enum tokens); route it back with
/// [`suffix`] / [`index`].
/// If `key` is `prefix` followed by a `:` separator, the rest of the
/// key; otherwise `None`. The separator is required — `suffix("thumbnail:3",
/// "thumb")` does *not* match, so sibling prefixes sharing a stem
/// can't shadow each other.
///
/// The suffix may itself contain further `:` segments (the select
/// widget routes `profile:7:option:3`); use [`index`] to read just the
/// first segment as a typed value.
/// Parse the first segment after `prefix:` as a `T` — `index::<u32>("profile:7:option:3",
/// "profile")` is `Some(7)`, `index::<usize>("thumb:42", "thumb")` is
/// `Some(42)`.
///
/// Returns `None` when the prefix doesn't match (the normal "not my
/// event" case, no logging) **or** when the segment fails to parse —
/// but the parse failure also logs a warning, because a prefix match
/// with an unparseable index is a bug shape, not a routing miss: a
/// stale option list, a key built with a different id type, or two
/// widgets sharing a prefix. The hand-rolled `strip_prefix` +
/// `parse().ok()` chain this replaces dropped those events silently.