hydro_deploy/rust_crate/
mod.rs1use std::path::PathBuf;
2use std::sync::Arc;
3
4use nameof::name_of;
5use tracing_options::TracingOptions;
6
7use super::Host;
8use crate::rust_crate::build::BuildParams;
9use crate::{HostTargetType, ServiceBuilder};
10
11pub mod build;
12pub mod ports;
13
14pub mod service;
15pub use service::*;
16
17pub(crate) mod flamegraph;
18pub mod tracing_options;
19
20#[derive(PartialEq, Clone)]
21pub enum CrateTarget {
22 Default,
23 Bin(String),
24 Example(String),
25}
26
27#[derive(Clone)]
30pub struct RustCrate {
31 src: PathBuf,
32 target: CrateTarget,
33 profile: Option<String>,
34 rustflags: Option<String>,
35 target_dir: Option<PathBuf>,
36 build_env: Vec<(String, String)>,
37 no_default_features: bool,
38 features: Option<Vec<String>>,
39 config: Vec<String>,
40 tracing: Option<TracingOptions>,
41 args: Vec<String>,
42 display_name: Option<String>,
43}
44
45impl RustCrate {
46 pub fn new(src: impl Into<PathBuf>) -> Self {
50 Self {
51 src: src.into(),
52 target: CrateTarget::Default,
53 profile: None,
54 rustflags: None,
55 target_dir: None,
56 build_env: vec![],
57 no_default_features: false,
58 features: None,
59 config: vec![],
60 tracing: None,
61 args: vec![],
62 display_name: None,
63 }
64 }
65
66 pub fn bin(mut self, bin: impl Into<String>) -> Self {
69 if self.target != CrateTarget::Default {
70 panic!("{} already set", name_of!(target in Self));
71 }
72
73 self.target = CrateTarget::Bin(bin.into());
74 self
75 }
76
77 pub fn example(mut self, example: impl Into<String>) -> Self {
80 if self.target != CrateTarget::Default {
81 panic!("{} already set", name_of!(target in Self));
82 }
83
84 self.target = CrateTarget::Example(example.into());
85 self
86 }
87
88 pub fn profile(mut self, profile: impl Into<String>) -> Self {
91 if self.profile.is_some() {
92 panic!("{} already set", name_of!(profile in Self));
93 }
94
95 self.profile = Some(profile.into());
96 self
97 }
98
99 pub fn rustflags(mut self, rustflags: impl Into<String>) -> Self {
100 if self.rustflags.is_some() {
101 panic!("{} already set", name_of!(rustflags in Self));
102 }
103
104 self.rustflags = Some(rustflags.into());
105 self
106 }
107
108 pub fn target_dir(mut self, target_dir: impl Into<PathBuf>) -> Self {
109 if self.target_dir.is_some() {
110 panic!("{} already set", name_of!(target_dir in Self));
111 }
112
113 self.target_dir = Some(target_dir.into());
114 self
115 }
116
117 pub fn build_env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
118 self.build_env.push((key.into(), value.into()));
119 self
120 }
121
122 pub fn no_default_features(mut self) -> Self {
123 self.no_default_features = true;
124 self
125 }
126
127 pub fn features(mut self, features: impl IntoIterator<Item = impl Into<String>>) -> Self {
128 if self.features.is_none() {
129 self.features = Some(vec![]);
130 }
131
132 self.features
133 .as_mut()
134 .unwrap()
135 .extend(features.into_iter().map(|s| s.into()));
136
137 self
138 }
139
140 pub fn config(mut self, config: impl Into<String>) -> Self {
141 self.config.push(config.into());
142 self
143 }
144
145 pub fn tracing(mut self, perf: impl Into<TracingOptions>) -> Self {
146 if self.tracing.is_some() {
147 panic!("{} already set", name_of!(tracing in Self));
148 }
149
150 self.tracing = Some(perf.into());
151 self
152 }
153
154 pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
156 self.args.extend(args.into_iter().map(|s| s.into()));
157 self
158 }
159
160 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
162 if self.display_name.is_some() {
163 panic!("{} already set", name_of!(display_name in Self));
164 }
165
166 self.display_name = Some(display_name.into());
167 self
168 }
169
170 pub fn get_build_params(&self, target: HostTargetType) -> BuildParams {
171 let (bin, example) = match &self.target {
172 CrateTarget::Default => (None, None),
173 CrateTarget::Bin(bin) => (Some(bin.clone()), None),
174 CrateTarget::Example(example) => (None, Some(example.clone())),
175 };
176
177 BuildParams::new(
178 self.src.clone(),
179 bin,
180 example,
181 self.profile.clone(),
182 self.rustflags.clone(),
183 self.target_dir.clone(),
184 self.build_env.clone(),
185 self.no_default_features,
186 target,
187 self.features.clone(),
188 self.config.clone(),
189 )
190 }
191}
192
193impl ServiceBuilder for RustCrate {
194 type Service = RustCrateService;
195 fn build(self, id: usize, on: Arc<dyn Host>) -> Self::Service {
196 let build_params = self.get_build_params(on.target_type());
197
198 RustCrateService::new(
199 id,
200 on,
201 build_params,
202 self.tracing,
203 Some(self.args),
204 self.display_name,
205 vec![],
206 )
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::deployment;
214
215 #[tokio::test]
216 async fn test_crate_panic() {
217 let mut deployment = deployment::Deployment::new();
218
219 let service = deployment.add_service(
220 RustCrate::new("../hydro_deploy_examples")
221 .example("panic_program")
222 .profile("dev"),
223 deployment.Localhost(),
224 );
225
226 deployment.deploy().await.unwrap();
227
228 let mut stdout = service.try_read().unwrap().stdout();
229
230 deployment.start().await.unwrap();
231
232 assert_eq!(stdout.recv().await.unwrap(), "hello!");
233
234 assert!(stdout.recv().await.is_none());
235 }
236}