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