Fork of daniellemaywood.uk/gleam — Wasm codegen work
3.8 kB
138 lines
1use std::fmt;
2use std::str::FromStr;
3
4use crate::error::Result;
5use crate::io::make_relative;
6use camino::{Utf8Path, Utf8PathBuf};
7use ecow::EcoString;
8use hexpm::version::Range;
9use serde::de::{self, Deserializer, MapAccess, Visitor};
10use serde::ser::{Serialize, SerializeMap, Serializer};
11use serde::Deserialize;
12
13#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
14#[serde(untagged, remote = "Self")]
15pub enum Requirement {
16 Hex { version: Range },
17 Path { path: Utf8PathBuf },
18 Git { git: EcoString },
19}
20
21impl Requirement {
22 pub fn hex(range: &str) -> Requirement {
23 Requirement::Hex {
24 version: Range::new(range.to_string()),
25 }
26 }
27
28 pub fn path(path: &str) -> Requirement {
29 Requirement::Path { path: path.into() }
30 }
31
32 pub fn git(url: &str) -> Requirement {
33 Requirement::Git { git: url.into() }
34 }
35
36 pub fn to_toml(&self, root_path: &Utf8Path) -> String {
37 match self {
38 Requirement::Hex { version: range } => {
39 format!(r#"{{ version = "{}" }}"#, range)
40 }
41 Requirement::Path { path } => {
42 format!(
43 r#"{{ path = "{}" }}"#,
44 make_relative(root_path, path).as_str().replace('\\', "/")
45 )
46 }
47 Requirement::Git { git: url } => format!(r#"{{ git = "{}" }}"#, url),
48 }
49 }
50}
51
52// Serialization
53
54impl Serialize for Requirement {
55 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
56 where
57 S: Serializer,
58 {
59 let mut map = serializer.serialize_map(Some(1))?;
60 match self {
61 Requirement::Hex { version: range } => map.serialize_entry("version", range)?,
62 Requirement::Path { path } => map.serialize_entry("path", path)?,
63 Requirement::Git { git: url } => map.serialize_entry("git", url)?,
64 }
65 map.end()
66 }
67}
68
69// Deserialization
70
71#[derive(Debug, Copy, Clone)]
72pub struct Void;
73
74impl FromStr for Requirement {
75 type Err = Void;
76
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
78 Ok(Requirement::hex(s))
79 }
80}
81
82struct RequirementVisitor;
83
84impl<'de> Visitor<'de> for RequirementVisitor {
85 type Value = Requirement;
86
87 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
88 formatter.write_str("string or map")
89 }
90
91 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
92 where
93 E: de::Error,
94 {
95 Ok(FromStr::from_str(value).expect("expected string"))
96 }
97
98 fn visit_map<M>(self, visitor: M) -> Result<Self::Value, M::Error>
99 where
100 M: MapAccess<'de>,
101 {
102 Requirement::deserialize(de::value::MapAccessDeserializer::new(visitor))
103 }
104}
105
106impl<'de> Deserialize<'de> for Requirement {
107 fn deserialize<D>(deserializer: D) -> Result<Requirement, D::Error>
108 where
109 D: Deserializer<'de>,
110 {
111 deserializer.deserialize_any(RequirementVisitor)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117
118 use super::*;
119 use std::collections::HashMap;
120
121 #[test]
122 fn read_requirement() {
123 let toml = r#"
124 short = "~> 0.5"
125 hex = { version = "~> 1.0.0" }
126 local = { path = "/path/to/package" }
127 github = { git = "https://github.com/gleam-lang/otp.git" }
128 "#;
129 let deps: HashMap<String, Requirement> = toml::from_str(toml).unwrap();
130 assert_eq!(deps["short"], Requirement::hex("~> 0.5"));
131 assert_eq!(deps["hex"], Requirement::hex("~> 1.0.0"));
132 assert_eq!(deps["local"], Requirement::path("/path/to/package"));
133 assert_eq!(
134 deps["github"],
135 Requirement::git("https://github.com/gleam-lang/otp.git")
136 );
137 }
138}