dubbo_config/
config.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18use std::{collections::HashMap, env, fs};
19
20use crate::{protocol::Protocol, registry::RegistryConfig};
21use once_cell::sync::OnceCell;
22use serde::{Deserialize, Serialize};
23use tracing::trace;
24
25use super::{protocol::ProtocolConfig, provider::ProviderConfig, service::ServiceConfig};
26
27pub const DUBBO_CONFIG_PATH: &str = "./dubbo.yaml";
28
29pub static GLOBAL_ROOT_CONFIG: OnceCell<RootConfig> = OnceCell::new();
30pub const DUBBO_CONFIG_PREFIX: &str = "dubbo";
31
32/// used to storage all structed config, from some source: cmd, file..;
33/// Impl Config trait, business init by read Config trait
34#[allow(dead_code)]
35#[derive(Debug, Default, Serialize, Deserialize, Clone)]
36pub struct RootConfig {
37    #[serde(default)]
38    pub protocols: ProtocolConfig,
39
40    #[serde(default)]
41    pub provider: ProviderConfig,
42
43    #[serde(default)]
44    pub registries: HashMap<String, RegistryConfig>,
45
46    #[serde(default)]
47    pub data: HashMap<String, String>,
48}
49
50pub fn get_global_config() -> &'static RootConfig {
51    GLOBAL_ROOT_CONFIG.get_or_init(|| {
52        tracing::debug!("current path: {:?}", env::current_dir());
53        RootConfig::new()
54            .load()
55            .unwrap_or_else(|err| panic!("Failed to load global config, error: {}", err))
56    })
57}
58
59impl RootConfig {
60    pub fn new() -> Self {
61        Self {
62            protocols: HashMap::new(),
63            registries: HashMap::new(),
64            provider: ProviderConfig::new(),
65            data: HashMap::new(),
66        }
67    }
68
69    pub fn load(&self) -> std::io::Result<Self> {
70        let config_path = match env::var("DUBBO_CONFIG_PATH") {
71            Ok(v) => {
72                tracing::info!("read config_path from env: {:?}", v);
73                v
74            }
75            Err(err) => {
76                tracing::warn!(
77                    "error loading config_path: {:?}, use default path: {:?}",
78                    err,
79                    DUBBO_CONFIG_PATH
80                );
81                DUBBO_CONFIG_PATH.to_string()
82            }
83        };
84
85        tracing::info!("current path: {:?}", env::current_dir());
86        let data = fs::read(config_path)?;
87        trace!("config data: {:?}", String::from_utf8(data.clone()));
88        let conf: HashMap<String, RootConfig> = serde_yaml::from_slice(&data).unwrap();
89        let root_config: RootConfig = conf.get(DUBBO_CONFIG_PREFIX).unwrap().clone();
90        tracing::debug!("origin config: {:?}", conf);
91        Ok(root_config)
92    }
93
94    pub fn test_config(&mut self) {
95        let mut provider = ProviderConfig::new();
96        provider.protocol_ids = vec!["triple".to_string()];
97        provider.registry_ids = vec![];
98
99        let service_config = ServiceConfig::default()
100            .group("test".to_string())
101            .version("1.0.0".to_string())
102            .protocol("triple".to_string())
103            .interface("grpc.examples.echo.Echo".to_string());
104
105        self.provider
106            .services
107            .insert("grpc.examples.echo.Echo".to_string(), service_config);
108        self.provider.services.insert(
109            "helloworld.Greeter".to_string(),
110            ServiceConfig::default()
111                .group("test".to_string())
112                .version("1.0.0".to_string())
113                .interface("helloworld.Greeter".to_string())
114                .protocol("triple".to_string()),
115        );
116        self.protocols.insert(
117            "triple".to_string(),
118            Protocol::default()
119                .name("triple".to_string())
120                .ip("0.0.0.0".to_string())
121                .port("8889".to_string()),
122        );
123
124        self.provider = provider.clone();
125        println!("provider config: {:?}", provider);
126        // 通过环境变量读取某个文件。加在到内存中
127        self.data.insert(
128            "dubbo.provider.url".to_string(),
129            "dubbo://127.0.0.1:8888/?serviceName=hellworld".to_string(),
130        );
131        // self.data.insert("dubbo.consume.", v)
132    }
133
134    #[inline]
135    pub fn leak(self) -> &'static Self {
136        Box::leak(Box::new(self))
137    }
138}
139
140impl Config for RootConfig {
141    fn bool(&self, key: String) -> bool {
142        match self.data.get(&key) {
143            None => false,
144            Some(val) => match val.parse::<bool>() {
145                Ok(v) => v,
146                Err(_err) => {
147                    tracing::error!("key: {}, val: {} is not boolean", key, val);
148                    false
149                }
150            },
151        }
152    }
153
154    fn string(&self, key: String) -> String {
155        match self.data.get(&key) {
156            None => "".to_string(),
157            Some(val) => val.to_string(),
158        }
159    }
160}
161
162pub trait BusinessConfig {
163    fn init() -> Self;
164    fn load() -> Result<(), std::convert::Infallible>;
165}
166
167pub trait Config {
168    fn bool(&self, key: String) -> bool;
169    fn string(&self, key: String) -> String;
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn test_config() {
178        let mut r = RootConfig::new();
179        r.test_config();
180    }
181
182    #[test]
183    fn test_load() {
184        // case 1: read config yaml from default path
185        println!("current path: {:?}", env::current_dir());
186        let r = RootConfig::new();
187        r.load();
188    }
189
190    #[test]
191    fn test_load1() {
192        // case 2: read config yaml from path in env
193        println!("current path: {:?}", env::current_dir());
194        let r = RootConfig::new();
195        r.load();
196    }
197
198    #[test]
199    fn test_write_yaml() {
200        let mut r = RootConfig::new();
201        r.test_config();
202        let yaml = serde_yaml::to_string(&r).unwrap();
203        println!("config data: {:?}", yaml);
204
205        fs::write("./test_dubbo.yaml", yaml);
206    }
207}