sofie_core/
environment.rs1use std::env::VarError;
2
3use crate::{Error, Result};
4
5pub fn environment_variable(name: &str) -> Result<Option<String>> {
6 unicode(name, std::env::var(name))
7}
8
9pub const BASE_URL: &str = "SOFIE_BASE_URL";
10
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct Variable<'name> {
13 pub name: &'name str,
14 pub value: String,
15}
16
17pub fn base_url(
19 own: &str,
20 lookup: impl Fn(&str) -> Result<Option<String>>,
21) -> Result<Option<Variable<'_>>> {
22 for name in [own, BASE_URL] {
23 if let Some(value) = lookup(name)?.filter(|value| !value.trim().is_empty()) {
24 return Ok(Some(Variable { name, value }));
25 }
26 }
27 Ok(None)
28}
29
30#[must_use]
31pub fn base_url_missing(own: &str, purpose: &str) -> Error {
32 Error::internal_message(format!("neither {own} nor {BASE_URL} is set; {purpose}"))
33}
34
35fn unicode(name: &str, value: std::result::Result<String, VarError>) -> Result<Option<String>> {
36 match value {
37 Ok(value) => Ok(Some(value)),
38 Err(VarError::NotPresent) => Ok(None),
39 Err(VarError::NotUnicode(_)) => {
40 Err(Error::internal_message(format!("{name} is not Unicode")))
41 }
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use std::ffi::OsString;
48
49 use super::*;
50
51 #[test]
52 fn a_value_that_is_not_unicode_is_refused() {
53 let error = unicode("DATABASE_URL", Err(VarError::NotUnicode(OsString::new())))
54 .err()
55 .unwrap();
56 assert_eq!(error.to_string(), "DATABASE_URL is not Unicode");
57 }
58
59 fn given(variables: &[(&str, &str)]) -> impl Fn(&str) -> Result<Option<String>> {
60 let variables: Vec<(String, String)> = variables
61 .iter()
62 .map(|(name, value)| ((*name).to_owned(), (*value).to_owned()))
63 .collect();
64 move |name| {
65 Ok(variables
66 .iter()
67 .find(|(variable, _)| variable == name)
68 .map(|(_, value)| value.clone()))
69 }
70 }
71
72 #[test]
73 fn a_package_s_own_base_url_overrides_the_shared_one() {
74 const OWN: &str = "SOFIE_MAIL_BASE_URL";
75 let shared = base_url(OWN, given(&[(BASE_URL, "https://a.test")])).unwrap();
76 assert_eq!(
77 shared,
78 Some(Variable {
79 name: BASE_URL,
80 value: "https://a.test".to_owned()
81 })
82 );
83
84 let both = [(BASE_URL, "https://a.test"), (OWN, "https://mail.test")];
85 let own = base_url(OWN, given(&both)).unwrap().unwrap();
86 assert_eq!((own.name, own.value.as_str()), (OWN, "https://mail.test"));
87
88 let empty_own = [(BASE_URL, "https://a.test"), (OWN, " ")];
89 let fallen_back = base_url(OWN, given(&empty_own)).unwrap().unwrap();
90 assert_eq!(fallen_back.name, BASE_URL);
91 assert_eq!(base_url(OWN, given(&[])).unwrap(), None);
92 assert_eq!(
93 base_url_missing(OWN, "mails link back to the app").to_string(),
94 "neither SOFIE_MAIL_BASE_URL nor SOFIE_BASE_URL is set; mails link back to the app"
95 );
96 }
97}