use std::collections::HashMap;
use std::sync::{Mutex, OnceLock};
fn r(x: f64) -> String {
let s = format!("{:.2}", x);
let s = s.trim_end_matches('0');
let s = s.trim_end_matches('.');
s.to_string()
}
fn cube(inner_unit: &str, size: i32) -> String {
let s = size as f64;
let h = size / 2;
let a = r(s * 0.44);
let b = r(s * 0.21);
let d = r(s * 0.44);
let e = r(s * 0.06);
let f = r(s * 0.28);
let p06 = r(s * 0.06);
let p28 = r(s * 0.28);
let p49 = r(s * 0.49);
let p72 = r(s * 0.72);
let p94 = r(s * 0.94);
let p50 = r(s * 0.50);
format!(
"<g class=\"icon-shadow\" transform=\"translate(-{h},-{h})\">\
<polygon points=\"{p50},{p06} {p94},{p28} {p50},{p49} {p06},{p28}\" fill=\"url(#g-face-top)\" stroke=\"#3d5d00\" stroke-width=\"1\"/>\
<polygon points=\"{p50},{p49} {p94},{p28} {p94},{p72} {p50},{p94}\" fill=\"url(#g-face-side)\" stroke=\"#3d5d00\" stroke-width=\"1\"/>\
<polygon points=\"{p06},{p28} {p50},{p49} {p50},{p94} {p06},{p72}\" fill=\"url(#g-face-front)\" stroke=\"#3d5d00\" stroke-width=\"1\"/>\
<g transform=\"matrix({a},{b},0,{d},{e},{f})\" stroke-linejoin=\"round\">{inner_unit}</g>\
</g>"
)
}
const NOTEBOOK_GLYPH: &str = r##"
<g stroke="white" stroke-width="0.045" stroke-linecap="round" fill="none">
<line x1="0.18" y1="0.32" x2="0.82" y2="0.32"/>
<line x1="0.18" y1="0.52" x2="0.82" y2="0.52"/>
<line x1="0.18" y1="0.72" x2="0.60" y2="0.72"/>
</g>
"##;
const DOCKER_GLYPH: &str = r##"
<g fill="white">
<rect x="0.10" y="0.30" width="0.34" height="0.18"/>
<rect x="0.56" y="0.30" width="0.34" height="0.18"/>
<rect x="0.10" y="0.56" width="0.34" height="0.18"/>
<rect x="0.56" y="0.56" width="0.34" height="0.18"/>
</g>
"##;
const NEURAL_GLYPH: &str = r##"
<g stroke="white" stroke-width="0.032" fill="white">
<line x1="0.50" y1="0.50" x2="0.20" y2="0.22"/>
<line x1="0.50" y1="0.50" x2="0.80" y2="0.22"/>
<line x1="0.50" y1="0.50" x2="0.20" y2="0.78"/>
<line x1="0.50" y1="0.50" x2="0.80" y2="0.78"/>
<circle cx="0.50" cy="0.50" r="0.085"/>
<circle cx="0.20" cy="0.22" r="0.055"/>
<circle cx="0.80" cy="0.22" r="0.055"/>
<circle cx="0.20" cy="0.78" r="0.055"/>
<circle cx="0.80" cy="0.78" r="0.055"/>
</g>
"##;
fn boxed(inner: &str) -> String {
format!(
"<g class=\"icon-shadow\">\
<rect x=\"-35\" y=\"-35\" width=\"70\" height=\"70\" rx=\"8\" fill=\"url(#g-box-orange)\" stroke=\"#c2410c\" stroke-width=\"1.5\"/>\
{inner}\
</g>"
)
}
const SEND_GLYPH: &str = r##"
<g transform="translate(-13, -13)">
<line x1="28" y1="2" x2="13" y2="17" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<polygon points="28,2 21,28 13,17 2,11 28,2" fill="white" stroke="white" stroke-width="1.2" stroke-linejoin="round"/>
</g>
"##;
const ZAP_GLYPH: &str = r##"
<polygon points="2,-18 -12,4 -1,4 -3,18 11,-4 0,-4 2,-18" fill="white" stroke="white" stroke-width="1.2" stroke-linejoin="round"/>
"##;
const ARCHIVE_GLYPH: &str = r##"
<g transform="translate(0,-2)">
<rect x="-20" y="-15" width="40" height="9" rx="2" fill="white"/>
<path d="M -16,-6 V 16 a 2,2 0 0 0 2,2 H 14 a 2,2 0 0 0 2,-2 V -6" fill="none" stroke="white" stroke-width="2" stroke-linejoin="round"/>
<line x1="-6" y1="3" x2="6" y2="3" stroke="white" stroke-width="2" stroke-linecap="round"/>
</g>
"##;
const CLOUD_GLYPH: &str = r##"
<path d="M -10,12 a 12,12 0 1 1 5,-23 a 16,16 0 0 1 22,15 a 9,9 0 0 1 -2,17 H -10 a 9,9 0 0 1 0,-9 z"
fill="white" stroke="white" stroke-width="1" stroke-linejoin="round"/>
"##;
const USER_CIRCLE: &str = r##"
<g class="icon-shadow">
<circle r="38" fill="url(#g-user-blue)"/>
<circle cy="-9" r="10" fill="white"/>
<path d="M -18,20 a 18,14 0 0 1 36,0 z" fill="white"/>
</g>
"##;
const CYLINDER: &str = r##"
<g class="icon-shadow" transform="translate(-35, -35)">
<ellipse cx="35" cy="12" rx="28" ry="8" fill="#fdba74" stroke="#c2410c" stroke-width="1.4"/>
<path d="M 7,12 V 58 a 28,8 0 0 0 56,0 V 12" fill="url(#g-cyl-orange)" stroke="#c2410c" stroke-width="1.4"/>
<ellipse cx="35" cy="12" rx="28" ry="8" fill="none" stroke="#c2410c" stroke-width="1.2"/>
<path d="M 7,28 a 28,8 0 0 0 56,0" fill="none" stroke="#c2410c" stroke-width="0.8" opacity="0.55"/>
<path d="M 7,42 a 28,8 0 0 0 56,0" fill="none" stroke="#c2410c" stroke-width="0.8" opacity="0.55"/>
</g>
"##;
const KEY: &str = r##"
<g stroke="#d97706" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
<circle cx="-10" cy="0" r="6" fill="#fff4e0"/>
<line x1="-4" y1="-2" x2="16" y2="-2"/>
<line x1="10" y1="-2" x2="10" y2="3"/>
<line x1="16" y1="-2" x2="16" y2="5"/>
</g>
"##;
const HEX_AGENT: &str = r##"
<g class="icon-shadow">
<polygon points="-30,-17 0,-32 30,-17 30,17 0,32 -30,17"
fill="#e9f5d0" stroke="#3d5d00" stroke-width="1.6"/>
<!-- head -->
<circle cx="0" cy="-7" r="6.5" fill="#76b900"/>
<!-- glasses: two dots + bridge -->
<circle cx="-2.5" cy="-7" r="1" fill="#e9f5d0"/>
<circle cx="2.5" cy="-7" r="1" fill="#e9f5d0"/>
<!-- body / shoulders -->
<path d="M -14,18 Q -14,4 0,4 Q 14,4 14,18 Z" fill="#76b900"/>
<!-- collar V -->
<path d="M -4,6 L 0,12 L 4,6" fill="#e9f5d0" stroke="none"/>
</g>
"##;
const GEAR_GLYPH: &str = r##"
<g fill="white" stroke="white" stroke-width="1.4" stroke-linejoin="round">
<path d="M 0,-15 L 4,-15 L 5,-11 L 9,-9 L 12,-12 L 15,-9 L 12,-6 L 14,-2 L 18,-1 L 18,3
L 14,4 L 12,8 L 15,11 L 12,14 L 9,11 L 5,13 L 4,17 L 0,17 L -1,13 L -5,11
L -8,14 L -11,11 L -8,8 L -10,4 L -14,3 L -14,-1 L -10,-2 L -8,-6 L -11,-9
L -8,-12 L -5,-9 L -1,-11 L 0,-15 Z"
transform="translate(2, -2) scale(0.85)"/>
<circle cx="2" cy="-2" r="4" fill="#f59e0b" stroke="#f59e0b"/>
</g>
"##;
const FOLDER_GLYPH: &str = r##"
<g fill="white" stroke="white" stroke-width="1.2" stroke-linejoin="round">
<path d="M -22,-12 L -8,-12 L -4,-7 L 22,-7 L 22,16 L -22,16 Z"/>
</g>
"##;
const FILES_STACK: &str = r##"
<g fill="white" stroke="white" stroke-width="1.2" stroke-linejoin="round">
<!-- back document -->
<rect x="-18" y="-18" width="20" height="26" rx="2" fill="white" opacity="0.6"/>
<!-- front document -->
<rect x="-8" y="-8" width="22" height="28" rx="2" fill="white"/>
<line x1="-3" y1="-1" x2="10" y2="-1" stroke="#d97706" stroke-width="1.2"/>
<line x1="-3" y1="4" x2="10" y2="4" stroke="#d97706" stroke-width="1.2"/>
<line x1="-3" y1="9" x2="6" y2="9" stroke="#d97706" stroke-width="1.2"/>
</g>
"##;
const CHECKLIST_GLYPH: &str = r##"
<g stroke="#94a3b8" fill="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="-22" y="-22" width="44" height="44" rx="2" fill="white"/>
<!-- 3 rows of checkbox + line -->
<polyline points="-15,-14 -12,-11 -8,-16" fill="none" stroke="#475569" stroke-width="2"/>
<line x1="-4" y1="-13" x2="14" y2="-13" stroke="#94a3b8"/>
<polyline points="-15,-2 -12,1 -8,-4" fill="none" stroke="#475569" stroke-width="2"/>
<line x1="-4" y1="-1" x2="14" y2="-1" stroke="#94a3b8"/>
<polyline points="-15,10 -12,13 -8,8" fill="none" stroke="#475569" stroke-width="2"/>
<line x1="-4" y1="11" x2="14" y2="11" stroke="#94a3b8"/>
</g>
"##;
const MAGNIFIER_GLYPH: &str = r##"
<g fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<circle cx="-3" cy="-3" r="11" fill="white" stroke="white" stroke-width="0"/>
<circle cx="-3" cy="-3" r="9" fill="none" stroke="#f59e0b" stroke-width="3"/>
<line x1="5" y1="5" x2="14" y2="14" stroke="white" stroke-width="4"/>
</g>
"##;
const HEX_NET_GLYPH: &str = r##"
<g stroke="white" stroke-width="0.04" fill="white">
<!-- 6 connection lines + 6 nodes for a network pattern -->
<line x1="0.50" y1="0.20" x2="0.20" y2="0.50"/>
<line x1="0.50" y1="0.20" x2="0.80" y2="0.50"/>
<line x1="0.20" y1="0.50" x2="0.50" y2="0.80"/>
<line x1="0.80" y1="0.50" x2="0.50" y2="0.80"/>
<line x1="0.20" y1="0.50" x2="0.80" y2="0.50"/>
<line x1="0.50" y1="0.20" x2="0.50" y2="0.80"/>
<circle cx="0.50" cy="0.20" r="0.055"/>
<circle cx="0.20" cy="0.50" r="0.055"/>
<circle cx="0.80" cy="0.50" r="0.055"/>
<circle cx="0.50" cy="0.80" r="0.055"/>
</g>
"##;
fn halo(inner: &str, size: i32) -> String {
let s = size / 2;
format!(
"<circle r=\"{s}\" fill=\"#76b900\" opacity=\"0.10\"/>\
<circle r=\"{}\" fill=\"#76b900\" opacity=\"0.08\"/>\
{inner}",
s - 6
)
}
fn aws_tile(color: &str, dark: &str, glyph: &str, hero: bool) -> String {
let size = if hero { 80 } else { 64 };
let h = size / 2;
format!(
"<g class=\"icon-shadow\">\
<rect x=\"-{h}\" y=\"-{h}\" width=\"{size}\" height=\"{size}\" rx=\"10\" fill=\"{color}\" stroke=\"{dark}\" stroke-width=\"1.4\"/>\
{glyph}\
</g>"
)
}
const AMPLIFY_GLYPH: &str = r##"
<g fill="white" stroke="none">
<!-- stylized A (Amplify mark) -->
<path d="M -14,12 L -3,-12 L 3,-12 L 14,12 L 7,12 L 4,5 L -4,5 L -7,12 Z"/>
<path d="M -1,-1 L 1,-1 L 2,2 L -2,2 Z"/>
</g>
"##;
const LEX_GLYPH: &str = r##"
<g fill="white" stroke="none">
<!-- chat bubble + dots -->
<path d="M -14,-8 a 4,4 0 0 1 4,-4 H 10 a 4,4 0 0 1 4,4 V 4 a 4,4 0 0 1 -4,4 H -2 L -8,14 V 8 H -10 a 4,4 0 0 1 -4,-4 Z"/>
<circle cx="-6" cy="-2" r="1.6" fill="#C925D1"/>
<circle cx="0" cy="-2" r="1.6" fill="#C925D1"/>
<circle cx="6" cy="-2" r="1.6" fill="#C925D1"/>
</g>
"##;
const LAMBDA_GLYPH: &str = r##"
<text x="0" y="11" text-anchor="middle"
style="font-size:38px;font-weight:700;fill:white;font-family:Georgia,serif">λ</text>
"##;
const CONNECT_GLYPH: &str = r##"
<g fill="white" stroke="none">
<!-- phone handset -->
<path d="M -12,-8 q 0,-4 4,-4 l 4,0 q 3,0 4,3 l 1,4 q 1,3 -2,5 l -3,2 q 3,6 9,9 l 2,-3 q 2,-3 5,-2 l 4,1 q 3,1 3,4 l 0,4 q 0,4 -4,4 q -16,0 -27,-11 q -11,-11 -11,-16 z"/>
</g>
"##;
const DYNAMODB_GLYPH: &str = r##"
<g fill="white" stroke="white" stroke-width="0" stroke-linejoin="round">
<!-- stacked database discs -->
<ellipse cx="0" cy="-9" rx="12" ry="3.5"/>
<path d="M -12,-9 V -1 a 12,3.5 0 0 0 24,0 V -9" fill="white"/>
<ellipse cx="0" cy="-1" rx="12" ry="3.5" fill="#4D72F3" opacity="0.35"/>
<path d="M -12,0 V 8 a 12,3.5 0 0 0 24,0 V 0" fill="white"/>
<ellipse cx="0" cy="8" rx="12" ry="3.5" fill="#4D72F3" opacity="0.35"/>
<path d="M -12,9 V 12 a 12,3.5 0 0 0 24,0 V 9" fill="white"/>
</g>
"##;
const BEDROCK_GLYPH: &str = r##"
<g fill="white" stroke="white" stroke-width="1.6" stroke-linecap="round">
<!-- brain-ish network -->
<circle cx="0" cy="0" r="3.5" fill="white"/>
<circle cx="-10" cy="-7" r="2.5" fill="white"/>
<circle cx="10" cy="-7" r="2.5" fill="white"/>
<circle cx="-10" cy="7" r="2.5" fill="white"/>
<circle cx="10" cy="7" r="2.5" fill="white"/>
<line x1="-7" y1="-5" x2="-2" y2="-1"/>
<line x1="7" y1="-5" x2="2" y2="-1"/>
<line x1="-7" y1="5" x2="-2" y2="1"/>
<line x1="7" y1="5" x2="2" y2="1"/>
</g>
"##;
const S3_GLYPH: &str = r##"
<g fill="white" stroke="none">
<!-- bucket -->
<path d="M -12,-8 L 12,-8 L 10,12 a 2,2 0 0 1 -2,2 L -8,14 a 2,2 0 0 1 -2,-2 Z"/>
<ellipse cx="0" cy="-8" rx="12" ry="3" fill="#7AA116"/>
<ellipse cx="0" cy="-8" rx="12" ry="3" fill="none" stroke="white" stroke-width="1.2"/>
</g>
"##;
const KENDRA_GLYPH: &str = r##"
<g fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round">
<!-- magnifier -->
<circle cx="-3" cy="-3" r="8"/>
<line x1="3" y1="3" x2="11" y2="11"/>
</g>
"##;
const CUSTOMER_PERSON: &str = r##"
<g class="icon-shadow">
<circle r="34" fill="url(#g-user-blue)"/>
<circle cy="-8" r="9" fill="white"/>
<path d="M -16,18 a 16,12 0 0 1 32,0 z" fill="white"/>
</g>
"##;
const INTERNET_CLOUD: &str = r##"
<g class="icon-shadow">
<path d="M -22,8 a 14,14 0 1 1 6,-26 a 18,18 0 0 1 26,16 a 11,11 0 0 1 -3,21 H -22 a 11,11 0 0 1 -7,-11 z"
fill="#dbeafe" stroke="#1d4ed8" stroke-width="1.6" stroke-linejoin="round"/>
<text x="0" y="6" text-anchor="middle"
style="font-size:9.5px;font-weight:700;fill:#1d4ed8;letter-spacing:0.05em">WWW</text>
</g>
"##;
const SLACK_BADGE: &str = r##"
<g class="icon-shadow">
<rect x="-16" y="-16" width="32" height="32" rx="7" fill="white" stroke="#cbd5e1" stroke-width="1"/>
<!-- Slack hash mark, 4 coloured arms -->
<g stroke-linecap="round" stroke-width="3.6" fill="none">
<line x1="-7" y1="-3" x2="7" y2="-3" stroke="#e01e5a"/>
<line x1="-7" y1="3" x2="7" y2="3" stroke="#36c5f0"/>
<line x1="-3" y1="-7" x2="-3" y2="7" stroke="#2eb67d"/>
<line x1="3" y1="-7" x2="3" y2="7" stroke="#ecb22e"/>
</g>
</g>
"##;
const PLUS_MARK: &str = r##"
<g fill="#475569" stroke="#475569" stroke-width="2" stroke-linecap="round">
<line x1="-6" y1="0" x2="6" y2="0"/>
<line x1="0" y1="-6" x2="0" y2="6"/>
</g>
"##;
const AWS_LOGO: &str = r##"
<g class="icon-shadow">
<rect x="-18" y="-18" width="36" height="36" rx="4" fill="#232f3e"/>
<text x="0" y="2" text-anchor="middle"
style="font-size:14px;font-weight:800;fill:white;font-family:Arial,Helvetica,sans-serif;letter-spacing:-0.4px">aws</text>
<path d="M -9,8 Q 0,13 9,8" fill="none" stroke="#ff9900" stroke-width="1.8" stroke-linecap="round"/>
</g>
"##;
const SITE_GLOBE: &str = r##"
<g fill="none" stroke="#475569" stroke-width="1.4" stroke-linecap="round">
<circle r="9"/>
<ellipse cx="0" cy="0" rx="4" ry="9"/>
<line x1="-9" y1="0" x2="9" y2="0"/>
</g>
"##;
fn step_badge(n: i32) -> String {
format!(
"<circle r=\"14\" fill=\"#fff4e0\" stroke=\"#d97706\" stroke-width=\"1.8\"/>\
<text x=\"0\" y=\"4.5\" text-anchor=\"middle\" style=\"font-size:14px;font-weight:700;fill:#92400e\">{n}</text>"
)
}
pub fn builtin_icon(key: &str) -> Option<String> {
Some(match key {
"user" => USER_CIRCLE.to_string(),
"notebook" => cube(NOTEBOOK_GLYPH, 80),
"boxes" => cube(DOCKER_GLYPH, 80),
"neural" => halo(&cube(NEURAL_GLYPH, 100), 124),
"neural-sm" => cube(HEX_NET_GLYPH, 80),
"send" => boxed(SEND_GLYPH),
"zap" => boxed(ZAP_GLYPH),
"archive" => boxed(ARCHIVE_GLYPH),
"cloud" => boxed(CLOUD_GLYPH),
"cylinder" => CYLINDER.to_string(),
"key" => KEY.to_string(),
"hex-agent" => HEX_AGENT.to_string(),
"gear" => boxed(GEAR_GLYPH),
"folder" => boxed(FOLDER_GLYPH),
"files" => boxed(FILES_STACK),
"checklist" => boxed(CHECKLIST_GLYPH),
"magnifier" => boxed(MAGNIFIER_GLYPH),
"aws-amplify" => aws_tile("#DD344C", "#a31836", AMPLIFY_GLYPH, false),
"aws-lex" => aws_tile("#C925D1", "#8a1690", LEX_GLYPH, false),
"aws-lambda" => aws_tile("#ED7100", "#a44a00", LAMBDA_GLYPH, true),
"aws-connect" => aws_tile("#DD344C", "#a31836", CONNECT_GLYPH, false),
"aws-dynamodb" => aws_tile("#4D72F3", "#2a4ec2", DYNAMODB_GLYPH, false),
"aws-bedrock" => aws_tile("#01A88D", "#017364", BEDROCK_GLYPH, false),
"aws-s3" => aws_tile("#7AA116", "#506b0e", S3_GLYPH, false),
"aws-kendra" => aws_tile("#C925D1", "#8a1690", KENDRA_GLYPH, false),
"customer-person" => CUSTOMER_PERSON.to_string(),
"internet-cloud" => INTERNET_CLOUD.to_string(),
"step-1" => step_badge(1),
"step-2" => step_badge(2),
"step-3" => step_badge(3),
"aws-logo" => AWS_LOGO.to_string(),
"site-globe" => SITE_GLOBE.to_string(),
"slack" => SLACK_BADGE.to_string(),
"plus" => PLUS_MARK.to_string(),
_ => return None,
})
}
fn registry() -> &'static Mutex<HashMap<String, String>> {
static REG: OnceLock<Mutex<HashMap<String, String>>> = OnceLock::new();
REG.get_or_init(|| Mutex::new(HashMap::new()))
}
pub fn register_icon(key: &str, bytes: &[u8], is_png: bool) {
const SIZE: i32 = 64;
let half = SIZE / 2;
let frag = if is_png {
let b64 = base64_encode(bytes);
format!(
"<image href=\"data:image/png;base64,{b64}\" x=\"-{half}\" y=\"-{half}\" width=\"{SIZE}\" height=\"{SIZE}\"/>"
)
} else {
let raw = String::from_utf8_lossy(bytes);
format!(
"<svg x=\"-{half}\" y=\"-{half}\" width=\"{SIZE}\" height=\"{SIZE}\" overflow=\"visible\">{raw}</svg>"
)
};
registry().lock().unwrap().insert(key.to_string(), frag);
}
pub fn register_icon_svg(key: &str, fragment: &str) {
registry()
.lock()
.unwrap()
.insert(key.to_string(), fragment.to_string());
}
pub fn get_icon(key: &str) -> Option<String> {
if let Some(b) = builtin_icon(key) {
return Some(b);
}
registry().lock().unwrap().get(key).cloned()
}
fn base64_encode(data: &[u8]) -> String {
const T: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
for chunk in data.chunks(3) {
let b = [
chunk[0],
*chunk.get(1).unwrap_or(&0),
*chunk.get(2).unwrap_or(&0),
];
let n = ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | (b[2] as u32);
out.push(T[((n >> 18) & 63) as usize] as char);
out.push(T[((n >> 12) & 63) as usize] as char);
out.push(if chunk.len() > 1 {
T[((n >> 6) & 63) as usize] as char
} else {
'='
});
out.push(if chunk.len() > 2 {
T[(n & 63) as usize] as char
} else {
'='
});
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builtin_icons_present() {
for k in [
"user",
"hex-agent",
"folder",
"checklist",
"neural-sm",
"cylinder",
] {
assert!(builtin_icon(k).is_some(), "missing builtin {k}");
}
assert!(builtin_icon("nope-xyz").is_none());
}
#[test]
fn cube_uses_tidy_rounding() {
let c = cube("", 80);
assert!(c.contains("translate(-40,-40)"));
assert!(c.contains("matrix(35.2,16.8,0,35.2,4.8,22.4)"));
}
#[test]
fn base64_roundtrip_known() {
assert_eq!(base64_encode(b"M"), "TQ==");
assert_eq!(base64_encode(b"Ma"), "TWE=");
assert_eq!(base64_encode(b"Man"), "TWFu");
}
#[test]
fn register_and_get() {
register_icon_svg("test:custom", "<circle r=\"5\"/>");
assert_eq!(
get_icon("test:custom").as_deref(),
Some("<circle r=\"5\"/>")
);
}
}