1use crate::lockfile::ContentHash;
13use crate::{PackageId, Version};
14use serde::{Deserialize, Serialize};
15
16#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
19pub struct Derivation {
20 pub name: String,
21 pub version: Version,
22 pub package_id: PackageId,
24 pub inputs: Vec<DerivationRef>,
26 pub build: BuildScript,
28 pub cache_key: ContentHash,
33}
34
35#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
39pub struct DerivationRef {
40 pub name: String,
41 pub cache_key: ContentHash,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
47pub struct BuildScript {
48 pub steps: Vec<BuildStep>,
49 #[serde(default)]
51 pub env: indexmap::IndexMap<String, String>,
52}
53
54#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
56pub struct BuildStep {
57 pub kind: BuildStepKind,
58 pub command: BuildCommand,
59}
60
61#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(rename_all = "PascalCase")]
66pub enum BuildStepKind {
67 Fetch,
68 PreUnpack,
69 Unpack,
70 PostUnpack,
71 Patch,
72 PreConfigure,
73 Configure,
74 PostConfigure,
75 PreBuild,
76 Build,
77 PostBuild,
78 PreCheck,
79 Check,
80 PostCheck,
81 PreInstall,
82 Install,
83 PostInstall,
84 Custom {
89 name: String,
90 },
91}
92
93#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
97#[serde(tag = "kind", rename_all = "kebab-case")]
98pub enum BuildCommand {
99 Cargo {
101 subcommand: String,
102 args: Vec<String>,
103 },
104 Npm {
106 manager: String,
107 subcommand: String,
108 args: Vec<String>,
109 },
110 Pip {
112 subcommand: String,
113 args: Vec<String>,
114 },
115 Gem {
117 subcommand: String,
118 args: Vec<String>,
119 },
120 Go {
122 subcommand: String,
123 args: Vec<String>,
124 },
125 Make {
127 target: String,
128 env: indexmap::IndexMap<String, String>,
129 },
130 Copy { from: String, to: String },
132 Mkdir { path: String },
134 Symlink { target: String, link_name: String },
136 Native { program: String, args: Vec<String> },
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use crate::Registry;
146
147 #[test]
148 fn derivation_round_trips_through_serde() {
149 let d = Derivation {
150 name: "serde-1.0.228".into(),
151 version: Version::new(1, 0, 228),
152 package_id: PackageId {
153 name: "serde".into(),
154 version: Version::new(1, 0, 228),
155 registry: Registry::CratesIo,
156 },
157 inputs: vec![],
158 build: BuildScript {
159 steps: vec![BuildStep {
160 kind: BuildStepKind::Build,
161 command: BuildCommand::Cargo {
162 subcommand: "build".into(),
163 args: vec!["--release".into()],
164 },
165 }],
166 env: indexmap::IndexMap::new(),
167 },
168 cache_key: ContentHash::of(b"snapshot"),
169 };
170 let j = serde_json::to_string(&d).unwrap();
171 let parsed: Derivation = serde_json::from_str(&j).unwrap();
172 assert_eq!(d, parsed);
173 }
174
175 #[test]
176 fn build_command_variants_round_trip() {
177 let commands = vec![
178 BuildCommand::Cargo {
179 subcommand: "build".into(),
180 args: vec![],
181 },
182 BuildCommand::Npm {
183 manager: "pnpm".into(),
184 subcommand: "install".into(),
185 args: vec![],
186 },
187 BuildCommand::Copy {
188 from: "/src".into(),
189 to: "/out".into(),
190 },
191 ];
192 for c in commands {
193 let j = serde_json::to_string(&c).unwrap();
194 let parsed: BuildCommand = serde_json::from_str(&j).unwrap();
195 assert_eq!(c, parsed);
196 }
197 }
198}