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    is_dylib: bool,
45    no_default_features: bool,
46    features: Option<Vec<String>>,
47    config: Vec<String>,
48    tracing: Option<TracingOptions>,
49    args: Vec<String>,
50    display_name: Option<String>,
51    env: HashMap<String, String>,
52}
53
54impl RustCrate {
55    /// Creates a new `RustCrate`.
56    ///
57    /// The `src` argument is the path to the package's directory.
58    /// The `crate_root` argument is a path to the package's workspace root, which may
59    /// be a parent of `src` in a multi-crate workspace.
60    pub fn new(src: impl Into<PathBuf>, workspace_root: impl Into<PathBuf>) -> Self {
61        Self {
62            src: src.into(),
63            workspace_root: workspace_root.into(),
64            target: CrateTarget::Default,
65            profile: None,
66            rustflags: None,
67            target_dir: None,
68            build_env: vec![],
69            is_dylib: false,
70            no_default_features: false,
71            features: None,
72            config: vec![],
73            tracing: None,
74            args: vec![],
75            display_name: None,
76            env: HashMap::new(),
77        }
78    }
79
80    /// Sets the target to be a binary with the given name,
81    /// equivalent to `cargo run --bin <name>`.
82    pub fn bin(mut self, bin: impl Into<String>) -> Self {
83        if self.target != CrateTarget::Default {
84            panic!("{} already set", name_of!(target in Self));
85        }
86
87        self.target = CrateTarget::Bin(bin.into());
88        self
89    }
90
91    /// Sets the target to be an example with the given name,
92    /// equivalent to `cargo run --example <name>`.
93    pub fn example(mut self, example: impl Into<String>) -> Self {
94        if self.target != CrateTarget::Default {
95            panic!("{} already set", name_of!(target in Self));
96        }
97
98        self.target = CrateTarget::Example(example.into());
99        self
100    }
101
102    /// Sets the profile to be used when building the crate.
103    /// Equivalent to `cargo run --profile <profile>`.
104    pub fn profile(mut self, profile: impl Into<String>) -> Self {
105        if self.profile.is_some() {
106            panic!("{} already set", name_of!(profile in Self));
107        }
108
109        self.profile = Some(profile.into());
110        self
111    }
112
113    pub fn rustflags(mut self, rustflags: impl Into<String>) -> Self {
114        if self.rustflags.is_some() {
115            panic!("{} already set", name_of!(rustflags in Self));
116        }
117
118        self.rustflags = Some(rustflags.into());
119        self
120    }
121
122    pub fn target_dir(mut self, target_dir: impl Into<PathBuf>) -> Self {
123        if self.target_dir.is_some() {
124            panic!("{} already set", name_of!(target_dir in Self));
125        }
126
127        self.target_dir = Some(target_dir.into());
128        self
129    }
130
131    pub fn build_env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
132        self.build_env.push((key.into(), value.into()));
133        self
134    }
135
136    pub fn set_is_dylib(mut self, is_dylib: bool) -> Self {
137        self.is_dylib = is_dylib;
138        self
139    }
140
141    pub fn no_default_features(mut self) -> Self {
142        self.no_default_features = true;
143        self
144    }
145
146    pub fn features(mut self, features: impl IntoIterator<Item = impl Into<String>>) -> Self {
147        if self.features.is_none() {
148            self.features = Some(vec![]);
149        }
150
151        self.features
152            .as_mut()
153            .unwrap()
154            .extend(features.into_iter().map(|s| s.into()));
155
156        self
157    }
158
159    pub fn config(mut self, config: impl Into<String>) -> Self {
160        self.config.push(config.into());
161        self
162    }
163
164    pub fn tracing(mut self, perf: impl Into<TracingOptions>) -> Self {
165        if self.tracing.is_some() {
166            panic!("{} already set", name_of!(tracing in Self));
167        }
168
169        self.tracing = Some(perf.into());
170        self
171    }
172
173    /// Sets the arguments to be passed to the binary when it is launched.
174    pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
175        self.args.extend(args.into_iter().map(|s| s.into()));
176        self
177    }
178
179    /// Sets the display name for this service, which will be used in logging.
180    pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
181        if self.display_name.is_some() {
182            panic!("{} already set", name_of!(display_name in Self));
183        }
184
185        self.display_name = Some(display_name.into());
186        self
187    }
188
189    /// Sets an environment variable to be written to a .env file on the launched instance.
190    pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
191        self.env.insert(key.into(), value.into());
192        self
193    }
194
195    pub fn get_build_params(&self, target: HostTargetType) -> BuildParams {
196        let (bin, example) = match &self.target {
197            CrateTarget::Default => (None, None),
198            CrateTarget::Bin(bin) => (Some(bin.clone()), None),
199            CrateTarget::Example(example) => (None, Some(example.clone())),
200        };
201
202        BuildParams::new(
203            self.src.clone(),
204            self.workspace_root.clone(),
205            bin,
206            example,
207            self.profile.clone(),
208            self.rustflags.clone(),
209            self.target_dir.clone(),
210            self.build_env.clone(),
211            self.no_default_features,
212            target,
213            self.is_dylib,
214            self.features.clone(),
215            self.config.clone(),
216        )
217    }
218}
219
220impl ServiceBuilder for RustCrate {
221    type Service = RustCrateService;
222    fn build(self, id: usize, on: Arc<dyn Host>) -> Self::Service {
223        let build_params = self.get_build_params(on.target_type());
224
225        RustCrateService::new(
226            id,
227            on,
228            build_params,
229            self.tracing,
230            Some(self.args),
231            self.display_name,
232            vec![],
233            self.env,
234        )
235    }
236}