1use std::{borrow::Cow, env, fmt, fmt::Display, str::FromStr};
2
3use anyhow::{Context, anyhow, ensure};
4use lexe_std::Apply;
5#[cfg(any(test, feature = "test-utils"))]
6use proptest::strategy::Strategy;
7#[cfg(any(test, feature = "test-utils"))]
8use proptest_derive::Arbitrary;
9use serde::Serialize;
10use serde_with::DeserializeFromStr;
11use strum::VariantArray;
12
13use crate::ln::network::Network;
14
15#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
17#[derive(DeserializeFromStr, VariantArray)]
18#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
19pub enum DeployEnv {
20 Dev,
22 Staging,
24 Prod,
26}
27
28impl DeployEnv {
29 pub fn from_env() -> anyhow::Result<Self> {
31 env::var("DEPLOY_ENVIRONMENT")
32 .context("DEPLOY_ENVIRONMENT was not set")?
33 .as_str()
34 .apply(Self::from_str)
35 }
36
37 #[inline]
39 pub fn is_dev(self) -> bool {
40 matches!(self, Self::Dev)
41 }
42
43 #[inline]
45 pub fn is_staging(self) -> bool {
46 matches!(self, Self::Staging)
47 }
48
49 #[inline]
51 pub fn is_prod(self) -> bool {
52 matches!(self, Self::Prod)
53 }
54
55 #[inline]
57 pub fn is_staging_or_prod(self) -> bool {
58 matches!(self, Self::Staging | Self::Prod)
59 }
60
61 #[inline]
63 pub fn as_str(&self) -> &'static str {
64 match self {
65 Self::Dev => "dev",
66 Self::Staging => "staging",
67 Self::Prod => "prod",
68 }
69 }
70
71 pub fn validate_network(&self, network: Network) -> anyhow::Result<()> {
73 use Network::*;
74 match self {
75 Self::Dev => ensure!(
76 matches!(network, Regtest | Testnet3 | Testnet4),
77 "Dev environment can only be regtest or testnet!"
78 ),
79 Self::Staging => ensure!(
80 matches!(network, Testnet3 | Testnet4),
81 "Staging environment can only be testnet!"
82 ),
83 Self::Prod => ensure!(
84 matches!(network, Mainnet),
85 "Prod environment can only be mainnet!"
86 ),
87 }
88 Ok(())
89 }
90
91 pub fn validate_sgx(&self, use_sgx: bool) -> anyhow::Result<()> {
93 if matches!(self, Self::Staging | Self::Prod) {
94 ensure!(use_sgx, "Staging and prod can only run in SGX!");
95 }
96 Ok(())
97 }
98
99 pub fn gateway_url(
103 &self,
104 dev_gateway_url: Option<Cow<'static, str>>,
105 ) -> Cow<'static, str> {
106 match self {
107 Self::Dev => dev_gateway_url
108 .unwrap_or(Cow::Borrowed("https://localhost:4040")),
109 Self::Staging => Cow::Borrowed("https://gateway.staging.lexe.app"),
110 Self::Prod => Cow::Borrowed("https://gateway.lexe.app"),
111 }
112 }
113
114 #[cfg(any(test, feature = "test-utils"))]
116 pub fn any_valid_network_combo()
117 -> impl Strategy<Value = (DeployEnv, Network)> {
118 use proptest::strategy::Just;
119 proptest::prop_oneof![
124 Just((Self::Dev, Network::Regtest)),
125 Just((Self::Dev, Network::Testnet3)),
126 Just((Self::Dev, Network::Testnet4)),
127 Just((Self::Staging, Network::Testnet3)),
128 Just((Self::Staging, Network::Testnet4)),
129 Just((Self::Prod, Network::Mainnet)),
130 ]
131 }
132}
133
134impl FromStr for DeployEnv {
135 type Err = anyhow::Error;
136
137 fn from_str(s: &str) -> anyhow::Result<Self> {
138 match s {
139 "dev" => Ok(Self::Dev),
140 "staging" => Ok(Self::Staging),
141 "prod" => Ok(Self::Prod),
142 _ => Err(anyhow!(
143 "Unrecognized DEPLOY_ENVIRONMENT '{s}': \
144 must be 'dev', 'staging', or 'prod'"
145 )),
146 }
147 }
148}
149
150impl Display for DeployEnv {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 f.write_str(self.as_str())
153 }
154}
155
156impl Serialize for DeployEnv {
157 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
158 where
159 S: serde::Serializer,
160 {
161 self.as_str().serialize(serializer)
162 }
163}
164
165#[cfg(test)]
166mod test {
167 use proptest::{prop_assert, proptest};
168
169 use super::*;
170 use crate::test_utils::roundtrip;
171
172 #[test]
173 fn deploy_env_roundtrip() {
174 let expected_ser = r#"["dev","staging","prod"]"#;
175 roundtrip::json_unit_enum_backwards_compat::<DeployEnv>(expected_ser);
176 roundtrip::fromstr_display_roundtrip_proptest::<DeployEnv>();
177 }
178
179 #[test]
180 fn test_any_valid_network_combo() {
181 proptest!(|(
182 (deploy_env, network) in DeployEnv::any_valid_network_combo(),
183 )| {
184 prop_assert!(deploy_env.validate_network(network).is_ok());
185 })
186 }
187}