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