submilli_engine/
mangle.rs1use std::fmt;
7
8use serde::{Deserialize, Serialize};
9
10pub const PRELUDE_PACKAGE: &str = "submilli:prelude";
11pub const USER_PACKAGE: &str = "main";
12pub const SEP: char = '#';
13const MODULE_PREFIX: &str = "mod:";
14
15#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
17pub struct MangledName(String);
18
19impl MangledName {
20 pub fn as_str(&self) -> &str {
21 &self.0
22 }
23}
24
25impl fmt::Display for MangledName {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 f.write_str(&self.0)
28 }
29}
30
31impl AsRef<str> for MangledName {
32 fn as_ref(&self) -> &str {
33 &self.0
34 }
35}
36
37pub fn package_symbol(package: &str, symbol: &str) -> MangledName {
39 MangledName(format!("{package}{SEP}{symbol}"))
40}
41
42pub fn finalizer_argument(package: &str) -> MangledName {
45 MangledName(format!("{package}{SEP}finalizer:argument"))
46}
47
48pub fn package_module_symbol(package: &str, module: &str, symbol: &str) -> MangledName {
53 MangledName(format!(
54 "{MODULE_PREFIX}{package}{SEP}{module}{SEP}{symbol}"
55 ))
56}
57
58pub fn prelude(symbol: &str) -> MangledName {
59 package_symbol(PRELUDE_PACKAGE, symbol)
60}
61
62pub fn is_builtin(name: &MangledName) -> bool {
65 let unprefixed = name
66 .as_str()
67 .strip_prefix(MODULE_PREFIX)
68 .unwrap_or(name.as_str());
69 unprefixed.starts_with("submilli:")
70}
71
72pub fn extend(parent: &MangledName, suffix: &str) -> MangledName {
73 MangledName(format!("{}{}{}", parent.as_str(), SEP, suffix))
74}
75
76pub fn property_setter(property: &str) -> String {
80 format!("@set:{property}")
81}
82
83pub fn static_member(class: &MangledName, member: &str) -> MangledName {
87 extend(&extend(class, "static"), member)
88}
89
90pub fn closure_cache(function: &MangledName) -> MangledName {
94 extend(function, "<closure>")
95}
96
97pub fn host(host_module: &str, symbol: &str) -> MangledName {
98 package_symbol(host_module, symbol)
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use std::collections::BTreeMap;
105
106 #[test]
107 fn package_symbol_shape() {
108 assert_eq!(package_symbol("main", "foo").as_str(), "main#foo");
109 assert_eq!(
110 package_symbol("submilli:uuid", "v4").as_str(),
111 "submilli:uuid#v4"
112 );
113 }
114
115 #[test]
116 fn package_module_symbol_shape() {
117 assert_eq!(
118 package_module_symbol("main", "util", "foo").as_str(),
119 "mod:main#util#foo"
120 );
121 assert_eq!(
122 package_module_symbol("@pied-piper/stripe", "internal/math", "helper").as_str(),
123 "mod:@pied-piper/stripe#internal/math#helper",
124 );
125 }
126
127 #[test]
128 fn package_and_module_symbols_cannot_collide() {
129 assert_ne!(
130 package_symbol("main#util", "foo"),
131 package_module_symbol("main", "util", "foo"),
132 );
133 }
134
135 #[test]
136 fn prelude_shape() {
137 assert_eq!(
138 prelude("string_concat").as_str(),
139 "submilli:prelude#string_concat"
140 );
141 }
142
143 #[test]
144 fn extend_composes_three_segments() {
145 let iface = prelude("String");
146 assert_eq!(
147 extend(&iface, "concat").as_str(),
148 "submilli:prelude#String#concat",
149 );
150 }
151
152 #[test]
153 fn host_shape() {
154 assert_eq!(
155 host("submilli:console", "log").as_str(),
156 "submilli:console#log"
157 );
158 }
159
160 #[test]
161 fn newtype_keyed_lookup() {
162 let mut map: BTreeMap<MangledName, u32> = BTreeMap::new();
163 map.insert(package_symbol("main", "foo"), 42);
164 assert_eq!(map.get(&package_symbol("main", "foo")), Some(&42));
165 assert_eq!(map.get(&package_symbol("main", "absent")), None);
166 }
167}