abstract_std/objects/
dependency.rs

1//! Dependency definitions for Abstract Modules
2use semver::{Comparator, Version};
3use serde::{Deserialize, Serialize};
4
5use super::module::ModuleId;
6
7/// Statically defined dependency used in-contract
8#[derive(Debug, Clone, PartialEq)]
9pub struct StaticDependency {
10    pub id: ModuleId<'static>,
11    pub version_req: &'static [&'static str],
12}
13
14impl StaticDependency {
15    pub const fn new(
16        module_id: ModuleId<'static>,
17        version_requirement: &'static [&'static str],
18    ) -> Self {
19        Self {
20            id: module_id,
21            version_req: version_requirement,
22        }
23    }
24
25    /// Iterate through the (statically provided) version requirements and ensure that they are valid.
26    pub fn check(&self) -> Result<(), semver::Error> {
27        for req in self.version_req {
28            Comparator::parse(req)?;
29        }
30        Ok(())
31    }
32
33    /// Iterates through the version requirements and checks that the provided **version** is compatible.
34    pub fn matches(&self, version: &Version) -> bool {
35        self.version_req
36            .iter()
37            .all(|req| Comparator::parse(req).unwrap().matches(version))
38    }
39}
40
41/// On-chain stored version of the module dependencies. Retrievable though raw-queries.
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43pub struct Dependency {
44    pub id: String,
45    pub version_req: Vec<Comparator>,
46}
47
48impl From<&StaticDependency> for Dependency {
49    fn from(dep: &StaticDependency) -> Self {
50        Self {
51            id: dep.id.to_string(),
52            version_req: dep.version_req.iter().map(|s| s.parse().unwrap()).collect(),
53        }
54    }
55}
56
57#[cosmwasm_schema::cw_serde]
58pub struct DependencyResponse {
59    pub id: String,
60    pub version_req: Vec<String>,
61}
62
63impl From<Dependency> for DependencyResponse {
64    fn from(dep: Dependency) -> Self {
65        Self {
66            id: dep.id,
67            version_req: dep
68                .version_req
69                .into_iter()
70                .map(|comp| comp.to_string())
71                .collect(),
72        }
73    }
74}
75
76#[cfg(test)]
77mod test {
78    #![allow(clippy::needless_borrows_for_generic_args)]
79
80    use super::*;
81
82    #[coverage_helper::test]
83    fn test_static_constructor() {
84        const VERSION_CONSTRAINT: [&str; 1] = ["^1.0.0"];
85
86        let dep = StaticDependency::new("test", &VERSION_CONSTRAINT);
87
88        assert_eq!(dep.id, "test");
89        assert_eq!(dep.version_req.to_vec(), VERSION_CONSTRAINT.to_vec());
90    }
91
92    #[coverage_helper::test]
93    fn static_check_passes() {
94        const VERSION_CONSTRAINT: [&str; 1] = ["^1.0.0"];
95
96        let dep = StaticDependency::new("test", &VERSION_CONSTRAINT);
97
98        assert!(dep.check().is_ok());
99    }
100
101    #[coverage_helper::test]
102    fn static_check_passes_without_comparator() {
103        const VERSION_CONSTRAINT: [&str; 1] = ["1.0.0"];
104
105        let dep = StaticDependency::new("test", &VERSION_CONSTRAINT);
106
107        assert!(dep.check().is_ok());
108    }
109
110    #[coverage_helper::test]
111    fn static_check_fails() {
112        const VERSION_CONSTRAINT: [&str; 1] = ["^1e.0"];
113
114        let dep = StaticDependency::new("test", &VERSION_CONSTRAINT);
115
116        assert!(dep.check().is_err());
117    }
118
119    #[coverage_helper::test]
120    fn matches_should_match_matching_versions() {
121        const VERSION_CONSTRAINT: [&str; 1] = ["^1.0.0"];
122
123        let dep = StaticDependency::new("test", &VERSION_CONSTRAINT);
124
125        assert!(dep.matches(&Version::parse("1.0.0").unwrap()));
126        assert!(dep.matches(&Version::parse("1.1.0").unwrap()));
127        assert!(dep.matches(&Version::parse("1.1.1").unwrap()));
128    }
129
130    #[coverage_helper::test]
131    fn matches_should_not_match_non_matching_versions() {
132        const VERSION_CONSTRAINT: [&str; 1] = ["^1.0.0"];
133
134        let dep = StaticDependency::new("test", &VERSION_CONSTRAINT);
135
136        assert!(!dep.matches(&Version::parse("2.0.0").unwrap()));
137        assert!(!dep.matches(&Version::parse("0.1.0").unwrap()));
138        assert!(!dep.matches(&Version::parse("0.1.1").unwrap()));
139    }
140
141    #[coverage_helper::test]
142    fn test_dependency_from_static() {
143        const VERSION_CONSTRAINT: [&str; 1] = ["^1.0.0"];
144
145        let dep = &StaticDependency::new("test", &VERSION_CONSTRAINT);
146
147        let dep: Dependency = dep.into();
148
149        assert_eq!(dep.id, "test".to_string());
150        assert_eq!(dep.version_req, vec![Comparator::parse("^1.0.0").unwrap()]);
151    }
152}