1use crate::error::Result;
5use serde::{Deserialize, Serialize};
6use std::env;
7use std::ffi::{OsStr, OsString};
8use std::net::SocketAddrV4;
9use std::sync::{Arc, LazyLock};
10use strum::EnumIs;
11use tap::Pipe;
12use url::Url;
13
14const URL: &str = "https://tsukilabs.dev.br/nil/";
15
16static REMOTE_SERVER_ADDR: LazyLock<Url> = LazyLock::new(|| {
17 env::var("NIL_REMOTE_SERVER_ADDR")
18 .unwrap_or_else(|_| URL.to_owned())
19 .pipe_deref(Url::parse)
20 .expect("Failed to parse remote server address")
21});
22
23#[derive(Clone, Copy, Debug, Default, EnumIs, PartialEq, Eq, Hash, Deserialize, Serialize)]
24#[serde(tag = "kind", rename_all = "kebab-case")]
25pub enum ServerAddr {
26 #[default]
27 Remote,
28 Local {
29 addr: SocketAddrV4,
30 },
31}
32
33impl ServerAddr {
34 #[inline]
35 pub fn url(&self, route: &str) -> Result<Url> {
36 match self {
37 Self::Remote => Ok(REMOTE_SERVER_ADDR.join(route)?),
38 Self::Local { addr } => {
39 let ip = addr.ip();
40 let port = addr.port();
41 Ok(Url::parse(&format!("http://{ip}:{port}/{route}"))?)
42 }
43 }
44 }
45}
46
47impl From<SocketAddrV4> for ServerAddr {
48 fn from(addr: SocketAddrV4) -> Self {
49 Self::Local { addr }
50 }
51}
52
53impl From<&[u8]> for ServerAddr {
54 fn from(bytes: &[u8]) -> Self {
55 if let Ok(addr) = SocketAddrV4::parse_ascii(bytes) {
56 Self::Local { addr }
57 } else {
58 Self::Remote
59 }
60 }
61}
62
63impl From<&OsStr> for ServerAddr {
64 fn from(value: &OsStr) -> Self {
65 Self::from(value.as_encoded_bytes())
66 }
67}
68
69impl From<OsString> for ServerAddr {
70 fn from(value: OsString) -> Self {
71 Self::from(value.as_os_str())
72 }
73}
74
75macro_rules! from_bytes {
76 ($($type_:ty),+ $(,)?) => {
77 $(
78 impl From<$type_> for ServerAddr {
79 fn from(value: $type_) -> Self {
80 Self::from(value.as_bytes())
81 }
82 }
83 )+
84 };
85}
86
87from_bytes!(&str, String, &String, Arc<str>, Box<str>);