Skip to main content

gpui_kit/foundation/
ident.rs

1use gpui::{ElementId, SharedString};
2
3/// One identity used for both the GPUI element and the semantic tree.
4///
5/// Ids come from business identity, never list position, so an assertion that
6/// targets `settings.provider.anthropic` keeps working when the row moves.
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub struct Ident(SharedString);
9
10impl Ident {
11    pub fn new(id: impl Into<SharedString>) -> Self {
12        Self(id.into())
13    }
14
15    /// Derives a child identity, for example the clear button inside a field.
16    pub fn child(&self, suffix: impl AsRef<str>) -> Self {
17        Self(SharedString::from(format!(
18            "{}.{}",
19            self.0,
20            suffix.as_ref()
21        )))
22    }
23
24    pub fn element_id(&self) -> ElementId {
25        ElementId::Name(self.0.clone())
26    }
27
28    /// An element id for one repeated part, such as a loader cell.
29    ///
30    /// Repeated visual parts have no business identity, so they get an index
31    /// here and never appear as semantic assertion targets.
32    pub fn indexed_element_id(&self, index: usize) -> ElementId {
33        ElementId::named_usize(self.0.clone(), index)
34    }
35
36    pub fn semantic_id(&self) -> SharedString {
37        self.0.clone()
38    }
39
40    pub fn as_str(&self) -> &str {
41        self.0.as_ref()
42    }
43}
44
45impl From<&'static str> for Ident {
46    fn from(value: &'static str) -> Self {
47        Self::new(value)
48    }
49}
50
51impl From<String> for Ident {
52    fn from(value: String) -> Self {
53        Self::new(value)
54    }
55}
56
57impl From<SharedString> for Ident {
58    fn from(value: SharedString) -> Self {
59        Self::new(value)
60    }
61}
62
63impl std::fmt::Display for Ident {
64    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        formatter.write_str(self.0.as_ref())
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn child_ids_are_prefixed_by_their_owner() {
75        let field = Ident::new("settings.token");
76        assert_eq!(field.child("clear").as_str(), "settings.token.clear");
77    }
78
79    #[test]
80    fn element_and_semantic_ids_share_one_string() {
81        let ident = Ident::new("gallery.primary");
82        assert_eq!(
83            ident.element_id(),
84            ElementId::Name("gallery.primary".into())
85        );
86        assert_eq!(ident.semantic_id().as_ref(), "gallery.primary");
87    }
88}