1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum Kind {
28 Struct,
30 Enum,
32 Class,
34}
35
36impl Kind {
37 const fn suffix(self) -> char {
38 match self {
39 Kind::Struct => 'V',
40 Kind::Enum => 'O',
41 Kind::Class => 'C',
42 }
43 }
44}
45
46pub fn nominal(path: &[&str], kind: Kind) -> String {
52 let mut out = String::new();
53 for (i, seg) in path.iter().enumerate() {
54 out.push_str(&seg.len().to_string());
55 out.push_str(seg);
56 if i > 0 && i + 1 < path.len() {
59 out.push(Kind::Struct.suffix());
60 }
61 }
62 out.push(kind.suffix());
63 out
64}
65
66pub fn imported(name: &str, kind: Kind) -> String {
72 format!("So{}{}{}", name.len(), name, kind.suffix())
73}
74
75pub fn again(name: &str, kind: Kind) -> String {
82 format!("AA{}{}{}", name.len(), name, kind.suffix())
83}
84
85pub fn modified_content(modifier: &str) -> String {
90 format!(
91 "{}y{}{modifier}G",
92 nominal(&["SwiftUI", "ModifiedContent"], Kind::Struct),
93 again("AnyView", Kind::Struct),
94 )
95}
96
97pub fn stdlib(name: &str, kind: Kind) -> String {
101 format!("s{}{}{}", name.len(), name, kind.suffix())
102}
103
104pub fn existential(module_and_name: &str) -> String {
106 format!("{module_and_name}_p")
107}
108
109pub fn range(inner: &str) -> String {
111 format!("Sny{inner}G")
112}
113
114pub fn nested(shorthand: &str, name: &str, kind: Kind) -> String {
119 format!("{shorthand}{}{}{}", name.len(), name, kind.suffix())
120}
121
122pub fn optional(inner: &str) -> String {
124 format!("{inner}Sg")
125}
126
127pub fn array(inner: &str) -> String {
129 format!("Say{inner}G")
130}
131
132pub fn set(inner: &str) -> String {
134 format!("Shy{inner}G")
135}
136
137pub fn bound(generic: &str, args: &[&str]) -> String {
140 format!("{generic}y{}G", args.concat())
141}
142
143pub const STRING: &str = "SS";
145pub const INT: &str = "Si";
147pub const BOOL: &str = "Sb";
149pub const DOUBLE: &str = "Sd";
151pub const DATE: &str = "10Foundation4DateV";
153pub const NEVER: &str = "s5NeverO";
155
156pub fn demangle(mangled: &str) -> String {
158 let full = if mangled.starts_with("$s") { mangled.to_string() } else { format!("$s{mangled}") };
160 match std::ffi::CString::new(full.clone()) {
161 Ok(c) => swift_runtime::demangle::demangle(&c).unwrap_or(full),
162 Err(_) => full,
163 }
164}
165
166macro_rules! swift_type {
170 (opt $($rest:tt)+) => { $crate::mangle::optional(&swift_type!($($rest)+)) };
171 ([$($rest:tt)+]) => { $crate::mangle::array(&swift_type!($($rest)+)) };
172 (again $name:ident) => { $crate::mangle::again(stringify!($name), $crate::mangle::Kind::Struct) };
173 (again enum $name:ident) => { $crate::mangle::again(stringify!($name), $crate::mangle::Kind::Enum) };
174 (stdlib enum $name:ident) => { $crate::mangle::stdlib(stringify!($name), $crate::mangle::Kind::Enum) };
175 (objc $name:ident) => { $crate::mangle::imported(stringify!($name), $crate::mangle::Kind::Class) };
176 (objc struct $name:ident) => { $crate::mangle::imported(stringify!($name), $crate::mangle::Kind::Struct) };
177 (enum $($seg:ident).+) => { $crate::mangle::nominal(&[$(stringify!($seg)),+], $crate::mangle::Kind::Enum) };
178 (class $($seg:ident).+) => { $crate::mangle::nominal(&[$(stringify!($seg)),+], $crate::mangle::Kind::Class) };
179 ($($seg:ident).+) => { $crate::mangle::nominal(&[$(stringify!($seg)),+], $crate::mangle::Kind::Struct) };
180}
181
182pub(crate) use swift_type;
183
184macro_rules! result_types {
199 ($(
200 $(#[$meta:meta])*
201 $vis:vis $name:ident = $build:expr;
202 )*) => {$(
203 $(#[$meta])*
204 $vis static $name: $crate::views::types::CachedType =
205 $crate::views::types::CachedType::new(std::sync::LazyLock::new(|| {
206 #[allow(unused_imports)]
207 use $crate::mangle::{modified, opt, ty, ty_class, ty_enum, Kind, Type};
208 $build.mangle().into_bytes()
209 }));
210 )*};
211}
212
213pub(crate) use result_types;
214
215macro_rules! modifier_types {
222 ($(
223 $(#[$meta:meta])*
224 $vis:vis $name:ident = $build:expr;
225 )*) => {$(
226 $(#[$meta])*
227 $vis static $name: $crate::views::types::ModifierType =
228 $crate::views::types::ModifierType::new(std::sync::LazyLock::new(|| {
229 #[allow(unused_imports)]
230 use $crate::mangle::{modified, opt, ty, ty_class, ty_enum, Kind, Type};
231 $build.mangle().into_bytes()
232 }));
233 )*};
234}
235
236pub(crate) use modifier_types;
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
248 fn matches_the_hand_written_manglings() {
249 assert_eq!(swift_type!(SwiftUI.EmptyVisualEffect), "7SwiftUI17EmptyVisualEffectV");
250 assert_eq!(swift_type!(SwiftUI.ContentTransition), "7SwiftUI17ContentTransitionV");
251 assert_eq!(swift_type!(SwiftUI.EnvironmentValues), "7SwiftUI17EnvironmentValuesV");
252 assert_eq!(swift_type!(SwiftUI.UnitPoint), "7SwiftUI9UnitPointV");
253 assert_eq!(swift_type!(SwiftUI.EdgeInsets), "7SwiftUI10EdgeInsetsV");
254 assert_eq!(swift_type!(SwiftUI.TextSelection), "7SwiftUI13TextSelectionV");
255 assert_eq!(swift_type!(SwiftUI.LinearGradient), "7SwiftUI14LinearGradientV");
256 assert_eq!(swift_type!(SwiftUI.LocalizedStringKey), "7SwiftUI18LocalizedStringKeyV");
257 assert_eq!(swift_type!(SwiftUI.PinnedScrollableViews), "7SwiftUI21PinnedScrollableViewsV");
258 assert_eq!(swift_type!(Foundation.URL), "10Foundation3URLV");
259 assert_eq!(swift_type!(UniformTypeIdentifiers.UTType), "22UniformTypeIdentifiers6UTTypeV");
260 assert_eq!(swift_type!(objc NSItemProvider), "So14NSItemProviderC");
261 assert_eq!(swift_type!(objc struct CGRect), "So6CGRectV");
262
263 assert_eq!(swift_type!(enum SwiftUI.Axis), "7SwiftUI4AxisO");
264 assert_eq!(swift_type!(enum SwiftUI.Font.Design), "7SwiftUI4FontV6DesignO");
265 assert_eq!(swift_type!(SwiftUI.Namespace.ID), "7SwiftUI9NamespaceV2IDV");
266
267 assert_eq!(swift_type!(opt SwiftUI.Color), "7SwiftUI5ColorVSg");
268 assert_eq!(swift_type!(opt enum SwiftUI.Axis), "7SwiftUI4AxisOSg");
269 assert_eq!(swift_type!(opt SwiftUI.Namespace.ID), "7SwiftUI9NamespaceV2IDVSg");
270 assert_eq!(swift_type!(opt enum SwiftUI.Font.Design), "7SwiftUI4FontV6DesignOSg");
271
272 assert_eq!(swift_type!([SwiftUI.EdgeInsets]), "Say7SwiftUI10EdgeInsetsVG");
273 assert_eq!(array(STRING), "SaySSG");
274 assert_eq!(array(INT), "SaySiG");
275 assert_eq!(optional(STRING), "SSSg");
276 assert_eq!(optional(BOOL), "SbSg");
277 assert_eq!(optional(DOUBLE), "SdSg");
278
279 assert_eq!(bound("7SwiftUI7BindingV", &[BOOL]), "7SwiftUI7BindingVySbG");
280 assert_eq!(bound("7SwiftUI7BindingV", &[INT]), "7SwiftUI7BindingVySiG");
281 assert_eq!(bound("7Combine5EmptyV", &[INT, NEVER]), "7Combine5EmptyVySis5NeverOG");
282
283 assert_eq!(swift_type!(again AnyView), "AA7AnyViewV");
285 assert_eq!(swift_type!(again _BlurEffect), "AA11_BlurEffectV");
286 assert_eq!(
287 modified_content(&swift_type!(again _BlurEffect)),
288 "7SwiftUI15ModifiedContentVyAA7AnyViewVAA11_BlurEffectVG"
289 );
290 assert_eq!(
291 modified_content(&bound(&swift_type!(again _ClipEffect), &[&swift_type!(again RoundedRectangle)])),
292 "7SwiftUI15ModifiedContentVyAA7AnyViewVAA11_ClipEffectVyAA16RoundedRectangleVGG"
293 );
294
295 assert_eq!(range(&nested(STRING, "Index", Kind::Struct)), "SnySS5IndexVG");
297 assert_eq!(
298 bound(&swift_type!(stdlib enum Result), &[&swift_type!(Foundation.URL), &existential("s5Error")]),
299 "s6ResultOy10Foundation3URLVs5Error_pG"
300 );
301 }
302
303 #[test]
314 fn compressor_reproduces_swiftui() {
315 use crate::mangle;
316 fn check(readable: &str, built: &mangle::Type, expected: &str) {
317 assert_eq!(built.mangle(), expected, "building {readable}");
318 }
319 check("SecureField<Text>", &mangle::ty("SecureField").of(vec![mangle::ty("Text")]), "7SwiftUI11SecureFieldVyAA4TextVG");
321 check("ToolbarItem<(), AnyView>", &mangle::ty("ToolbarItem").of(vec![mangle::Type::Void, mangle::ty("AnyView")]), "7SwiftUI11ToolbarItemVyytAA7AnyViewVG");
322 check("ProgressView<EmptyView, EmptyView>", &mangle::ty("ProgressView").of(vec![mangle::ty("EmptyView"), mangle::ty("EmptyView")]), "7SwiftUI12ProgressViewVyAA05EmptyD0VAEG");
323 check("TimelineView<AnimationTimelineSchedule, AnyView>", &mangle::ty("TimelineView").of(vec![mangle::ty("AnimationTimelineSchedule"), mangle::ty("AnyView")]), "7SwiftUI12TimelineViewVyAA09AnimationC8ScheduleVAA03AnyD0VG");
324 check("GeometryReader<AnyView>", &mangle::ty("GeometryReader").of(vec![mangle::ty("AnyView")]), "7SwiftUI14GeometryReaderVyAA7AnyViewVG");
325 check("NavigationLink<Text, AnyView>", &mangle::ty("NavigationLink").of(vec![mangle::ty("Text"), mangle::ty("AnyView")]), "7SwiftUI14NavigationLinkVyAA4TextVAA7AnyViewVG");
326 check("DisclosureGroup<Text, AnyView>", &mangle::ty("DisclosureGroup").of(vec![mangle::ty("Text"), mangle::ty("AnyView")]), "7SwiftUI15DisclosureGroupVyAA4TextVAA7AnyViewVG");
327 check("ModifiedContent<AnyView, _BlurEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_BlurEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA11_BlurEffectVG");
328 check("ModifiedContent<AnyView, _ClipEffect<RoundedRectangle>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ClipEffect").of(vec![mangle::ty("RoundedRectangle")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA11_ClipEffectVyAA16RoundedRectangleVGG");
329 check("ModifiedContent<AnyView, _ClipEffect<Circle>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ClipEffect").of(vec![mangle::ty("Circle")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA11_ClipEffectVyAA6CircleVGG");
330 check("ModifiedContent<AnyView, _ClipEffect<Capsule>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ClipEffect").of(vec![mangle::ty("Capsule")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA11_ClipEffectVyAA7CapsuleVGG");
331 check("ModifiedContent<AnyView, _ClipEffect<Ellipse>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ClipEffect").of(vec![mangle::ty("Ellipse")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA11_ClipEffectVyAA7EllipseVGG");
332 check("ModifiedContent<AnyView, _ClipEffect<Rectangle>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ClipEffect").of(vec![mangle::ty("Rectangle")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA11_ClipEffectVyAA9RectangleVGG");
333 check("ModifiedContent<AnyView, _FrameLayout>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_FrameLayout")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA12_FrameLayoutVG");
334 check("ModifiedContent<AnyView, _ScaleEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ScaleEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA12_ScaleEffectVG");
335 check("ModifiedContent<AnyView, _OffsetEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_OffsetEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA13_OffsetEffectVG");
336 check("ModifiedContent<AnyView, _ShadowEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ShadowEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA13_ShadowEffectVG");
337 check("ModifiedContent<AnyView, _OpacityEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_OpacityEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA14_OpacityEffectVG");
338 check("ModifiedContent<AnyView, _PaddingLayout>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_PaddingLayout")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA14_PaddingLayoutVG");
339 check("ModifiedContent<AnyView, _ContrastEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ContrastEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA15_ContrastEffectVG");
340 check("ModifiedContent<AnyView, _HiddenModifier>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_HiddenModifier")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA15_HiddenModifierVG");
341 check("ModifiedContent<AnyView, _RotationEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_RotationEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA15_RotationEffectVG");
342 check("ModifiedContent<AnyView, ToolbarModifier<(), TupleToolbarContent<ToolbarItem<(), AnyView>>>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("ToolbarModifier").of(vec![mangle::Type::Void, mangle::ty("TupleToolbarContent").of(vec![mangle::ty("ToolbarItem").of(vec![mangle::Type::Void, mangle::ty("AnyView")])])])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA15ToolbarModifierVyytAA05TuplegD0VyAA0G4ItemVyytAEGGGG");
343 check("ModifiedContent<AnyView, _BlendModeEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_BlendModeEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA16_BlendModeEffectVG");
344 check("ModifiedContent<AnyView, _FixedSizeLayout>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_FixedSizeLayout")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA16_FixedSizeLayoutVG");
345 check("ModifiedContent<AnyView, _GrayscaleEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_GrayscaleEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA16_GrayscaleEffectVG");
346 check("ModifiedContent<AnyView, _OverlayModifier<_ShapeView<_StrokedShape<Rectangle._Inset>, Color>>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_OverlayModifier").of(vec![mangle::ty("_ShapeView").of(vec![mangle::ty("_StrokedShape").of(vec![mangle::Type::Nominal { module: "SwiftUI", path: vec!["Rectangle", "_Inset"], kind: mangle::Kind::Struct }]), mangle::ty("Color")])])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA16_OverlayModifierVyAA06_ShapeF0VyAA08_StrokedI0VyAA9RectangleV6_InsetVGAA5ColorVGGG");
347 check("ModifiedContent<AnyView, _OverlayModifier<AnyView>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_OverlayModifier").of(vec![mangle::ty("AnyView")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA16_OverlayModifierVyAEGG");
348 check("ModifiedContent<AnyView, _BrightnessEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_BrightnessEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA17_BrightnessEffectVG");
349 check("ModifiedContent<AnyView, _Rotation3DEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_Rotation3DEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA17_Rotation3DEffectVG");
350 check("ModifiedContent<AnyView, _SaturationEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_SaturationEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA17_SaturationEffectVG");
351 check("ModifiedContent<AnyView, _AnimationModifier<Int>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_AnimationModifier").of(vec![mangle::Type::Verbatim("Si".into())])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA18_AnimationModifierVySiGG");
352 check("ModifiedContent<AnyView, _AspectRatioLayout>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_AspectRatioLayout")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA18_AspectRatioLayoutVG");
353 check("ModifiedContent<AnyView, _FocusableModifier>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_FocusableModifier")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA18_FocusableModifierVG");
354 check("ModifiedContent<AnyView, AddGestureModifier<DragGesture, DefaultGestureCombiner>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("AddGestureModifier").of(vec![mangle::ty("DragGesture"), mangle::ty("DefaultGestureCombiner")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA18AddGestureModifierVyAA04DragH0VAA07DefaultH8CombinerVGG");
355 check("ModifiedContent<AnyView, AddGestureModifier<RotateGesture, DefaultGestureCombiner>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("AddGestureModifier").of(vec![mangle::ty("RotateGesture"), mangle::ty("DefaultGestureCombiner")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA18AddGestureModifierVyAA06RotateH0VAA07DefaultH8CombinerVGG");
356 check("ModifiedContent<AnyView, AddGestureModifier<MagnifyGesture, DefaultGestureCombiner>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("AddGestureModifier").of(vec![mangle::ty("MagnifyGesture"), mangle::ty("DefaultGestureCombiner")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA18AddGestureModifierVyAA07MagnifyH0VAA07DefaultH8CombinerVGG");
357 check("ModifiedContent<AnyView, AddGestureModifier<LongPressGesture, DefaultGestureCombiner>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("AddGestureModifier").of(vec![mangle::ty("LongPressGesture"), mangle::ty("DefaultGestureCombiner")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA18AddGestureModifierVyAA09LongPressH0VAA07DefaultH8CombinerVGG");
358 check("ModifiedContent<AnyView, _DrawingGroupEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_DrawingGroupEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA19_DrawingGroupEffectVG");
359 check("ModifiedContent<AnyView, _ShaderFilterEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ShaderFilterEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA19_ShaderFilterEffectVG");
360 check("ModifiedContent<AnyView, _MaskAlignmentEffect<AnyView>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_MaskAlignmentEffect").of(vec![mangle::ty("AnyView")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA20_MaskAlignmentEffectVyAEGG");
361 check("ModifiedContent<AnyView, _TraitWritingModifier<TransitionTraitKey>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_TraitWritingModifier").of(vec![mangle::ty("TransitionTraitKey")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA21_TraitWritingModifierVyAA010TransitionG3KeyVGG");
362 check("ModifiedContent<AnyView, _TraitWritingModifier<ZIndexTraitKey>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_TraitWritingModifier").of(vec![mangle::ty("ZIndexTraitKey")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA21_TraitWritingModifierVyAA06ZIndexG3KeyVGG");
363 check("ModifiedContent<AnyView, _CompositingGroupEffect>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_CompositingGroupEffect")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA23_CompositingGroupEffectVG");
364 check("ModifiedContent<AnyView, _BackgroundStyleModifier<LinearGradient>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_BackgroundStyleModifier").of(vec![mangle::ty("LinearGradient")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA24_BackgroundStyleModifierVyAA14LinearGradientVGG");
365 check("ModifiedContent<AnyView, _BackgroundStyleModifier<RadialGradient>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_BackgroundStyleModifier").of(vec![mangle::ty("RadialGradient")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA24_BackgroundStyleModifierVyAA14RadialGradientVGG");
366 check("ModifiedContent<AnyView, _BackgroundStyleModifier<Color>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_BackgroundStyleModifier").of(vec![mangle::ty("Color")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA24_BackgroundStyleModifierVyAA5ColorVGG");
367 check("ModifiedContent<AnyView, _BackgroundStyleModifier<Material>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_BackgroundStyleModifier").of(vec![mangle::ty("Material")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA24_BackgroundStyleModifierVyAA8MaterialVGG");
368 check("ModifiedContent<AnyView, _ForegroundStyleModifier<Color>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ForegroundStyleModifier").of(vec![mangle::ty("Color")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA24_ForegroundStyleModifierVyAA5ColorVGG");
369 check("ModifiedContent<AnyView, _AppearanceActionModifier>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_AppearanceActionModifier")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA25_AppearanceActionModifierVG");
370 check("ModifiedContent<AnyView, _PreferenceWritingModifier<PreferredColorSchemeKey>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_PreferenceWritingModifier").of(vec![mangle::ty("PreferredColorSchemeKey")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA26_PreferenceWritingModifierVyAA23PreferredColorSchemeKeyVGG");
371 check("ModifiedContent<AnyView, PopoverPresentationModifier<PopoverConditionalStateProvider<AnyView>>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("PopoverPresentationModifier").of(vec![mangle::ty("PopoverConditionalStateProvider").of(vec![mangle::ty("AnyView")])])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA27PopoverPresentationModifierVyAA0G24ConditionalStateProviderVyAEGGG");
372 check("ModifiedContent<AnyView, _EnvironmentKeyWritingModifier<CoreGraphics.CGFloat>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_EnvironmentKeyWritingModifier").of(vec![mangle::Type::Nominal { module: "CoreGraphics", path: vec!["CGFloat"], kind: mangle::Kind::Struct }])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA30_EnvironmentKeyWritingModifierVy12CoreGraphics7CGFloatVGG");
373 check("ModifiedContent<AnyView, _EnvironmentKeyWritingModifier<AnyShapeStyle?>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_EnvironmentKeyWritingModifier").of(vec![mangle::opt(mangle::ty("AnyShapeStyle"))])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA30_EnvironmentKeyWritingModifierVyAA0E10ShapeStyleVSgGG");
374 check("ModifiedContent<AnyView, _EnvironmentKeyWritingModifier<ControlSize>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_EnvironmentKeyWritingModifier").of(vec![mangle::ty_enum("ControlSize")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA30_EnvironmentKeyWritingModifierVyAA11ControlSizeOGG");
375 check("ModifiedContent<AnyView, _EnvironmentKeyWritingModifier<TextAlignment>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_EnvironmentKeyWritingModifier").of(vec![mangle::ty_enum("TextAlignment")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA30_EnvironmentKeyWritingModifierVyAA13TextAlignmentOGG");
376 check("ModifiedContent<AnyView, _EnvironmentKeyWritingModifier<Font?>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_EnvironmentKeyWritingModifier").of(vec![mangle::opt(mangle::ty("Font"))])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA30_EnvironmentKeyWritingModifierVyAA4FontVSgGG");
377 check("ModifiedContent<AnyView, _EnvironmentKeyWritingModifier<Int?>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_EnvironmentKeyWritingModifier").of(vec![mangle::opt(mangle::Type::Verbatim("Si".into()))])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA30_EnvironmentKeyWritingModifierVySiSgGG");
378 check("ModifiedContent<AnyView, AccessibilityAttachmentModifier>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("AccessibilityAttachmentModifier")]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA31AccessibilityAttachmentModifierVG");
379 check("ModifiedContent<AnyView, _EnvironmentKeyTransformModifier<[AnyFontModifier]>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_EnvironmentKeyTransformModifier").of(vec![mangle::Type::Verbatim("Sa".into()).of(vec![mangle::ty_class("AnyFontModifier")])])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA32_EnvironmentKeyTransformModifierVySayAA0e4FontJ0CGGG");
380 check("ModifiedContent<AnyView, _EnvironmentKeyTransformModifier<Bool>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_EnvironmentKeyTransformModifier").of(vec![mangle::Type::Verbatim("Sb".into())])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA32_EnvironmentKeyTransformModifierVySbGG");
381 check("ModifiedContent<AnyView, PrimitiveButtonStyleContainerModifier<GlassButtonStyle>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("PrimitiveButtonStyleContainerModifier").of(vec![mangle::ty("GlassButtonStyle")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA37PrimitiveButtonStyleContainerModifierVyAA05GlasshI0VGG");
382 check("ModifiedContent<AnyView, PrimitiveButtonStyleContainerModifier<BorderedButtonStyle>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("PrimitiveButtonStyleContainerModifier").of(vec![mangle::ty("BorderedButtonStyle")])]), "7SwiftUI15ModifiedContentVyAA7AnyViewVAA37PrimitiveButtonStyleContainerModifierVyAA08BorderedhI0VGG");
383 check("ModifiedContent<ModifiedContent<AnyView, _ValueActionModifier2<Int>>, _AppearanceActionModifier>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("_ValueActionModifier2").of(vec![mangle::Type::Verbatim("Si".into())])]), mangle::ty("_AppearanceActionModifier")]), "7SwiftUI15ModifiedContentVyACyAA7AnyViewVAA21_ValueActionModifier2VySiGGAA011_AppearanceH8ModifierVG");
384 check("ModifiedContent<ModifiedContent<AnyView, TransactionalPreferenceTransformModifier<NavigationTitleKey>>, _PreferenceTransformModifier<ToolbarKey>>", &mangle::ty("ModifiedContent").of(vec![mangle::ty("ModifiedContent").of(vec![mangle::ty("AnyView"), mangle::ty("TransactionalPreferenceTransformModifier").of(vec![mangle::ty("NavigationTitleKey")])]), mangle::ty("_PreferenceTransformModifier").of(vec![mangle::ty("ToolbarKey")])]), "7SwiftUI15ModifiedContentVyACyAA7AnyViewVAA40TransactionalPreferenceTransformModifierVyAA18NavigationTitleKeyVGGAA01_hiJ0VyAA07ToolbarM0VGG");
385 check("NavigationStack<NavigationPath, AnyView>", &mangle::ty("NavigationStack").of(vec![mangle::ty("NavigationPath"), mangle::ty("AnyView")]), "7SwiftUI15NavigationStackVyAA0C4PathVAA7AnyViewVG");
386 check("_ConditionalContent<AnyView, AnyView>", &mangle::ty("_ConditionalContent").of(vec![mangle::ty("AnyView"), mangle::ty("AnyView")]), "7SwiftUI19_ConditionalContentVyAA7AnyViewVAEG");
387 check("TupleToolbarContent<ToolbarItem<(), AnyView>>", &mangle::ty("TupleToolbarContent").of(vec![mangle::ty("ToolbarItem").of(vec![mangle::Type::Void, mangle::ty("AnyView")])]), "7SwiftUI19TupleToolbarContentVyAA0D4ItemVyytAA7AnyViewVGG");
388 check("Form<AnyView>", &mangle::ty("Form").of(vec![mangle::ty("AnyView")]), "7SwiftUI4FormVyAA7AnyViewVG");
389 check("Grid<AnyView>", &mangle::ty("Grid").of(vec![mangle::ty("AnyView")]), "7SwiftUI4GridVyAA7AnyViewVG");
390 check("List<Never, AnyView>", &mangle::ty("List").of(vec![mangle::Type::Verbatim("s5NeverO".into()), mangle::ty("AnyView")]), "7SwiftUI4ListVys5NeverOAA7AnyViewVG");
391 check("Menu<Text, AnyView>", &mangle::ty("Menu").of(vec![mangle::ty("Text"), mangle::ty("AnyView")]), "7SwiftUI4MenuVyAA4TextVAA7AnyViewVG");
392 check("Gauge<AnyView, EmptyView, EmptyView, EmptyView>", &mangle::ty("Gauge").of(vec![mangle::ty("AnyView"), mangle::ty("EmptyView"), mangle::ty("EmptyView"), mangle::ty("EmptyView")]), "7SwiftUI5GaugeVyAA7AnyViewVAA05EmptyE0VA2GG");
393 check("Label<Text, Image>", &mangle::ty("Label").of(vec![mangle::ty("Text"), mangle::ty("Image")]), "7SwiftUI5LabelVyAA4TextVAA5ImageVG");
394 check("Button<Text>", &mangle::ty("Button").of(vec![mangle::ty("Text")]), "7SwiftUI6ButtonVyAA4TextVG");
395 check("Slider<EmptyView, EmptyView>", &mangle::ty("Slider").of(vec![mangle::ty("EmptyView"), mangle::ty("EmptyView")]), "7SwiftUI6SliderVyAA9EmptyViewVAEG");
396 check("Toggle<Text>", &mangle::ty("Toggle").of(vec![mangle::ty("Text")]), "7SwiftUI6ToggleVyAA4TextVG");
397 check("Binding<TextSelection?>", &mangle::ty("Binding").of(vec![mangle::opt(mangle::ty("TextSelection"))]), "7SwiftUI7BindingVyAA13TextSelectionVSgG");
398 check("ForEach<Range<Int>, Int, AnyView>", &mangle::ty("ForEach").of(vec![mangle::Type::Verbatim("Sn".into()).of(vec![mangle::Type::Verbatim("Si".into())]), mangle::Type::Verbatim("Si".into()), mangle::ty("AnyView")]), "7SwiftUI7ForEachVySnySiGSiAA7AnyViewVG");
399 check("GridRow<AnyView>", &mangle::ty("GridRow").of(vec![mangle::ty("AnyView")]), "7SwiftUI7GridRowVyAA7AnyViewVG");
400 check("Stepper<Text>", &mangle::ty("Stepper").of(vec![mangle::ty("Text")]), "7SwiftUI7StepperVyAA4TextVG");
401 check("TabView<Int, AnyView>", &mangle::ty("TabView").of(vec![mangle::Type::Verbatim("Si".into()), mangle::ty("AnyView")]), "7SwiftUI7TabViewVySiAA03AnyD0VG");
402 check("GroupBox<EmptyView, AnyView>", &mangle::ty("GroupBox").of(vec![mangle::ty("EmptyView"), mangle::ty("AnyView")]), "7SwiftUI8GroupBoxVyAA9EmptyViewVAA03AnyF0VG");
403 check("TextField<Text>", &mangle::ty("TextField").of(vec![mangle::ty("Text")]), "7SwiftUI9TextFieldVyAA0C0VG");
404 }
405
406 #[test]
408 fn counts_what_a_person_miscounted() {
409 assert_eq!("decimalDigits".len(), 13, "the bug was writing 14");
410 assert_eq!(
411 nominal(&["Foundation", "CharacterSet"], Kind::Struct),
412 "10Foundation12CharacterSetV"
413 );
414 }
415}
416
417#[derive(Clone, Debug)]
443pub enum Type {
444 Nominal { module: &'static str, path: Vec<&'static str>, kind: Kind },
446 Bound(Box<Type>, Vec<Type>),
448 Void,
450 Verbatim(String),
452 Opt(Box<Type>),
455 Pack(Box<Type>, Vec<Type>),
458}
459
460pub fn pack(base: Type, args: Vec<Type>) -> Type {
462 Type::Pack(Box::new(base), args)
463}
464
465pub fn opt(inner: Type) -> Type {
467 Type::Opt(Box::new(inner))
468}
469
470pub fn ty(name: &'static str) -> Type {
472 Type::Nominal { module: "SwiftUI", path: vec![name], kind: Kind::Struct }
473}
474
475pub fn ty_enum(name: &'static str) -> Type {
477 Type::Nominal { module: "SwiftUI", path: vec![name], kind: Kind::Enum }
478}
479
480pub fn modified(modifier: Type) -> Type {
483 ty("ModifiedContent").of(vec![ty("AnyView"), modifier])
484}
485
486pub fn ty_class(name: &'static str) -> Type {
488 Type::Nominal { module: "SwiftUI", path: vec![name], kind: Kind::Class }
489}
490
491impl Type {
492 pub fn of(self, args: Vec<Type>) -> Type {
494 Type::Bound(Box::new(self), args)
495 }
496
497 pub fn mangle(&self) -> String {
499 let mut m = Mangler::default();
500 let mut out = String::new();
501 m.write(self, &mut out);
502 out
503 }
504}
505
506#[derive(Default)]
508struct Mangler {
509 words: Vec<String>,
510 slots: Vec<String>,
512}
513
514fn split_words(ident: &str) -> Vec<String> {
516 let chars: Vec<char> = ident.chars().collect();
517 let mut out: Vec<String> = Vec::new();
518 let mut cur = String::new();
519 for (i, &c) in chars.iter().enumerate() {
520 if c == '_' {
524 if !cur.is_empty() {
525 out.push(std::mem::take(&mut cur));
526 }
527 out.push("_".to_string());
528 continue;
529 }
530 let starts = if c.is_uppercase() {
537 let prev = if i > 0 { chars[i - 1] } else { '\0' };
538 prev.is_lowercase() || prev.is_ascii_digit()
539 } else {
540 false
541 };
542 if starts && !cur.is_empty() {
543 out.push(std::mem::take(&mut cur));
544 }
545 cur.push(c);
546 }
547 if !cur.is_empty() {
548 out.push(cur);
549 }
550 out
551}
552
553impl Mangler {
554 fn identifier(&mut self, name: &str) -> String {
556 let pieces = split_words(name);
558 let mut run: Vec<Result<usize, String>> = Vec::new();
559 let mut literal = String::new();
560 for w in &pieces {
561 let known = w.len() >= 2;
562 match self.words.iter().position(|k| k == w).filter(|_| known) {
563 Some(i) => {
564 if !literal.is_empty() {
565 run.push(Err(std::mem::take(&mut literal)));
566 }
567 run.push(Ok(i));
568 }
569 None => {
570 literal.push_str(w);
571 if known {
572 self.words.push(w.clone());
573 }
574 }
575 }
576 }
577 if !literal.is_empty() {
578 run.push(Err(literal));
579 }
580 if !run.iter().any(|p| p.is_ok()) {
581 return format!("{}{}", name.len(), name);
582 }
583 let mut out = String::from("0");
584 for (i, piece) in run.iter().enumerate() {
585 match piece {
586 Ok(idx) => {
587 let last = !run[i + 1..].iter().any(|p| p.is_ok());
594 let base = if last { b'A' } else { b'a' };
595 out.push((base + *idx as u8) as char);
596 }
597 Err(text) => out.push_str(&format!("{}{}", text.len(), text)),
598 }
599 }
600 if run.last().is_some_and(|p| p.is_ok()) {
601 out.push('0');
604 }
605 out
606 }
607
608 fn substitute(&self, key: &str) -> Option<String> {
610 let i = self.slots.iter().position(|k| k == key)?;
611 (i < 26).then(|| format!("A{}", (b'A' + i as u8) as char))
612 }
613
614 fn write(&mut self, t: &Type, out: &mut String) {
615 match t {
616 Type::Void => out.push_str("yt"),
617 Type::Pack(base, args) => {
618 self.write(base, out);
619 out.push('y');
620 for a in args {
621 self.write(a, out);
622 }
623 out.push_str("_QPG");
624 }
625 Type::Opt(inner) => {
626 self.write(inner, out);
627 out.push_str("Sg");
628 }
629 Type::Verbatim(s) => out.push_str(s),
630 Type::Bound(base, args) => {
631 self.write(base, out);
632 out.push('y');
633 let mut written: Vec<String> = Vec::with_capacity(args.len());
637 for a in args {
638 let mut one = String::new();
639 self.write(a, &mut one);
640 written.push(one);
641 }
642 let mut i = 0;
643 while i < written.len() {
644 let is_sub = written[i].len() == 2
645 && written[i].starts_with('A')
646 && written[i].as_bytes()[1].is_ascii_uppercase();
647 let mut run = 1;
648 if is_sub {
649 while i + run < written.len() && written[i + run] == written[i] {
650 run += 1;
651 }
652 }
653 if run > 1 {
654 out.push('A');
655 out.push_str(&run.to_string());
656 out.push(written[i].as_bytes()[1] as char);
657 } else {
658 out.push_str(&written[i]);
659 }
660 i += run;
661 }
662 out.push('G');
663 }
664 Type::Nominal { module, path, kind } => {
665 let key = format!("{module}.{}", path.join("."));
666 if let Some(s) = self.substitute(&key) {
667 out.push_str(&s);
668 return;
669 }
670 match self.substitute(module) {
672 Some(s) => out.push_str(&s),
673 None => {
674 out.push_str(&self.identifier(module));
675 self.slots.push((*module).to_string());
676 }
677 }
678 for (i, seg) in path.iter().enumerate() {
679 let id = self.identifier(seg);
680 out.push_str(&id);
681 self.slots.push(format!("{key}#ident{i}"));
682 let k = if i + 1 == path.len() { *kind } else { Kind::Struct };
683 out.push(k.suffix());
684 self.slots.push(format!("{module}.{}", path[..=i].join(".")));
691 }
692 }
693 }
694 }
695}