libcnb_data/
build_plan.rs1use serde::Serialize;
2use serde::ser::Error;
3use std::collections::VecDeque;
4use toml::value::Table;
5
6#[derive(Serialize, Debug, Default)]
7#[must_use]
8pub struct BuildPlan {
9 #[serde(skip_serializing_if = "Vec::is_empty")]
10 pub provides: Vec<Provide>,
11 #[serde(skip_serializing_if = "Vec::is_empty")]
12 pub requires: Vec<Require>,
13 #[serde(skip_serializing_if = "Vec::is_empty")]
14 pub or: Vec<Or>,
15}
16
17impl BuildPlan {
18 pub fn new() -> Self {
19 Self::default()
20 }
21}
22
23#[derive(Default)]
24#[must_use]
25pub struct BuildPlanBuilder {
26 acc: VecDeque<(Vec<Provide>, Vec<Require>)>,
27 current_provides: Vec<Provide>,
28 current_requires: Vec<Require>,
29}
30
31impl BuildPlanBuilder {
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn provides(mut self, name: impl AsRef<str>) -> Self {
37 self.current_provides.push(Provide::new(name.as_ref()));
38 self
39 }
40
41 pub fn requires(mut self, require: impl Into<Require>) -> Self {
42 self.current_requires.push(require.into());
43 self
44 }
45
46 pub fn or(mut self) -> Self {
47 self.acc
48 .push_back((self.current_provides, self.current_requires));
49 self.current_provides = Vec::new();
50 self.current_requires = Vec::new();
51
52 self
53 }
54
55 pub fn build(self) -> BuildPlan {
56 let mut xyz = self.or();
57
58 if let Some(head) = xyz.acc.pop_front() {
59 let mut build_plan = BuildPlan::new();
60 build_plan.provides = head.0;
61 build_plan.requires = head.1;
62
63 for alternative in xyz.acc {
64 build_plan.or.push(Or {
65 provides: alternative.0,
66 requires: alternative.1,
67 });
68 }
69
70 build_plan
71 } else {
72 BuildPlan::new()
73 }
74 }
75}
76
77#[derive(Serialize, Debug)]
78pub struct Or {
79 #[serde(skip_serializing_if = "Vec::is_empty")]
80 provides: Vec<Provide>,
81 #[serde(skip_serializing_if = "Vec::is_empty")]
82 requires: Vec<Require>,
83}
84
85#[derive(Serialize, Debug)]
86pub struct Provide {
87 name: String,
88}
89
90impl Provide {
91 pub fn new(name: impl Into<String>) -> Self {
92 Self { name: name.into() }
93 }
94}
95
96#[derive(Serialize, Debug)]
97pub struct Require {
98 pub name: String,
99 pub metadata: Table,
100}
101
102impl Require {
103 pub fn new(name: impl Into<String>) -> Self {
104 Self {
105 name: name.into(),
106 metadata: Table::new(),
107 }
108 }
109
110 pub fn metadata<T: Serialize>(&mut self, metadata: T) -> Result<(), toml::ser::Error> {
116 if let toml::Value::Table(table) = toml::Value::try_from(metadata)? {
117 self.metadata = table;
118
119 Ok(())
120 } else {
121 Err(toml::ser::Error::custom(String::from(
122 "Couldn't be serialized as a TOML Table.",
123 )))
124 }
125 }
126}
127
128impl<S: Into<String>> From<S> for Require {
129 fn from(s: S) -> Self {
130 Self::new(s)
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn it_writes_simple_build_plan() {
140 let mut build_plan = BuildPlan::new();
141 build_plan.provides.push(Provide::new("rust"));
142 build_plan.requires.push(Require::new("rust"));
143
144 assert!(toml::to_string(&build_plan).is_ok());
145 }
146
147 #[test]
148 fn it_serializes_metadata() {
149 #[derive(Serialize)]
150 struct Metadata {
151 foo: String,
152 }
153
154 let mut require = Require::new("foo");
155 let metadata = Metadata {
156 foo: String::from("bar"),
157 };
158 let result = require.metadata(metadata);
159 assert!(result.is_ok());
160 assert_eq!(
161 require.metadata.get("foo"),
162 Some(&toml::Value::String(String::from("bar")))
163 );
164 }
165}