1use ui_storybook::components::shell::AppSection;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum MountPoint {
22 AppShell(AppSection),
25 Bubble,
28 DropZone,
31}
32
33#[must_use]
38pub fn parse_mount_point(query: &str) -> MountPoint {
39 if let Some(section) = parse_surface(query) {
40 return MountPoint::AppShell(section);
41 }
42 let stripped = query.strip_prefix('?').unwrap_or(query);
43 for pair in stripped.split('&') {
44 if let Some((key, value)) = pair.split_once('=')
45 && key == "mount"
46 && value == "bubble"
47 {
48 return MountPoint::Bubble;
49 }
50 }
51 MountPoint::DropZone
52}
53
54#[must_use]
61pub fn parse_surface(query: &str) -> Option<AppSection> {
62 let stripped = query.strip_prefix('?').unwrap_or(query);
63 for pair in stripped.split('&') {
64 if let Some((key, value)) = pair.split_once('=')
65 && key == "surface"
66 {
67 return parse_slug(value);
68 }
69 }
70 None
71}
72
73#[must_use]
77pub fn parse_slug(slug: &str) -> Option<AppSection> {
78 match slug {
79 "recorder" | "record" => Some(AppSection::Record),
80 "library" => Some(AppSection::Library),
81 "editor" => Some(AppSection::Editor),
82 "cursor" => Some(AppSection::Cursor),
83 "prefs" => Some(AppSection::Prefs),
84 _ => None,
85 }
86}
87
88#[must_use]
91pub fn surface_slug(section: AppSection) -> &'static str {
92 match section {
93 AppSection::Record => "recorder",
94 AppSection::Library => "library",
95 AppSection::Editor => "editor",
96 AppSection::Cursor => "cursor",
97 AppSection::Prefs => "prefs",
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn round_trip_every_section() {
107 for section in [
108 AppSection::Record,
109 AppSection::Library,
110 AppSection::Editor,
111 AppSection::Cursor,
112 AppSection::Prefs,
113 ] {
114 let slug = surface_slug(section);
115 let parsed = parse_slug(slug);
116 assert_eq!(parsed, Some(section), "slug={slug}");
117 }
118 }
119
120 #[test]
121 fn parse_surface_finds_param_with_leading_question_mark() {
122 assert_eq!(parse_surface("?surface=recorder"), Some(AppSection::Record));
123 }
124
125 #[test]
126 fn parse_surface_finds_param_without_leading_question_mark() {
127 assert_eq!(parse_surface("surface=library"), Some(AppSection::Library));
128 }
129
130 #[test]
131 fn parse_surface_finds_param_among_multiple() {
132 assert_eq!(
133 parse_surface("?foo=bar&surface=editor&baz=quux"),
134 Some(AppSection::Editor)
135 );
136 }
137
138 #[test]
139 fn parse_surface_returns_none_for_unknown_slug() {
140 assert_eq!(parse_surface("?surface=mystery"), None);
141 }
142
143 #[test]
144 fn parse_surface_returns_none_for_missing_param() {
145 assert_eq!(parse_surface("?foo=bar"), None);
146 assert_eq!(parse_surface(""), None);
147 }
148
149 #[test]
150 fn parse_surface_accepts_record_alias() {
151 assert_eq!(parse_surface("?surface=record"), Some(AppSection::Record));
154 assert_eq!(parse_surface("?surface=recorder"), Some(AppSection::Record));
155 }
156
157 #[test]
158 fn mount_point_dispatches_appshell_for_surface_query() {
159 assert_eq!(
160 parse_mount_point("?surface=recorder"),
161 MountPoint::AppShell(AppSection::Record)
162 );
163 }
164
165 #[test]
166 fn mount_point_dispatches_bubble_for_mount_query() {
167 assert_eq!(parse_mount_point("?mount=bubble"), MountPoint::Bubble);
168 assert_eq!(parse_mount_point("mount=bubble"), MountPoint::Bubble);
169 }
170
171 #[test]
172 fn mount_point_falls_back_to_drop_zone_for_unknown_query() {
173 assert_eq!(parse_mount_point(""), MountPoint::DropZone);
174 assert_eq!(parse_mount_point("?foo=bar"), MountPoint::DropZone);
175 assert_eq!(parse_mount_point("?mount=unknown"), MountPoint::DropZone);
176 }
177
178 #[test]
179 fn mount_point_appshell_wins_when_both_queries_present() {
180 assert_eq!(
184 parse_mount_point("?surface=library&mount=bubble"),
185 MountPoint::AppShell(AppSection::Library)
186 );
187 }
188}