Skip to main content

hydro_deploy/rust_crate/
mod.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3use std::sync::Arc;
4
5use nameof::name_of;
6use tracing_options::TracingOptions;
7
8use super::Host;
9use crate::rust_crate::build::BuildParams;
10use crate::{HostTargetType, ServiceBuilder};
11
12pub mod build;
13pub mod ports;
14
15pub mod service;
16pub use service::*;
17
18#[cfg(feature = "profile-folding")]
19pub(crate) mod flamegraph;
20pub mod tracing_options;
21
22#[derive(PartialEq, Clone)]
23pub enum CrateTarget {
24    Default,
25    Bin(String),
26    Example(String),
27}
28
29/// Specifies a crate that uses `hydro_deploy_integration` to be
30/// deployed as a service.
31///
32/// A [crate](https://doc.rust-lang.org/cargo/appendix/glossary.html#crate) is a particular
33/// [target](https://doc.rust-lang.org/cargo/appendix/glossary.html#target) within a
34/// [package](https://doc.rust-lang.org/cargo/appendix/glossary.html#package).
35#[derive(Clone)]
36pub struct RustCrate {
37    src: PathBuf,
38    workspace_root: PathBuf,
39    target: CrateTarget,
40    profile: Option<String>,
41    rustflags: Option<String>,
42    target_dir: Option<PathBuf>,
43    build_env: Vec<(String, String)>,
44    /// If set, the crate is a generated trybuild `dylib-examples` crate whose examples link
45    /// dynamically against the trybuild dylib with this cargo package name.
46    dylib_package: Option<String>,
47    no_default_features: bool,
48    features: Option<Vec<String>>,
49    config: Vec<String>,
50    tracing: Option<TracingOptions>,
51    args: Vec<String>,
52    display_name: Option<String>,
53    env: HashMap<String, String>,
54    pin_to_core: Option<usize>,
55}
56
57impl RustCrate {
58    /// Creates a new `RustCrate`.
59    ///
60    /// The `src` argument is the path to the package's directory.
61    /// The `crate_root` argument is a path to the package's workspace root, which may
62    /// be a parent of `src` in a multi-crate workspace.
63    pub fn new(src: impl Into<PathBuf>, workspace_root: impl Into<PathBuf>) -> Self {
64        Self {
65            src: src.into(),
66            workspace_root: workspace_root.into(),
67            target: CrateTarget::Default,
68            profile: None,
69            rustflags: None,
70            target_dir: None,
71            build_env: vec![],
72            dylib_package: None,
73            no_default_features: false,
74            features: None,
75            config: vec![],
76            tracing: None,
77            args: vec![],
78            display_name: None,
79            env: HashMap::new(),
80            pin_to_core: None,
81        }
82    }
83
84    /// Sets the target to be a binary with the given name,
85    /// equivalent to `cargo run --bin <name>`.
86    pub fn bin(mut self, bin: impl Into<String>) -> Self {
87        assert!(
88            self.target == CrateTarget::Default,
89            "{} already set",
90            name_of!(target in Self)
91        );
92
93        self.target = CrateTarget::Bin(bin.into());
94        self
95    }
96
97    /// Sets the target to be an example with the given name,
98    /// equivalent to `cargo run --example <name>`.
99    pub fn example(mut self, example: impl Into<String>) -> Self {
100        assert!(
101            self.target == CrateTarget::Default,
102            "{} already set",
103            name_of!(target in Self)
104        );
105
106        self.target = CrateTarget::Example(example.into());
107        self
108    }
109
110    /// Sets the profile to be used when building the crate.
111    /// Equivalent to `cargo run --profile <profile>`.
112    pub fn profile(mut self, profile: impl Into<String>) -> Self {
113        assert!(
114            self.profile.is_none(),
115            "{} already set",
116            name_of!(profile in Self)
117        );
118
119        self.profile = Some(profile.into());
120        self
121    }
122
123    pub fn rustflags(mut self, rustflags: impl Into<String>) -> Self {
124        assert!(
125            self.rustflags.is_none(),
126            "{} already set",
127            name_of!(rustflags in Self)
128        );
129
130        self.rustflags = Some(rustflags.into());
131        self
132    }
133
134    pub fn target_dir(mut self, target_dir: impl Into<PathBuf>) -> Self {
135        assert!(
136            self.target_dir.is_none(),
137            "{} already set",
138            name_of!(target_dir in Self)
139        );
140
141        self.target_dir = Some(target_dir.into());
142        self
143    }
144
145    pub fn build_env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
146        self.build_env.push((key.into(), value.into()));
147        self
148    }
149
150    /// Marks this crate as a generated trybuild `dylib-examples` crate whose examples link
151    /// dynamically against the trybuild dylib with cargo package name `dylib_package`.
152    #[must_use]
153    pub fn dylib_package(mut self, dylib_package: impl Into<String>) -> Self {
154        self.dylib_package = Some(dylib_package.into());
155        self
156    }
157
158    #[must_use]
159    pub fn no_default_features(mut self) -> Self {
160        self.no_default_features = true;
161        self
162    }
163
164    pub fn features(mut self, features: impl IntoIterator<Item = impl Into<String>>) -> Self {
165        if self.features.is_none() {
166            self.features = Some(vec![]);
167        }
168
169        self.features
170            .as_mut()
171            .unwrap()
172            .extend(features.into_iter().map(|s| s.into()));
173
174        self
175    }
176
177    pub fn config(mut self, config: impl Into<String>) -> Self {
178        self.config.push(config.into());
179        self
180    }
181
182    pub fn tracing(mut self, perf: impl Into<TracingOptions>) -> Self {
183        assert!(
184            self.tracing.is_none(),
185            "{} already set",
186            name_of!(tracing in Self)
187        );
188
189        self.tracing = Some(perf.into());
190        self
191    }
192
193    /// Sets the arguments to be passed to the binary when it is launched.
194    pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
195        self.args.extend(args.into_iter().map(|s| s.into()));
196        self
197    }
198
199    /// Sets the display name for this service, which will be used in logging.
200    pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
201        assert!(
202            self.display_name.is_none(),
203            "{} already set",
204            name_of!(display_name in Self)
205        );
206
207        self.display_name = Some(display_name.into());
208        self
209    }
210
211    /// Sets an environment variable to be written to a .env file on the launched instance.
212    pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
213        self.env.insert(key.into(), value.into());
214        self
215    }
216
217    #[must_use]
218    pub fn pin_to_core(mut self, core: usize) -> Self {
219        self.pin_to_core = Some(core);
220        self
221    }
222
223    pub fn get_build_params(&self, target: HostTargetType) -> BuildParams {
224        let (bin, example) = match &self.target {
225            CrateTarget::Default => (None, None),
226            CrateTarget::Bin(bin) => (Some(bin.clone()), None),
227            CrateTarget::Example(example) => (None, Some(example.clone())),
228        };
229
230        BuildParams::new(
231            self.src.clone(),
232            self.workspace_root.clone(),
233            bin,
234            example,
235            self.profile.clone(),
236            self.rustflags.clone(),
237            self.target_dir.clone(),
238            self.build_env.clone(),
239            self.no_default_features,
240            target,
241            self.dylib_package.clone(),
242            self.features.clone(),
243            self.config.clone(),
244        )
245    }
246}
247
248impl ServiceBuilder for RustCrate {
249    type Service = RustCrateService;
250    fn build(self, id: usize, on: Arc<dyn Host>) -> Self::Service {
251        let build_params = self.get_build_params(on.target_type());
252
253        RustCrateService::new(
254            id,
255            on,
256            build_params,
257            self.tracing,
258            Some(self.args),
259            self.display_name,
260            vec![],
261            self.env,
262            self.pin_to_core,
263        )
264    }
265}