use super::error::OpConnectError;
#[derive(Debug, Clone)]
pub struct OpConnectConfig {
pub connect_url: String,
pub token: String,
}
impl OpConnectConfig {
pub fn from_env() -> Result<Self, OpConnectError> {
let url = std::env::var("OP_CONNECT_URL")
.map_err(|_| OpConnectError::Config("OP_CONNECT_URL is not set".into()))?;
if url.is_empty() {
return Err(OpConnectError::Config(
"OP_CONNECT_URL is set but empty".into(),
));
}
let token = std::env::var("OP_CONNECT_TOKEN")
.map_err(|_| OpConnectError::Config("OP_CONNECT_TOKEN is not set".into()))?;
if token.is_empty() {
return Err(OpConnectError::Config(
"OP_CONNECT_TOKEN is set but empty".into(),
));
}
let url = url.trim_end_matches('/').to_string();
if !url.starts_with("https://")
&& !url.starts_with("http://127.0.0.1")
&& !url.starts_with("http://localhost")
{
return Err(OpConnectError::Config(format!(
"OP_CONNECT_URL must use https:// for remote servers \
(plain HTTP is only allowed for localhost); got: {url}"
)));
}
Ok(Self {
connect_url: url,
token,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn connect_config_from_env_requires_both_vars() {
temp_env::with_vars(
[
("OP_CONNECT_URL", Some("https://connect.example.com")),
("OP_CONNECT_TOKEN", None::<&str>),
],
|| {
let result = OpConnectConfig::from_env();
assert!(
matches!(result, Err(OpConnectError::Config(_))),
"expected Config error when token is absent, got: {result:?}"
);
},
);
temp_env::with_vars(
[
("OP_CONNECT_URL", None::<&str>),
("OP_CONNECT_TOKEN", Some("tok")),
],
|| {
let result = OpConnectConfig::from_env();
assert!(
matches!(result, Err(OpConnectError::Config(_))),
"expected Config error when URL is absent, got: {result:?}"
);
},
);
}
#[test]
fn connect_config_rejects_http_non_localhost() {
temp_env::with_vars(
[
("OP_CONNECT_URL", Some("http://remote.example.com")),
("OP_CONNECT_TOKEN", Some("tok")),
],
|| {
let result = OpConnectConfig::from_env();
assert!(
matches!(result, Err(OpConnectError::Config(_))),
"expected Config error for non-localhost plain HTTP, got: {result:?}"
);
},
);
}
#[test]
fn connect_config_allows_http_localhost() {
temp_env::with_vars(
[
("OP_CONNECT_URL", Some("http://localhost:8080")),
("OP_CONNECT_TOKEN", Some("tok")),
],
|| {
let result = OpConnectConfig::from_env();
assert!(
result.is_ok(),
"expected Ok for localhost plain HTTP, got: {result:?}"
);
let cfg = result.unwrap();
assert_eq!(cfg.connect_url, "http://localhost:8080");
assert_eq!(cfg.token, "tok");
},
);
}
#[test]
fn connect_config_allows_http_127_0_0_1() {
temp_env::with_vars(
[
("OP_CONNECT_URL", Some("http://127.0.0.1:8080")),
("OP_CONNECT_TOKEN", Some("tok")),
],
|| {
let result = OpConnectConfig::from_env();
assert!(
result.is_ok(),
"expected Ok for 127.0.0.1 plain HTTP, got: {result:?}"
);
},
);
}
#[test]
fn connect_config_allows_https_remote() {
temp_env::with_vars(
[
("OP_CONNECT_URL", Some("https://connect.example.com")),
("OP_CONNECT_TOKEN", Some("my-token")),
],
|| {
let result = OpConnectConfig::from_env();
assert!(
result.is_ok(),
"expected Ok for HTTPS remote, got: {result:?}"
);
let cfg = result.unwrap();
assert_eq!(cfg.connect_url, "https://connect.example.com");
},
);
}
#[test]
fn connect_config_strips_trailing_slash_from_url() {
temp_env::with_vars(
[
("OP_CONNECT_URL", Some("https://connect.example.com/")),
("OP_CONNECT_TOKEN", Some("tok")),
],
|| {
let cfg = OpConnectConfig::from_env().unwrap();
assert_eq!(cfg.connect_url, "https://connect.example.com");
},
);
}
}