1#![cfg_attr(docsrs, feature(doc_cfg))]
5#![doc(html_favicon_url = "https://nil.dev.br/favicon.png")]
6#![feature(const_clone, const_cmp, derive_const)]
7
8use anyhow::{Error, Result, anyhow};
9use serde::{Deserialize, Serialize};
10use std::env;
11use std::ffi::OsStr;
12use std::num::NonZeroU16;
13use std::path::Path;
14use strum::{AsRefStr, Display, EnumString};
15use url::Url;
16
17#[derive(Copy, Debug, Display, AsRefStr, EnumString, Deserialize, Serialize)]
18#[derive_const(Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
20#[cfg_attr(feature = "typescript", ts(export, repr(enum = name)))]
21#[cfg_attr(feature = "typescript", ts(rename = "env_Var"))]
22pub enum Var {
23 #[serde(rename = "NIL_DATABASE_URL")]
24 #[strum(serialize = "NIL_DATABASE_URL")]
25 DatabaseUrl,
26
27 #[serde(rename = "NIL_JWT_SECRET")]
28 #[strum(serialize = "NIL_JWT_SECRET")]
29 JwtSecret,
30
31 #[serde(rename = "NIL_GENERATE_FFI_BINDINGS")]
32 #[strum(serialize = "NIL_GENERATE_FFI_BINDINGS")]
33 GenerateFfiBindings,
34
35 #[serde(rename = "NIL_LOG_DIR")]
36 #[strum(serialize = "NIL_LOG_DIR")]
37 LogDir,
38
39 #[serde(rename = "NIL_LOG_LEVEL")]
40 #[strum(serialize = "NIL_LOG_LEVEL")]
41 LogLevel,
42
43 #[serde(rename = "NIL_LOG_TOWER_HTTP")]
44 #[strum(serialize = "NIL_LOG_TOWER_HTTP")]
45 LogTowerHttp,
46
47 #[serde(rename = "NIL_MINIFY_SOURCE")]
48 #[strum(serialize = "NIL_MINIFY_SOURCE")]
49 MinifySource,
50
51 #[serde(rename = "NIL_REMOTE_SERVER_ADDR")]
52 #[strum(serialize = "NIL_REMOTE_SERVER_ADDR")]
53 RemoteServerAddr,
54
55 #[serde(rename = "NIL_REMOTE_WORLD_LIMIT")]
56 #[strum(serialize = "NIL_REMOTE_WORLD_LIMIT")]
57 RemoteWorldLimit,
58
59 #[serde(rename = "NIL_REMOTE_WORLD_LIMIT_PER_USER")]
60 #[strum(serialize = "NIL_REMOTE_WORLD_LIMIT_PER_USER")]
61 RemoteWorldLimitPerUser,
62}
63
64impl Var {
65 pub unsafe fn set(self, value: impl AsRef<OsStr>) {
69 unsafe { env::set_var(self, value) }
70 }
71
72 pub unsafe fn remove(self) {
76 unsafe { env::remove_var(self) }
77 }
78}
79
80impl AsRef<OsStr> for Var {
81 fn as_ref(&self) -> &OsStr {
82 OsStr::new(<Var as AsRef<str>>::as_ref(self))
83 }
84}
85
86pub fn database_url() -> Result<Box<str>> {
87 env::var(Var::DatabaseUrl)
88 .map(String::into_boxed_str)
89 .map_err(|_| on_var_err(Var::DatabaseUrl))
90}
91
92pub fn jwt_secret() -> Box<str> {
93 env::var(Var::JwtSecret)
95 .map(String::into_boxed_str)
96 .unwrap_or_else(|_| Box::from("CALL-OF-NIL"))
97}
98
99pub fn log_dir() -> Result<Box<Path>> {
100 env::var(Var::LogDir)
101 .map(|dir| Box::from(Path::new(&dir)))
102 .map_err(|_| on_var_err(Var::LogDir))
103}
104
105pub fn log_level() -> Box<str> {
106 env::var(Var::LogLevel)
107 .map(String::into_boxed_str)
108 .unwrap_or_else(|_| Box::from("trace"))
109}
110
111pub fn log_tower_http() -> bool {
112 env::var(Var::LogTowerHttp).is_ok_and(|it| it == "true")
113}
114
115pub fn generate_ffi_bindings() -> bool {
116 env::var(Var::GenerateFfiBindings).is_ok_and(|it| it == "true")
117}
118
119pub fn remote_server_addr() -> Url {
120 if let Ok(addr) = env::var(Var::RemoteServerAddr)
121 && let Ok(url) = Url::parse(&addr)
122 {
123 url
124 } else {
125 Url::parse("https://tsukilabs.dev.br/nil/").unwrap()
126 }
127}
128
129pub fn remote_world_limit() -> NonZeroU16 {
130 env::var(Var::RemoteWorldLimit)
131 .ok()
132 .and_then(|it| it.parse::<NonZeroU16>().ok())
133 .unwrap_or_else(|| unsafe { NonZeroU16::new_unchecked(100) })
134}
135
136pub fn remote_world_limit_per_user() -> NonZeroU16 {
137 env::var(Var::RemoteWorldLimitPerUser)
138 .ok()
139 .and_then(|it| it.parse::<NonZeroU16>().ok())
140 .unwrap_or_else(|| unsafe { NonZeroU16::new_unchecked(3) })
141}
142
143pub(crate) fn on_var_err(var: Var) -> Error {
144 anyhow!("environment variable not found: {var}")
145}