feat(api): add #[non_exhaustive] to public types likely to evolve

ForwardingAction, TargetPattern, ForwardingRule, OperationType,
InterfaceConfig, InterfaceKind, DynamicConfig, and CallError are all
likely to gain variants/fields in future phases. Marking them
#[non_exhaustive] now prevents downstream breakage when new
variants/fields are added. Added constructor methods for types that
are constructed from other crates.
This commit is contained in:
2026-06-08 05:34:15 +00:00
parent b0a885ea40
commit 619a6dcc77
7 changed files with 76 additions and 42 deletions

View File

@@ -2,12 +2,23 @@ use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CallError { pub struct CallError {
pub code: String, pub code: String,
pub message: String, pub message: String,
pub retryable: bool, pub retryable: bool,
} }
impl CallError {
pub fn new(code: impl Into<String>, message: impl Into<String>, retryable: bool) -> Self {
Self {
code: code.into(),
message: message.into(),
retryable,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseEnvelope { pub struct ResponseEnvelope {
pub request_id: String, pub request_id: String,

View File

@@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum OperationType { pub enum OperationType {
Query, Query,
Mutation, Mutation,

View File

@@ -229,6 +229,7 @@ impl Default for RateLimitConfig {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[non_exhaustive]
pub struct DynamicConfig { pub struct DynamicConfig {
pub auth: AuthPolicy, pub auth: AuthPolicy,
pub forwarding: ForwardingPolicy, pub forwarding: ForwardingPolicy,
@@ -244,6 +245,18 @@ impl DynamicConfig {
} }
} }
pub fn from_parts(
auth: AuthPolicy,
forwarding: ForwardingPolicy,
rate_limits: RateLimitConfig,
) -> Self {
Self {
auth,
forwarding,
rate_limits,
}
}
pub fn with_forwarding_policy(mut self, policy: ForwardingPolicy) -> Self { pub fn with_forwarding_policy(mut self, policy: ForwardingPolicy) -> Self {
self.forwarding = policy; self.forwarding = policy;
self self

View File

@@ -8,12 +8,14 @@ use crate::auth::identity::Identity;
use crate::transport::TransportKind; use crate::transport::TransportKind;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum ForwardingAction { pub enum ForwardingAction {
Allow, Allow,
Deny, Deny,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum TargetPattern { pub enum TargetPattern {
Any, Any,
Host(String), Host(String),
@@ -62,6 +64,7 @@ fn match_cidr(network: &IpNetwork, target: &str) -> bool {
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct ForwardingRule { pub struct ForwardingRule {
pub target: TargetPattern, pub target: TargetPattern,
pub action: ForwardingAction, pub action: ForwardingAction,
@@ -69,6 +72,22 @@ pub struct ForwardingRule {
pub transports: Vec<TransportKind>, pub transports: Vec<TransportKind>,
} }
impl ForwardingRule {
pub fn new(
target: TargetPattern,
action: ForwardingAction,
principals: Vec<String>,
transports: Vec<TransportKind>,
) -> Self {
Self {
target,
action,
principals,
transports,
}
}
}
impl ForwardingRule { impl ForwardingRule {
fn matches_principal(&self, identity: &Identity) -> bool { fn matches_principal(&self, identity: &Identity) -> bool {
if self.principals.is_empty() { if self.principals.is_empty() {

View File

@@ -7,6 +7,7 @@ use crate::auth::IdentityProvider;
use crate::config::DynamicConfig; use crate::config::DynamicConfig;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum InterfaceKind { pub enum InterfaceKind {
Ssh, Ssh,
RawFraming, RawFraming,
@@ -21,6 +22,7 @@ impl std::fmt::Display for InterfaceKind {
} }
} }
#[non_exhaustive]
pub enum InterfaceConfig { pub enum InterfaceConfig {
Ssh(SshInterfaceConfig), Ssh(SshInterfaceConfig),
RawFraming(RawFramingConfig), RawFraming(RawFramingConfig),
@@ -28,9 +30,11 @@ pub enum InterfaceConfig {
impl InterfaceConfig { impl InterfaceConfig {
pub fn kind(&self) -> InterfaceKind { pub fn kind(&self) -> InterfaceKind {
#[allow(unreachable_patterns)]
match self { match self {
InterfaceConfig::Ssh(_) => InterfaceKind::Ssh, InterfaceConfig::Ssh(_) => InterfaceKind::Ssh,
InterfaceConfig::RawFraming(_) => InterfaceKind::RawFraming, InterfaceConfig::RawFraming(_) => InterfaceKind::RawFraming,
_ => InterfaceKind::Ssh,
} }
} }
} }

View File

@@ -149,21 +149,7 @@ fn build_forwarding_policy(config: &ForwardingPolicyConfig) -> napi::Result<Forw
let target = parse_target_pattern(&rc.target)?; let target = parse_target_pattern(&rc.target)?;
let action = parse_forwarding_action(&rc.action)?; let action = parse_forwarding_action(&rc.action)?;
let principals = rc.principals.clone().unwrap_or_default(); let principals = rc.principals.clone().unwrap_or_default();
if principals.is_empty() { rules.push(ForwardingRule::new(target, action, principals, vec![]));
rules.push(ForwardingRule {
target,
action,
principals: vec![],
transports: vec![],
});
} else {
rules.push(ForwardingRule {
target,
action,
principals,
transports: vec![],
});
}
} }
} }
Ok(ForwardingPolicy { default, rules }) Ok(ForwardingPolicy { default, rules })
@@ -647,11 +633,11 @@ impl AlknetServer {
pub fn reload_auth(&self, auth: AuthConfigNapi) -> napi::Result<()> { pub fn reload_auth(&self, auth: AuthConfigNapi) -> napi::Result<()> {
let new_auth_policy = build_auth_policy_from_napi(&auth)?; let new_auth_policy = build_auth_policy_from_napi(&auth)?;
let current = self.reload_handle.dynamic(); let current = self.reload_handle.dynamic();
let new_config = DynamicConfig { let new_config = DynamicConfig::from_parts(
auth: new_auth_policy, new_auth_policy,
forwarding: current.forwarding.clone(), current.forwarding.clone(),
rate_limits: current.rate_limits.clone(), current.rate_limits.clone(),
}; );
self.reload_handle.reload(new_config); self.reload_handle.reload(new_config);
Ok(()) Ok(())
} }
@@ -660,11 +646,11 @@ impl AlknetServer {
pub fn reload_forwarding(&self, policy: ForwardingPolicyConfig) -> napi::Result<()> { pub fn reload_forwarding(&self, policy: ForwardingPolicyConfig) -> napi::Result<()> {
let new_forwarding = build_forwarding_policy(&policy)?; let new_forwarding = build_forwarding_policy(&policy)?;
let current = self.reload_handle.dynamic(); let current = self.reload_handle.dynamic();
let new_config = DynamicConfig { let new_config = DynamicConfig::from_parts(
auth: current.auth.clone(), current.auth.clone(),
forwarding: new_forwarding, new_forwarding,
rate_limits: current.rate_limits.clone(), current.rate_limits.clone(),
}; );
self.reload_handle.reload(new_config); self.reload_handle.reload(new_config);
Ok(()) Ok(())
} }
@@ -678,11 +664,11 @@ impl AlknetServer {
let new_auth_policy = build_auth_policy_from_napi(&auth)?; let new_auth_policy = build_auth_policy_from_napi(&auth)?;
let new_forwarding = build_forwarding_policy(&forwarding)?; let new_forwarding = build_forwarding_policy(&forwarding)?;
let current = self.reload_handle.dynamic(); let current = self.reload_handle.dynamic();
let new_config = DynamicConfig { let new_config = DynamicConfig::from_parts(
auth: new_auth_policy, new_auth_policy,
forwarding: new_forwarding, new_forwarding,
rate_limits: current.rate_limits.clone(), current.rate_limits.clone(),
}; );
self.reload_handle.reload(new_config); self.reload_handle.reload(new_config);
Ok(()) Ok(())
} }
@@ -755,11 +741,11 @@ pub async fn serve(options: AlknetServeOptions) -> napi::Result<AlknetServer> {
{ {
let current = reload_handle.dynamic(); let current = reload_handle.dynamic();
let initialized_config = DynamicConfig { let initialized_config = DynamicConfig::from_parts(
auth: initial_auth_policy, initial_auth_policy,
forwarding: current.forwarding.clone(), current.forwarding.clone(),
rate_limits: current.rate_limits.clone(), current.rate_limits.clone(),
}; );
drop(current); drop(current);
reload_handle.reload(initialized_config); reload_handle.reload(initialized_config);
} }
@@ -1370,11 +1356,11 @@ mod tests {
let initial = arc_swap.load(); let initial = arc_swap.load();
assert_eq!(initial.forwarding.default, ForwardingAction::Allow); assert_eq!(initial.forwarding.default, ForwardingAction::Allow);
let new_config = DynamicConfig { let new_config = DynamicConfig::from_parts(
auth: AuthPolicy::empty(), AuthPolicy::empty(),
forwarding: ForwardingPolicy::deny_all(), ForwardingPolicy::deny_all(),
rate_limits: RateLimitConfig::default(), RateLimitConfig::default(),
}; );
handle.reload(new_config); handle.reload(new_config);
let updated = arc_swap.load(); let updated = arc_swap.load();

View File

@@ -1,7 +1,7 @@
--- ---
id: cleanup/non-exhaustive-public-api id: cleanup/non-exhaustive-public-api
name: Add #[non_exhaustive] to public API enums and structs likely to evolve name: Add #[non_exhaustive] to public API enums and structs likely to evolve
status: pending status: completed
depends_on: depends_on:
- review/phase1-core-modifications - review/phase1-core-modifications
scope: narrow scope: narrow
@@ -46,4 +46,4 @@ Several public API types introduced in Phase 1 are likely to gain variants or fi
## Summary ## Summary
> To be filled on completion > Added #[non_exhaustive] to ForwardingAction, TargetPattern, ForwardingRule, OperationType, InterfaceConfig, InterfaceKind, DynamicConfig, and CallError. Added ForwardingRule::new(), DynamicConfig::from_parts(), and CallError::new() constructors so downstream crates can construct these types. Updated InterfaceConfig::kind() with wildcard arm (allow(unreachable_patterns)). TransportKind was not annotated as it already has tags-only variants and no exhaustive match statements were found.