sdf_parser_core/config/
import.rs

1use std::fmt;
2
3use schemars::JsonSchema;
4use serde::{
5    de::{Unexpected, Visitor},
6    Deserialize, Deserializer, Serialize,
7};
8use anyhow::Result;
9
10use super::Metadata;
11
12pub type PackageMetadata = Metadata;
13
14#[derive(Serialize, Deserialize, Debug, Clone, Default, JsonSchema)]
15pub struct PackageImport {
16    #[serde(rename = "pkg")]
17    pub package: ImportMetadata,
18    pub path: Option<String>,
19    #[serde(skip_serializing_if = "Vec::is_empty", default)]
20    pub types: Vec<TypeImport>,
21    #[serde(skip_serializing_if = "Vec::is_empty", default)]
22    pub states: Vec<StateImport>,
23    #[serde(skip_serializing_if = "Vec::is_empty", default)]
24    pub functions: Vec<FunctionImport>,
25}
26
27#[derive(Serialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]
28pub struct ImportMetadata {
29    pub namespace: String,
30    pub name: String,
31    pub version: String,
32}
33
34impl<'de> Deserialize<'de> for ImportMetadata {
35    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
36    where
37        D: Deserializer<'de>,
38    {
39        deserializer.deserialize_str(PackageImportMetadataVisitor)
40    }
41}
42
43struct PackageImportMetadataVisitor;
44
45impl Visitor<'_> for PackageImportMetadataVisitor {
46    type Value = ImportMetadata;
47
48    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
49        write!(
50            formatter,
51            "a string of the form `<namespace>/<name>@<version>`"
52        )
53    }
54
55    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
56    where
57        E: serde::de::Error,
58    {
59        if s.is_empty() {
60            return Err(serde::de::Error::invalid_value(Unexpected::Str(s), &self));
61        }
62
63        //check there is one "/" with content before and after
64        let split = s.split('/').collect::<Vec<&str>>();
65        let namespace = split
66            .first()
67            .ok_or(serde::de::Error::invalid_value(Unexpected::Str(s), &self))?;
68        let rest = split
69            .get(1)
70            .ok_or(serde::de::Error::invalid_value(Unexpected::Str(s), &self))?;
71
72        if namespace.is_empty() || rest.is_empty() {
73            return Err(serde::de::Error::invalid_value(Unexpected::Str(s), &self));
74        }
75
76        let split = rest.split('@').collect::<Vec<&str>>();
77        let name = split
78            .first()
79            .ok_or(serde::de::Error::invalid_value(Unexpected::Str(s), &self))?;
80        let version = split
81            .get(1)
82            .ok_or(serde::de::Error::invalid_value(Unexpected::Str(s), &self))?;
83
84        if name.is_empty() || version.is_empty() {
85            return Err(serde::de::Error::invalid_value(Unexpected::Str(s), &self));
86        }
87
88        Ok(ImportMetadata {
89            namespace: namespace.to_string(),
90            name: name.to_string(),
91            version: version.to_string(),
92        })
93    }
94}
95
96#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
97pub struct FunctionImport {
98    pub name: String,
99    pub alias: Option<String>,
100}
101
102#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
103pub struct TypeImport {
104    pub name: String,
105}
106
107#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
108pub struct StateImport {
109    pub name: String,
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115
116    #[test]
117    fn test_empty_name_does_not_parse() {
118        let yaml = "";
119
120        let result: Result<ImportMetadata, _> = serde_yaml::from_str(yaml);
121        assert!(result.is_err());
122    }
123
124    #[test]
125    fn test_name_without_forward_slash_does_not_parse() {
126        let yaml = "example-ns\\my-fn@0.0.0";
127
128        let result: Result<ImportMetadata, _> = serde_yaml::from_str(yaml);
129        let error = result.unwrap_err();
130
131        assert_eq!(
132            error.to_string(),
133            "invalid value: string \"example-ns\\\\my-fn@0.0.0\", expected a string of the form `<namespace>/<name>@<version>`"
134        )
135    }
136
137    #[test]
138    fn test_name_without_at_sign_does_not_parse() {
139        let yaml = "example-ns/my-fna0.0.0";
140
141        let result: Result<ImportMetadata, _> = serde_yaml::from_str(yaml);
142        let error = result.unwrap_err();
143
144        assert_eq!(
145            error.to_string(),
146            "invalid value: string \"example-ns/my-fna0.0.0\", expected a string of the form `<namespace>/<name>@<version>`"
147        )
148    }
149
150    #[test]
151    fn test_valid_name_parses() {
152        let yaml = "example-ns/my-fn@0.0.0";
153
154        let result: Result<ImportMetadata, _> = serde_yaml::from_str(yaml);
155        assert!(result.is_ok());
156    }
157}