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