architect_api/cpty/
cpty_id.rs

1use anyhow::bail;
2use ecow::EcoString;
3use serde_with::{DeserializeFromStr, SerializeDisplay};
4
5/// Most of the time there's only one instance of a component, in which
6/// case the component kind uniquely identifies it.  If there are multiple,
7/// you may specify an instance name to distinguish them.
8#[derive(
9    Debug,
10    Clone,
11    PartialEq,
12    Eq,
13    PartialOrd,
14    Ord,
15    Hash,
16    SerializeDisplay,
17    DeserializeFromStr,
18)]
19pub struct CptyId {
20    pub kind: EcoString,
21    pub instance: Option<EcoString>,
22}
23
24impl CptyId {
25    pub const fn inline(kind: &'static str, instance: Option<&'static str>) -> Self {
26        Self {
27            kind: EcoString::inline(kind),
28            instance: match instance {
29                Some(s) => Some(EcoString::inline(s)),
30                None => None,
31            },
32        }
33    }
34}
35
36impl std::fmt::Display for CptyId {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        write!(f, "{}", self.kind)?;
39        if let Some(instance) = &self.instance {
40            write!(f, "({instance})")?;
41        }
42        Ok(())
43    }
44}
45
46impl std::str::FromStr for CptyId {
47    type Err = anyhow::Error;
48
49    fn from_str(s: &str) -> Result<Self, Self::Err> {
50        match s.split_once('(') {
51            Some((kind, instance_and_paren)) => {
52                match instance_and_paren.strip_suffix(')') {
53                    Some(instance) => {
54                        Ok(Self { kind: kind.into(), instance: Some(instance.into()) })
55                    }
56                    None => bail!("invalid cpty id, missing closing paren: {s}"),
57                }
58            }
59            None => Ok(Self { kind: s.into(), instance: None }),
60        }
61    }
62}