Skip to main content

onetaskgraph_core/
global_id.rs

1//! Qualifying a source's own id into one a user can type.
2//!
3//! A plugin never sees a [`GlobalId`]; that is why this type lives in the engine
4//! and not in the contract.
5
6use std::fmt;
7use std::str::FromStr;
8
9use onetaskgraph_plugin_api::{NativeId, SourceError, SourceName};
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13/// One item, qualified by the source it came from.
14///
15/// Rendered `<source>:<native>` and parsed by splitting on the **first** colon,
16/// so a native id may contain colons freely.
17#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
18#[serde(try_from = "String", into = "String")]
19pub struct GlobalId {
20    /// The configured source the item came from.
21    pub source: SourceName,
22    /// The source's own opaque id for it.
23    pub native: NativeId,
24}
25
26impl GlobalId {
27    /// Qualify `native` as belonging to `source`.
28    #[must_use]
29    pub fn new(source: SourceName, native: NativeId) -> Self {
30        Self { source, native }
31    }
32}
33
34impl fmt::Display for GlobalId {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        write!(f, "{}:{}", self.source, self.native)
37    }
38}
39
40impl FromStr for GlobalId {
41    type Err = SourceError;
42
43    fn from_str(value: &str) -> Result<Self, Self::Err> {
44        let Some((source, native)) = value.split_once(':') else {
45            return Err(SourceError::Config {
46                message: format!(
47                    "{value:?} is not a qualified id; write it as <source>:<id>, for example \
48                     work:ENG-1"
49                ),
50            });
51        };
52        if native.is_empty() {
53            return Err(SourceError::Config {
54                message: format!("{value:?} names a source but no id; write it as <source>:<id>"),
55            });
56        }
57        Ok(Self {
58            source: SourceName::new(source)?,
59            native: NativeId::from(native),
60        })
61    }
62}
63
64impl TryFrom<String> for GlobalId {
65    type Error = SourceError;
66
67    fn try_from(value: String) -> Result<Self, Self::Error> {
68        value.parse()
69    }
70}
71
72impl From<GlobalId> for String {
73    fn from(value: GlobalId) -> Self {
74        value.to_string()
75    }
76}