Skip to main content

nil_env/
lib.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4#![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  /// # Safety
66  ///
67  /// See [`std::env::set_var`].
68  pub unsafe fn set(self, value: impl AsRef<OsStr>) {
69    unsafe { env::set_var(self, value) }
70  }
71
72  /// # Safety
73  ///
74  /// See [`std::env::remove_var`].
75  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  // Using a known secret should not be a problem for local servers.
94  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}