ez_rust_discovery/
lib.rs

1use std::{env, io::Error};
2use std::collections::HashMap;
3use std::env::VarError;
4use std::net::{AddrParseError, SocketAddr};
5use std::sync::Arc;
6use futures::executor::block_on;
7use futures::TryFutureExt;
8use local_ip_address::local_ip;
9use nacos_sdk::api::constants;
10use nacos_sdk::api::naming::{NamingService, NamingServiceBuilder, ServiceInstance};
11use nacos_sdk::api::props::ClientProps;
12use tracing::info;
13
14const META_GRPC_PORT: &'static str = "gRPC_port";
15
16#[derive(Debug)]
17pub enum EzError {
18    IO(Error),
19    Env(VarError, String),
20    Parse(AddrParseError),
21    LocalIP(local_ip_address::Error),
22    Other(String)
23}
24
25impl std::fmt::Display for EzError {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        match self {
28            EzError::IO(err) => write!(f,"IO Error: {}", err),
29            EzError::Env(err, name) => write!(f,"Read environment variables [{}] error: {}", name, err),
30            EzError::Parse(err) => write!(f,"Parse error: {}", err),
31            EzError::LocalIP(err) => write!(f,"Local IP error: {}", err),
32            EzError::Other(msg) => write!(f, "Other error: {}", msg),
33        }
34    }
35}
36
37impl std::error::Error for EzError {
38    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
39        match self {
40            EzError::IO(err) => Some(err),
41            EzError::Env(err, _) => Some(err),
42            EzError::Parse(err) => Some(err),
43            EzError::LocalIP(err) => Some(err),
44            _ => None,
45        }
46    }
47}
48
49impl From<Error> for EzError {
50    fn from(value: Error) -> Self {
51        EzError::IO(value)
52    }
53}
54
55impl From<AddrParseError> for EzError {
56    fn from(value: AddrParseError) -> Self {
57        EzError::Parse(value)
58    }
59}
60
61impl From<local_ip_address::Error> for EzError {
62    fn from(value: local_ip_address::Error) -> Self {
63        EzError::LocalIP(value)
64    }
65}
66
67pub struct ServeOptions {
68    pub addr: Option<String>,
69    pub namespace: Option<String>,
70    pub service_addr: Option<String>,
71    pub service_name: Option<String>,
72    pub service_host: Option<String>,
73}
74
75#[derive(Debug, Clone)]
76pub struct ServiceManager {
77    pub naming_service: Arc<NamingService>,
78    pub service_instance: ServiceInstance,
79    pub service_name: String,
80}
81
82impl ServiceManager {
83    pub fn new(opt: ServeOptions) -> Result<Self,EzError> {
84        let addr = parse_addr(match opt.addr {
85            Some(addr) => addr,
86            None => get_env("NACOS_ADDR")?
87        })?;
88        let namespace = match opt.namespace {
89            Some(namespace) => namespace,
90            None => get_env("NACOS_NAMESPACE")?
91        };
92        let service_addr = parse_addr(match opt.service_addr {
93            Some(service_addr) => parse_addr(service_addr)?,
94            None => get_env("SERVICE_ADDR")?
95        })?;
96        let service_name = match opt.service_name {
97            Some(service_name) => service_name,
98            None => get_env("SERVICE_NAME")?
99        };
100        let local_ip = local_ip()?.to_string();
101        let service_host = opt.service_host.unwrap_or_else(|| get_env("SERVICE_HOST").unwrap_or(local_ip));
102        info!("[NACOS_ADDR]: {}", addr);
103        info!("[NACOS_NAMESPACE]: {}", namespace);
104        info!("[SERVICE_ADDR]: {}", service_addr);
105        info!("[SERVICE_NAME]: {}", service_name);
106        info!("[SERVICE_HOST]: {}", service_host);
107        let naming_service = NamingServiceBuilder::new(
108            ClientProps::new().server_addr(addr).namespace(namespace))
109            .build()
110            .map_err(|e| {
111                EzError::Other(format!("NamingService create failed: {}", e))
112            })?;
113        let (_, port) = match service_addr.split_once(":") {
114            Some(value) => value,
115            None => return Err(EzError::Other("Invalid service address".to_string()))
116        };
117
118
119        let instance = ServiceInstance{
120            ip: service_host,
121            port: port.parse::<i32>().unwrap(),
122            weight: 1.0,
123            healthy: true,
124            enabled: true,
125            ephemeral: true,
126            metadata: HashMap::from([(META_GRPC_PORT.to_string(), port.to_string())]),
127            ..Default::default()
128        };
129        Ok(Self{
130            naming_service: Arc::new(naming_service),
131            service_instance: instance,
132            service_name,
133        })
134    }
135
136    pub fn online(&self) -> Result<(),EzError> {
137        block_on(self.naming_service.register_instance(self.service_name.clone(), Some(constants::DEFAULT_GROUP.to_string()), self.service_instance.clone()))
138            .map_err(|e| EzError::Other(format!("Service online error: {}", e.to_string())))?;
139        info!("Service online successfully");
140        Ok(())
141    }
142    pub fn offline(&self) -> Result<(), EzError> {
143        block_on(self.naming_service.deregister_instance(self.service_name.clone(), Some(constants::DEFAULT_GROUP.to_string()), self.service_instance.clone())
144            .map_err(|e| EzError::Other(format!("Service offline error: {}", e.to_string()))))?;
145        info!("Service offline successfully");
146        Ok(())
147    }
148}
149
150impl Default for ServeOptions {
151    fn default() -> Self {
152        Self{
153            addr: None,
154            namespace: None,
155            service_addr: None,
156            service_name: None,
157            service_host: None,
158        }
159    }
160}
161
162fn get_env(name: &str) -> Result<String, EzError> {
163    let res = env::var(name);
164    match res {
165        Ok(val) => Ok(val),
166        Err(err) => Err(EzError::Env(err, String::from(name)))
167    }
168}
169
170fn parse_addr(addr: String) -> Result<String, EzError> {
171    match addr.parse::<SocketAddr>() {
172        Ok(_) => Ok(addr),
173        Err(err) => Err(EzError::Parse(err))
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use std::thread::sleep;
181    #[test]
182    fn test_online(){
183        let manager = ServiceManager::new(ServeOptions::default()).unwrap();
184        manager.online().unwrap();
185        sleep(std::time::Duration::from_secs(10));
186        manager.offline().unwrap();
187    }
188}