1pub mod spec {
5 use base64ct::{Base64Url, Encoding};
6 use displaydoc::Display;
7 use indexmap::{IndexMap, IndexSet};
8 use serde::Deserialize;
9 use sha3::{Digest, Sha3_256};
10 use thiserror::Error;
11
12 use std::{path::PathBuf, str};
13
14 #[derive(Debug, Clone, Deserialize)]
15 pub struct Dep {
16 pub r#type: String,
17 pub lib_names: Vec<String>,
18 }
19
20 #[derive(Debug, Clone, Deserialize)]
21 pub struct FeatureLayout {
22 pub needed: Option<IndexSet<String>>,
23 pub conflicting: Option<IndexSet<String>>,
24 }
25
26 #[derive(Debug, Clone, Deserialize)]
27 pub struct Env {
28 pub spec: String,
29 pub deps: IndexMap<String, Dep>,
30 pub features: Option<FeatureLayout>,
31 }
32
33 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize)]
34 pub struct Repo {
35 pub path: PathBuf,
36 }
37
38 #[derive(Debug, Clone, Deserialize)]
52 pub struct LabelledPackageMetadata {
53 pub envs: IndexMap<String, Env>,
54 pub repo: Option<Repo>,
55 }
56
57 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58 pub struct Label(pub String);
59
60 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
61 pub struct Spec(pub String);
62
63 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
65 pub struct PackageName(pub String);
66
67 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
69 pub struct CrateName(pub String);
70
71 #[derive(Debug, Display, Error)]
72 pub enum SpecError {
73 Parsing(String),
75 }
76
77 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
78 pub enum LibraryType {
79 Static,
81 DynamicWithRpath,
83 }
84
85 impl str::FromStr for LibraryType {
86 type Err = SpecError;
87
88 fn from_str(s: &str) -> Result<Self, SpecError> {
89 match s {
90 "static" => Ok(Self::Static),
91 "dynamic" => Ok(Self::DynamicWithRpath),
92 s => Err(SpecError::Parsing(format!(
93 "dep type only accepts \"static\" or \"dynamic\"; was {:?}",
94 s
95 ))),
96 }
97 }
98 }
99
100 #[derive(Debug, Clone)]
101 pub struct SubDep {
102 pub pkg_name: PackageName,
103 pub r#type: LibraryType,
104 pub lib_names: Vec<String>,
105 }
106
107 #[derive(Debug, Clone)]
108 pub struct Recipe {
109 pub sub_deps: Vec<SubDep>,
110 }
111
112 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
113 pub struct EnvInstructions {
114 pub specs: Vec<Spec>,
115 pub repo: Option<Repo>,
116 }
117
118 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
119 #[repr(transparent)]
120 pub struct EnvHash(pub [u8; 32]);
121
122 impl EnvHash {
123 fn base64_encoded(&self) -> String { Base64Url::encode_string(&self.0[..]) }
124
125 pub fn hashed_env_name(&self, readable_name: &str) -> String {
126 format!(
127 "{}-{}",
128 readable_name,
129 self.base64_encoded().strip_suffix('=').unwrap()
130 )
131 }
132 }
133
134 impl EnvInstructions {
135 pub fn compute_digest(&self) -> EnvHash {
136 let mut hasher = Sha3_256::new();
137 for Spec(s) in self.specs.iter() {
138 hasher.update(s);
139 }
140 if let Some(ref repo) = self.repo {
141 hasher.update(repo.path.as_os_str().as_encoded_bytes());
142 }
143 EnvHash(hasher.finalize().into())
144 }
145 }
146
147 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
148 pub struct CargoFeature(pub String);
149
150 impl CargoFeature {
151 pub fn to_env_var_name(&self) -> String {
156 format!("CARGO_FEATURE_{}", self.0.to_uppercase().replace('-', "_"))
157 }
158 }
159
160 #[derive(Debug, Clone, Default)]
161 pub struct FeatureMap {
162 pub needed: IndexSet<CargoFeature>,
163 pub conflicting: IndexSet<CargoFeature>,
164 }
165
166 impl FeatureMap {
167 pub fn evaluate(&self, features: &FeatureSet) -> bool {
168 let Self {
169 needed,
170 conflicting,
171 } = self;
172 for n in needed.iter() {
173 if !features.0.contains(n) {
174 return false;
175 }
176 }
177 for c in conflicting.iter() {
178 if features.0.contains(c) {
179 return false;
180 }
181 }
182 true
183 }
184 }
185
186 #[derive(Debug, Clone, Default)]
187 pub struct FeatureSet(pub IndexSet<CargoFeature>);
188
189 #[derive(Debug, Clone)]
190 pub struct DisjointResolves {
191 pub by_label: IndexMap<Label, EnvInstructions>,
192 pub recipes: IndexMap<CrateName, IndexMap<Label, (Recipe, FeatureMap)>>,
193 pub declared_features_by_package: IndexMap<CrateName, Vec<CargoFeature>>,
194 }
195}