Skip to main content

nadisc_runtime/
assembly.rs

1//! 多后端类型化装配。输入已经完成占位符解析,公共组件不依赖任何应用配置框架。
2
3use crate::{
4    config::{invalid, validate_name, ConnectionPolicy, Limits, RegistrationPolicy},
5    providers::LocalProvider,
6    ConnectionSpec, DiscoveryOwner, Eligibility, OwnerBuilder, RegistrationSpec,
7};
8use nadisc::{
9    managed::{ManagedProvider, RegistrationPlan, Result},
10    DnsService, Instance,
11};
12use serde::{
13    de::{Error, MapAccess, Visitor},
14    Deserialize, Serialize,
15};
16use std::{
17    collections::{BTreeMap, HashMap},
18    sync::Arc,
19};
20
21/// 多注册中心连接、发布目标与资源预算;配置冻结后由同一 owner 管理生命周期。
22#[derive(Clone, Default, Deserialize, Serialize)]
23#[serde(default, deny_unknown_fields)]
24pub struct DiscoveryConfig {
25    /// 是否启用发现;禁用时不得同时声明连接、注册或资格探针。
26    pub enabled: bool,
27    /// 是否启用宿主本地资格探针;不以远端注册结果反向驱动本地资格。
28    pub eligibility_probe: bool,
29    /// 兼容显式默认引用;省略时只采用名为 primary 的连接,不按数量或顺序推断。
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub default_connection: Option<String>,
32    /// 所有连接和发布目标共享的有限资源预算。
33    pub limits: Limits,
34    /// 以 qualifier 为键的连接目录,不按顺序推断默认连接。
35    #[serde(deserialize_with = "unique_map")]
36    pub connections: BTreeMap<String, ConnectionConfig>,
37    /// 注册目标集合,包含各目标独立的状态或发布策略。
38    #[serde(deserialize_with = "unique_map")]
39    pub registrations: BTreeMap<String, RegistrationConfig>,
40}
41
42/// 单个 qualifier 的后端配置;provider 必须与唯一的后端配置块一致。
43#[derive(Clone, Deserialize, Serialize)]
44#[serde(default, deny_unknown_fields)]
45pub struct ConnectionConfig {
46    /// 后端类型或实例,决定该连接支持的发现及注册能力。
47    pub provider: String,
48    /// 是否要求启动阶段确认成功;失败时阻止宿主完成启动。
49    pub startup_required: bool,
50    /// 一次请求的最大耗时,单位为毫秒。
51    pub request_timeout_ms: u64,
52    /// 后台轮询间隔,单位为毫秒,必须留出请求和证据时效预算。
53    pub poll_interval_ms: u64,
54    /// 无人使用的服务观察保留时长,单位为毫秒。
55    pub idle_timeout_ms: u64,
56    /// 共享发现证据的年龄上限与异常宽限。
57    pub snapshot: SnapshotPolicy,
58    /// 连接准备失败时的重试退避预算。
59    pub retry: RetryPolicy,
60    /// Nacos 专用配置,只有 Nacos 后端可以使用。
61    #[serde(deserialize_with = "optional_unique_value")]
62    pub nacos: Option<serde_json::Value>,
63    /// Consul 专用配置,只有 Consul 后端可以使用。
64    #[serde(deserialize_with = "optional_unique_value")]
65    pub consul: Option<serde_json::Value>,
66    /// Kubernetes 只读发现配置,不授予 Service 或 EndpointSlice 写入权。
67    #[serde(deserialize_with = "optional_unique_value")]
68    pub kubernetes: Option<serde_json::Value>,
69    /// 静态实例映射;配置文件中的键为 static。
70    #[serde(rename = "static")]
71    pub static_config: Option<StaticConfig>,
72    /// DNS 服务映射,不支持服务注册。
73    pub dns: Option<DnsConfig>,
74    /// 该连接的 REST 调用策略,与其他 qualifier 的调用预算隔离。
75    pub rest: Option<RestPolicy>,
76}
77
78/// 宿主 REST 桥的独立调用策略;共享发现时效仅由 snapshot 策略裁决。
79#[derive(Clone, Deserialize, Serialize)]
80#[serde(default, deny_unknown_fields)]
81pub struct RestPolicy {
82    /// 一次请求的最大耗时,单位为毫秒。
83    pub request_timeout_ms: u64,
84    /// HTTP 建连预算,单位为毫秒,不得超过请求总预算。
85    pub connect_timeout_ms: u64,
86    /// 实例选择策略,可使用 round_robin 或 weighted。
87    pub lb_strategy: String,
88    /// 发现实例的 HTTP 协议,只接受 http 或 https。
89    pub instance_scheme: String,
90    /// 是否允许 GET/HEAD 在传输错误后按剩余预算重试。
91    pub retry_get_head_on_transport_error: bool,
92    /// 是否允许 GET/HEAD 在可重试响应状态后重试。
93    pub retry_get_head_on_retryable_status: bool,
94    /// 一次逻辑请求允许的总尝试次数,包含首次调用。
95    pub max_attempts: usize,
96}
97impl Default for RestPolicy {
98    /// 业务作用:给各连接使用独立有限的 HTTP 调用预算,默认不重试。
99    ///
100    /// 参数说明:无。
101    ///
102    /// 返回:十秒总预算、两秒连接预算和轮询分配策略。
103    fn default() -> Self {
104        Self {
105            request_timeout_ms: 10000,
106            connect_timeout_ms: 2000,
107            lb_strategy: "round_robin".into(),
108            instance_scheme: "http".into(),
109            retry_get_head_on_transport_error: false,
110            retry_get_head_on_retryable_status: false,
111            max_attempts: 1,
112        }
113    }
114}
115impl RestPolicy {
116    /// 业务作用:在连接准备前拒绝非法调用时长、协议、分配策略和重试次数。
117    ///
118    /// 参数说明:无。
119    ///
120    /// 返回:策略有效时成功;不开放任意 HTTP 方法的自动重试。
121    pub fn validate(&self) -> Result<()> {
122        if self.request_timeout_ms == 0
123            || self.request_timeout_ms > 86_400_000
124            || self.connect_timeout_ms == 0
125            || self.connect_timeout_ms > self.request_timeout_ms
126            || !matches!(self.lb_strategy.as_str(), "round_robin" | "weighted")
127            || !matches!(self.instance_scheme.as_str(), "http" | "https")
128            || !(1..=16).contains(&self.max_attempts)
129        {
130            return Err(invalid("invalid per-connection REST policy"));
131        }
132        Ok(())
133    }
134}
135
136/// 发现证据的有效期与查询异常后的有限宽限,单位为毫秒。
137#[derive(Clone, Deserialize, Serialize)]
138#[serde(default, deny_unknown_fields)]
139pub struct SnapshotPolicy {
140    /// 发现证据的最大年龄,单位为毫秒。
141    pub max_age_ms: u64,
142    /// 查询异常后的旧证据宽限,单位为毫秒;零表示禁止异常复用。
143    pub stale_if_error_ms: u64,
144}
145impl Default for SnapshotPolicy {
146    /// 业务作用:默认禁止异常后继续使用旧路由。
147    ///
148    /// 参数说明:无。
149    ///
150    /// 返回:十五秒最大证据年龄和零异常宽限。
151    fn default() -> Self {
152        Self {
153            max_age_ms: 15000,
154            stale_if_error_ms: 0,
155        }
156    }
157}
158
159/// 连接准备失败后的有限退避策略,单位为毫秒。
160#[derive(Clone, Deserialize, Serialize)]
161#[serde(default, deny_unknown_fields)]
162pub struct RetryPolicy {
163    /// 连接恢复的初始退避,单位为毫秒。
164    pub initial_backoff_ms: u64,
165    /// 连接恢复退避的上限,单位为毫秒。
166    pub max_backoff_ms: u64,
167}
168impl Default for RetryPolicy {
169    /// 业务作用:对不可达连接限制恢复重试频率。
170    ///
171    /// 参数说明:无。
172    ///
173    /// 返回:一秒起步、三十秒封顶的退避。
174    fn default() -> Self {
175        Self {
176            initial_backoff_ms: 1000,
177            max_backoff_ms: 30000,
178        }
179    }
180}
181
182impl Default for ConnectionConfig {
183    /// 业务作用:默认连接必须准备成功,但没有指定 provider 时不猜测后端。
184    ///
185    /// 参数说明:无。
186    ///
187    /// 返回:有限请求和观察预算的候选配置。
188    fn default() -> Self {
189        Self {
190            provider: String::new(),
191            startup_required: true,
192            request_timeout_ms: 3000,
193            poll_interval_ms: 5000,
194            idle_timeout_ms: 30000,
195            snapshot: SnapshotPolicy::default(),
196            retry: RetryPolicy::default(),
197            nacos: None,
198            consul: None,
199            kubernetes: None,
200            static_config: None,
201            dns: None,
202            rest: None,
203        }
204    }
205}
206
207/// 由配置提供的固定实例集合,不向注册中心写入。
208#[derive(Clone, Deserialize, Serialize)]
209#[serde(deny_unknown_fields)]
210pub struct StaticConfig {
211    /// 按逻辑服务名绑定的目标,不接受重复键。
212    #[serde(deserialize_with = "unique_map")]
213    pub services: BTreeMap<String, Vec<Instance>>,
214}
215/// 逻辑服务到 DNS 名称和端口的显式映射。
216#[derive(Clone, Deserialize, Serialize)]
217#[serde(deny_unknown_fields)]
218pub struct DnsConfig {
219    /// 按逻辑服务名绑定的目标,不接受重复键。
220    #[serde(deserialize_with = "unique_map")]
221    pub services: BTreeMap<String, DnsTarget>,
222}
223/// DNS 查询目标;端口由配置确定,不从名称推断。
224#[derive(Clone, Deserialize, Serialize)]
225#[serde(deny_unknown_fields)]
226pub struct DnsTarget {
227    /// 需要解析的 DNS 主机名。
228    pub host: String,
229    /// 目标监听端口;注册配置省略时由 listener_ref 解析。
230    pub port: u16,
231}
232
233/// 单个服务发布目标及其启动、健康回查和摘流策略。
234#[derive(Clone, Deserialize, Serialize)]
235#[serde(default, deny_unknown_fields)]
236pub struct RegistrationConfig {
237    /// 发布目标的 qualifier;Rust 字段保留兼容名称,配置序列化使用 qualifier。
238    #[serde(rename = "qualifier", alias = "connection_ref")]
239    pub connection_ref: String,
240    /// 发布到目标后端的服务名称,必须存在对应服务绑定。
241    pub service_name: String,
242    /// 对外可达地址;注册配置可显式覆盖宿主监听地址。
243    pub ip: Option<String>,
244    /// 目标监听端口;注册配置省略时由 listener_ref 解析。
245    pub port: Option<u16>,
246    /// 宿主已绑定监听器的引用,用于解析未显式指定的地址与端口。
247    pub listener_ref: Option<String>,
248    /// 宿主本地业务资格引用,控制是否允许发布实例。
249    pub eligibility_ref: Option<String>,
250    /// 随注册写入的实例属性,不得携带凭据。
251    pub metadata: HashMap<String, String>,
252    /// 是否要求启动阶段确认成功;失败时阻止宿主完成启动。
253    pub startup_required: bool,
254    /// 远端注册失去有效健康证据时是否影响宿主整体就绪。
255    pub readiness_critical: bool,
256    /// 注册健康回查间隔,单位为毫秒。
257    pub monitor_interval_ms: u64,
258    /// 注册健康证据的有效窗口,单位为毫秒。
259    pub observation_grace_ms: u64,
260    /// 单项注销预算,单位为毫秒,仍受宿主关闭总期限限制。
261    pub deregister_timeout_ms: u64,
262    /// Nacos 专用配置,只有 Nacos 后端可以使用。
263    pub nacos: Option<NacosRegistration>,
264    /// Consul 专用配置,只有 Consul 后端可以使用。
265    #[serde(deserialize_with = "optional_unique_value")]
266    pub consul: Option<serde_json::Value>,
267}
268
269impl Default for RegistrationConfig {
270    /// 业务作用:注册目标省略 qualifier 时使用 primary,默认要求首次注册确认。
271    ///
272    /// 参数说明:无。
273    ///
274    /// 返回:服务与地址待显式配置或宿主解析的注册项;兼容默认连接覆盖不改变此处的 primary。
275    fn default() -> Self {
276        Self {
277            connection_ref: "primary".into(),
278            service_name: String::new(),
279            ip: None,
280            port: None,
281            listener_ref: None,
282            eligibility_ref: None,
283            metadata: HashMap::new(),
284            startup_required: true,
285            readiness_critical: false,
286            monitor_interval_ms: 5000,
287            observation_grace_ms: 15000,
288            deregister_timeout_ms: 3000,
289            nacos: None,
290            consul: None,
291        }
292    }
293}
294
295/// Nacos 注册专用属性,不用于其他后端。
296#[derive(Clone, Deserialize, Serialize)]
297#[serde(default, deny_unknown_fields)]
298pub struct NacosRegistration {
299    /// 是否发布 Nacos 临时实例,由客户端维持其存活。
300    pub ephemeral: bool,
301    /// Nacos 实例所属集群名称。
302    pub cluster_name: String,
303    /// Nacos 实例选择权重。
304    pub weight: f64,
305}
306impl Default for NacosRegistration {
307    /// 业务作用:显式提供 Nacos 临时实例和默认集群属性。
308    ///
309    /// 参数说明:无。
310    ///
311    /// 返回:默认权重为一,不影响其他后端。
312    fn default() -> Self {
313        Self {
314            ephemeral: true,
315            cluster_name: "DEFAULT".into(),
316            weight: 1.0,
317        }
318    }
319}
320
321/// 宿主已绑定 listener 的事实;代理映射地址由 registration 的 ip/port 显式覆盖。
322#[derive(Clone)]
323pub struct ListenerEndpoint {
324    /// 对外可达地址;注册配置可显式覆盖宿主监听地址。
325    pub ip: String,
326    /// 目标监听端口;注册配置省略时由 listener_ref 解析。
327    pub port: u16,
328}
329
330impl DiscoveryConfig {
331    /// 业务作用:校验所有引用与 provider 块,必需注册提升所属连接的启动要求。
332    ///
333    /// 参数说明:无。
334    ///
335    /// 返回:配置可用于独立宿主或 napp;禁用配置不产生后端连接,引用与 provider 拒绝携带字段路径。
336    pub fn validate(&self) -> Result<()> {
337        self.limits
338            .validate()
339            .map_err(|error| error.at_config_path("discovery.limits"))?;
340        if !self.enabled
341            && (!self.connections.is_empty()
342                || !self.registrations.is_empty()
343                || self.default_connection.is_some()
344                || self.eligibility_probe)
345        {
346            return Err(invalid(
347                "disabled discovery cannot declare connections, registrations or probes",
348            ));
349        }
350        if self.connections.len() > self.limits.max_connections
351            || self.registrations.len() > self.limits.max_registrations
352        {
353            return Err(invalid("discovery configuration exceeds capacity"));
354        }
355        if let Some(name) = &self.default_connection {
356            if !self.connections.contains_key(name) {
357                return Err(invalid("unknown default connection")
358                    .at_config_path("discovery.default_connection"));
359            }
360        }
361        for (name, connection) in &self.connections {
362            validate_name(name).map_err(|error| error.at_config_path("discovery.connections"))?;
363            connection
364                .validate()
365                .map_err(|error| error.at_config_path(format!("discovery.connections.{name}")))?;
366        }
367        for (name, registration) in &self.registrations {
368            validate_name(name).map_err(|error| error.at_config_path("discovery.registrations"))?;
369            let connection = self
370                .connections
371                .get(&registration.connection_ref)
372                .ok_or_else(|| {
373                    invalid("unknown registration connection")
374                        .at_config_path(format!("discovery.registrations.{name}.qualifier"))
375                })?;
376            if !connection.has_service(&registration.service_name) {
377                return Err(invalid("unknown registration service binding")
378                    .at_config_path(format!("discovery.registrations.{name}.service_name")));
379            }
380            if registration.service_name.is_empty()
381                || registration.service_name.len() > 512
382                || registration
383                    .ip
384                    .as_ref()
385                    .is_some_and(|ip| ip.is_empty() || ip.trim() != ip)
386                || registration.port == Some(0)
387                || registration.metadata.len() > 128
388                || registration
389                    .metadata
390                    .iter()
391                    .any(|(key, value)| key.len() > 256 || value.len() > 4096)
392            {
393                return Err(invalid(
394                    "invalid registration service, endpoint or metadata",
395                ));
396            }
397            registration.policy().validate(&connection.policy())?;
398            match connection.provider.as_str() {
399                "nacos" if registration.consul.is_none() => {}
400                "consul" if registration.nacos.is_none() && registration.consul.is_some() => {}
401                _ => {
402                    return Err(invalid(
403                        "registration attributes do not match a writable provider",
404                    ))
405                }
406            }
407        }
408        Ok(())
409    }
410
411    /// 业务作用:构造各身份隔离的 provider 和公共 owner,不启动注册或网络轮询。
412    ///
413    /// 参数说明: 无。
414    ///
415    /// 配置值应已完成宿主占位符解析。
416    ///
417    /// 返回:唯一关闭 owner;只启用已编入的 provider。
418    pub async fn build(&self) -> Result<DiscoveryOwner> {
419        self.validate()?;
420        let mut builder = OwnerBuilder::default().limits(self.limits.clone());
421        if !self.enabled {
422            return builder.build();
423        }
424        if let Some(default) = &self.default_connection {
425            builder = builder.default_connection(default);
426        }
427        for (name, config) in &self.connections {
428            let mut policy = config.policy();
429            policy.startup_required |= self.registrations.values().any(|registration| {
430                registration.connection_ref == *name && registration.startup_required
431            });
432            builder = builder.connection(ConnectionSpec {
433                name: name.clone(),
434                provider: config.build().await.map_err(|error| {
435                    error.at_config_path(format!("discovery.connections.{name}"))
436                })?,
437                policy,
438            })?;
439        }
440        builder.build()
441    }
442
443    /// 业务作用:在全部监听器已绑定后解析一次完整注册集合。
444    ///
445    /// 参数说明:
446    /// - `listeners`:是实际端点
447    /// - `eligibility`:是宿主本地资格源;单项时允许省略对应引用。
448    ///
449    /// 返回:全部可解析的计划;不执行外部注册,不生成业务专用 metadata。
450    pub fn resolve(
451        &self,
452        listeners: &BTreeMap<String, ListenerEndpoint>,
453        eligibility: &BTreeMap<String, Eligibility>,
454    ) -> Result<Vec<RegistrationSpec>> {
455        if !self.enabled {
456            return Ok(Vec::new());
457        }
458        self.validate()?;
459        let mut plans = Vec::new();
460        for (name, registration) in &self.registrations {
461            let listener = match &registration.listener_ref {
462                Some(name) => Some(
463                    listeners
464                        .get(name)
465                        .ok_or_else(|| invalid("unknown registration listener"))?,
466                ),
467                None if listeners.len() == 1 => listeners.values().next(),
468                None => None,
469            };
470            let inferred_listener = registration
471                .listener_ref
472                .as_ref()
473                .or_else(|| (listeners.len() == 1).then(|| listeners.keys().next().unwrap()));
474            let eligibility = match &registration.eligibility_ref {
475                Some(name) => eligibility.get(name),
476                None if inferred_listener.is_some() => eligibility.get(inferred_listener.unwrap()),
477                None if eligibility.len() == 1 => eligibility.values().next(),
478                None => None,
479            }
480            .cloned()
481            .ok_or_else(|| invalid("registration eligibility source is absent or ambiguous"))?;
482            let ip = registration
483                .ip
484                .as_ref()
485                .or_else(|| listener.map(|listener| &listener.ip))
486                .ok_or_else(|| invalid("registration IP is absent"))?;
487            let port = registration
488                .port
489                .or_else(|| listener.map(|listener| listener.port))
490                .ok_or_else(|| invalid("registration port is absent"))?;
491            let mut instance =
492                Instance::new(ip.clone(), port).with_metadata_map(registration.metadata.clone());
493            if let Some(nacos) = &registration.nacos {
494                instance = instance
495                    .with_cluster(&nacos.cluster_name)
496                    .with_ephemeral(nacos.ephemeral)
497                    .with_weight(nacos.weight);
498            }
499            #[allow(unused_mut)]
500            let mut plan = RegistrationPlan {
501                id: name.clone(),
502                service: registration.service_name.clone(),
503                instance,
504                attributes: BTreeMap::new(),
505            };
506            if let Some(consul) = &registration.consul {
507                #[cfg(feature = "consul")]
508                {
509                    #[derive(Deserialize)]
510                    #[serde(deny_unknown_fields)]
511                    struct CheckConfig {
512                        check: nadisc_consul::HealthCheck,
513                    }
514                    let config: CheckConfig = serde_json::from_value(consul.clone())
515                        .map_err(|_| invalid("invalid Consul registration configuration"))?;
516                    config.check.apply(&mut plan)?;
517                }
518                #[cfg(not(feature = "consul"))]
519                {
520                    let _ = consul;
521                    return Err(invalid("Consul provider feature is disabled"));
522                }
523            }
524            plans.push(RegistrationSpec {
525                connection_ref: registration.connection_ref.clone(),
526                plan,
527                policy: registration.policy(),
528                eligibility,
529            });
530        }
531        Ok(plans)
532    }
533}
534
535impl RegistrationConfig {
536    /// 业务作用:将配置中的启动与运行期要求转换成公共注册策略。
537    ///
538    /// 参数说明:无。
539    ///
540    /// 返回:两个必需性维度保持独立。
541    pub fn policy(&self) -> RegistrationPolicy {
542        RegistrationPolicy {
543            startup_required: self.startup_required,
544            readiness_critical: self.readiness_critical,
545            monitor_interval_ms: self.monitor_interval_ms,
546            observation_grace_ms: self.observation_grace_ms,
547            deregister_timeout_ms: self.deregister_timeout_ms,
548        }
549    }
550}
551
552impl ConnectionConfig {
553    /// 业务作用:在网络准备之前核对宿主引用是否属于该连接的显式服务目录。
554    ///
555    /// 参数说明:
556    /// - `service`:是本地服务别名,不能替代后端服务名或跨连接查询。
557    ///
558    /// 返回:静态、DNS 或已配置 provider 服务表包含该精确别名时为 true。
559    pub fn has_service(&self, service: &str) -> bool {
560        match self.provider.as_str() {
561            "static" => self
562                .static_config
563                .as_ref()
564                .is_some_and(|config| config.services.contains_key(service)),
565            "dns" => self
566                .dns
567                .as_ref()
568                .is_some_and(|config| config.services.contains_key(service)),
569            "nacos" | "consul" | "kubernetes" => {
570                let value = match self.provider.as_str() {
571                    "nacos" => &self.nacos,
572                    "consul" => &self.consul,
573                    _ => &self.kubernetes,
574                };
575                value
576                    .as_ref()
577                    .and_then(|value| value.get("services"))
578                    .and_then(serde_json::Value::as_object)
579                    .is_some_and(|services| services.contains_key(service))
580            }
581            _ => false,
582        }
583    }
584
585    /// 业务作用:把公开配置转换为 owner 的冻结时效预算。
586    ///
587    /// 参数说明:无。
588    ///
589    /// 返回:不依赖具体传输默认值的公共策略。
590    pub fn policy(&self) -> ConnectionPolicy {
591        ConnectionPolicy {
592            startup_required: self.startup_required,
593            request_timeout_ms: self.request_timeout_ms,
594            poll_interval_ms: self.poll_interval_ms,
595            max_age_ms: self.snapshot.max_age_ms,
596            stale_if_error_ms: self.snapshot.stale_if_error_ms,
597            idle_timeout_ms: self.idle_timeout_ms,
598            initial_backoff_ms: self.retry.initial_backoff_ms,
599            max_backoff_ms: self.retry.max_backoff_ms,
600        }
601    }
602
603    /// 业务作用:拒绝同时填写多个 provider 块或未编入的运行时能力。
604    ///
605    /// 参数说明:无。
606    ///
607    /// 返回:恰好一个匹配 provider 块通过。
608    pub fn validate(&self) -> Result<()> {
609        self.policy().validate()?;
610        if let Some(rest) = &self.rest {
611            rest.validate()?;
612        }
613        let blocks = [
614            self.nacos.is_some(),
615            self.consul.is_some(),
616            self.kubernetes.is_some(),
617            self.static_config.is_some(),
618            self.dns.is_some(),
619        ];
620        if blocks.into_iter().filter(|present| *present).count() != 1 {
621            return Err(invalid("one provider configuration block is required"));
622        }
623        let supported = match self.provider.as_str() {
624            "static" => self.static_config.is_some(),
625            "dns" => self.dns.is_some(),
626            "nacos" => self.nacos.is_some() && cfg!(feature = "nacos"),
627            "consul" => self.consul.is_some() && cfg!(feature = "consul"),
628            "kubernetes" => self.kubernetes.is_some() && cfg!(feature = "kubernetes"),
629            _ => false,
630        };
631        if !supported {
632            return Err(
633                invalid("provider configuration or compiled feature is unavailable")
634                    .at_config_path("provider"),
635            );
636        }
637        // 全部 provider 的配置先通过类型检查,再允许 build 读取任何认证材料。
638        if let Some(config) = &self.static_config {
639            LocalProvider::static_services(config.services.clone())?;
640        }
641        if let Some(config) = &self.dns {
642            LocalProvider::dns_services(
643                config
644                    .services
645                    .iter()
646                    .map(|(name, target)| {
647                        (
648                            name.clone(),
649                            DnsService {
650                                host: target.host.clone(),
651                                port: target.port,
652                            },
653                        )
654                    })
655                    .collect(),
656            )?;
657        }
658        #[cfg(feature = "nacos")]
659        if let Some(value) = &self.nacos {
660            let mut config: nanacos::managed::ManagedNacosConfig =
661                serde_json::from_value(value.clone())
662                    .map_err(|_| invalid("invalid Nacos provider configuration"))?;
663            config.request_timeout_ms = self.request_timeout_ms;
664            nanacos::managed::ManagedNacos::new(config)?;
665        }
666        #[cfg(feature = "consul")]
667        if let Some(value) = &self.consul {
668            let mut config: nadisc_consul::ConsulConfig = serde_json::from_value(value.clone())
669                .map_err(|_| invalid("invalid Consul provider configuration"))?;
670            config.request_timeout_ms = self.request_timeout_ms;
671            nadisc_consul::ConsulProvider::new(config)?;
672        }
673        #[cfg(feature = "kubernetes")]
674        if let Some(value) = &self.kubernetes {
675            let mut config: nadisc_kubernetes::KubernetesConfig =
676                serde_json::from_value(value.clone())
677                    .map_err(|_| invalid("invalid Kubernetes provider configuration"))?;
678            config.request_timeout_ms = self.request_timeout_ms;
679            config.validate()?;
680        }
681        Ok(())
682    }
683
684    /// 业务作用:构造只属于当前连接的后端,不复用其他账号会话。
685    ///
686    /// 参数说明:无。
687    ///
688    /// 返回:中性 provider;认证文件读取受其 adapter 自身有限预算约束。
689    async fn build(&self) -> Result<Arc<dyn ManagedProvider>> {
690        self.validate()?;
691        match self.provider.as_str() {
692            "static" => Ok(Arc::new(LocalProvider::static_services(
693                self.static_config.as_ref().unwrap().services.clone(),
694            )?)),
695            "dns" => Ok(Arc::new(LocalProvider::dns_services(
696                self.dns
697                    .as_ref()
698                    .unwrap()
699                    .services
700                    .iter()
701                    .map(|(name, target)| {
702                        (
703                            name.clone(),
704                            DnsService {
705                                host: target.host.clone(),
706                                port: target.port,
707                            },
708                        )
709                    })
710                    .collect(),
711            )?)),
712            #[cfg(feature = "nacos")]
713            "nacos" => {
714                let mut config: nanacos::managed::ManagedNacosConfig =
715                    serde_json::from_value(self.nacos.clone().unwrap())
716                        .map_err(|_| invalid("invalid Nacos provider configuration"))?;
717                config.request_timeout_ms = self.request_timeout_ms;
718                Ok(Arc::new(nanacos::managed::ManagedNacos::new(config)?))
719            }
720            #[cfg(feature = "consul")]
721            "consul" => {
722                let mut config: nadisc_consul::ConsulConfig =
723                    serde_json::from_value(self.consul.clone().unwrap())
724                        .map_err(|_| invalid("invalid Consul provider configuration"))?;
725                config.request_timeout_ms = self.request_timeout_ms;
726                Ok(Arc::new(nadisc_consul::ConsulProvider::new(config)?))
727            }
728            #[cfg(feature = "kubernetes")]
729            "kubernetes" => {
730                let mut config: nadisc_kubernetes::KubernetesConfig =
731                    serde_json::from_value(self.kubernetes.clone().unwrap())
732                        .map_err(|_| invalid("invalid Kubernetes provider configuration"))?;
733                config.request_timeout_ms = self.request_timeout_ms;
734                Ok(Arc::new(
735                    nadisc_kubernetes::KubernetesProvider::new(config).await?,
736                ))
737            }
738            _ => Err(invalid("provider is unavailable")),
739        }
740    }
741}
742
743/// 业务作用:保存按需编入的后端配置前递归拒绝重复键,防止秘密或作用域字段被覆盖。
744///
745/// 参数说明:
746/// - `deserializer`:必须来自尚未折叠重复项的原始输入。
747///
748/// 返回:完整唯一键值树;重复项错误不包含原始值。
749fn optional_unique_value<'de, D: serde::Deserializer<'de>>(
750    deserializer: D,
751) -> std::result::Result<Option<serde_json::Value>, D::Error> {
752    Option::<UniqueValue>::deserialize(deserializer).map(|value| value.map(|value| value.0))
753}
754
755struct UniqueValue(serde_json::Value);
756impl<'de> Deserialize<'de> for UniqueValue {
757    /// 业务作用:在容纳后端扩展字段时保留原始重复键校验。
758    ///
759    /// 参数说明:
760    /// - `deserializer`:提供原始序列化事件。
761    ///
762    /// 返回:完整 JSON 兼容树;不接受非有限数值和重复字段。
763    fn deserialize<D: serde::Deserializer<'de>>(
764        deserializer: D,
765    ) -> std::result::Result<Self, D::Error> {
766        struct Unique;
767        impl<'de> Visitor<'de> for Unique {
768            type Value = UniqueValue;
769            /// 业务作用:返回不泄露配置内容的输入类型诊断。
770            ///
771            /// 参数说明:
772            /// - `formatter`:为诊断目标。
773            ///
774            /// 返回:固定类型描述。
775            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
776                formatter.write_str("a configuration value with unique fields")
777            }
778            /// 业务作用:校验映射每个字段只出现一次。
779            ///
780            /// 参数说明:
781            /// - `map`:是未折叠输入。
782            ///
783            /// 返回:唯一字段对象,重复输入整体失败。
784            fn visit_map<A: MapAccess<'de>>(
785                self,
786                mut map: A,
787            ) -> std::result::Result<Self::Value, A::Error> {
788                let mut values = serde_json::Map::new();
789                while let Some((key, value)) = map.next_entry::<String, UniqueValue>()? {
790                    if values.insert(key, value.0).is_some() {
791                        return Err(A::Error::custom("duplicate discovery field"));
792                    }
793                }
794                Ok(UniqueValue(serde_json::Value::Object(values)))
795            }
796            /// 业务作用:对配置数组中的每个对象继续执行字段唯一性检查。
797            ///
798            /// 参数说明:
799            /// - `seq`:是输入数组。
800            ///
801            /// 返回:完整数组,不截断元素。
802            fn visit_seq<A: serde::de::SeqAccess<'de>>(
803                self,
804                mut seq: A,
805            ) -> std::result::Result<Self::Value, A::Error> {
806                let mut values = Vec::new();
807                while let Some(value) = seq.next_element::<UniqueValue>()? {
808                    values.push(value.0);
809                }
810                Ok(UniqueValue(serde_json::Value::Array(values)))
811            }
812            /// 业务作用:保留文本配置的原始内容供后续 provider 校验。
813            ///
814            /// 参数说明:
815            /// - `value`:是字符串值。
816            ///
817            /// 返回:拥有内容的 JSON 字符串,不输出诊断日志。
818            fn visit_str<E: Error>(self, value: &str) -> std::result::Result<Self::Value, E> {
819                Ok(UniqueValue(value.into()))
820            }
821            /// 业务作用:保留布尔配置,不转换为字符串。
822            ///
823            /// 参数说明:
824            /// - `value`:是布尔值。
825            ///
826            /// 返回:对应 JSON 布尔值。
827            fn visit_bool<E: Error>(self, value: bool) -> std::result::Result<Self::Value, E> {
828                Ok(UniqueValue(value.into()))
829            }
830            /// 业务作用:保留无符号配置数值的范围。
831            ///
832            /// 参数说明:
833            /// - `value`:是无符号整数。
834            ///
835            /// 返回:对应 JSON 数字。
836            fn visit_u64<E: Error>(self, value: u64) -> std::result::Result<Self::Value, E> {
837                Ok(UniqueValue(value.into()))
838            }
839            /// 业务作用:保留有符号配置供后续范围校验。
840            ///
841            /// 参数说明:
842            /// - `value`:是有符号整数。
843            ///
844            /// 返回:对应 JSON 数字。
845            fn visit_i64<E: Error>(self, value: i64) -> std::result::Result<Self::Value, E> {
846                Ok(UniqueValue(value.into()))
847            }
848            /// 业务作用:拒绝不能表达确定权重或预算的非有限数值。
849            ///
850            /// 参数说明:
851            /// - `value`:是浮点输入。
852            ///
853            /// 返回:有限 JSON 数字或配置错误。
854            fn visit_f64<E: Error>(self, value: f64) -> std::result::Result<Self::Value, E> {
855                serde_json::Number::from_f64(value)
856                    .map(|value| UniqueValue(value.into()))
857                    .ok_or_else(|| E::custom("non-finite discovery value"))
858            }
859            /// 业务作用:保留显式空值以供类型化字段判断是否允许缺省。
860            ///
861            /// 参数说明:无。
862            ///
863            /// 返回:JSON 空值。
864            fn visit_unit<E: Error>(self) -> std::result::Result<Self::Value, E> {
865                Ok(UniqueValue(serde_json::Value::Null))
866            }
867        }
868        deserializer.deserialize_any(Unique)
869    }
870}
871
872/// 业务作用:在映射落地前拒绝重复名称,避免反序列化覆盖掩盖含混连接和注册。
873///
874/// 参数说明:
875/// - `deserializer`:提供原始映射事件,不接受预先丢失重复项的输入作为证明。
876///
877/// 返回:唯一键映射;错误不输出可能包含秘密的值。
878fn unique_map<'de, D, T>(deserializer: D) -> std::result::Result<BTreeMap<String, T>, D::Error>
879where
880    D: serde::Deserializer<'de>,
881    T: Deserialize<'de>,
882{
883    struct Unique<T>(std::marker::PhantomData<T>);
884    impl<'de, T: Deserialize<'de>> Visitor<'de> for Unique<T> {
885        type Value = BTreeMap<String, T>;
886        /// 业务作用:报告固定的映射类型要求。
887        ///
888        /// 参数说明:
889        /// - `formatter`:接收无配置值的类型描述。
890        ///
891        /// 返回:安全的反序列化诊断。
892        fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
893            formatter.write_str("a map with unique names")
894        }
895        /// 业务作用:逐项检查名称唯一性后才保存配置。
896        ///
897        /// 参数说明:
898        /// - `map`:是尚未合并的原始映射。
899        ///
900        /// 返回:完整唯一键集合或重复键错误。
901        fn visit_map<A: MapAccess<'de>>(
902            self,
903            mut map: A,
904        ) -> std::result::Result<Self::Value, A::Error> {
905            let mut values = BTreeMap::new();
906            while let Some((key, value)) = map.next_entry::<String, T>()? {
907                if values.insert(key, value).is_some() {
908                    return Err(A::Error::custom("duplicate discovery name"));
909                }
910            }
911            Ok(values)
912        }
913    }
914    deserializer.deserialize_map(Unique(std::marker::PhantomData))
915}