diff --git a/crates/alknet-core/src/call/response.rs b/crates/alknet-core/src/call/response.rs index 405c47c..ab7143d 100644 --- a/crates/alknet-core/src/call/response.rs +++ b/crates/alknet-core/src/call/response.rs @@ -2,12 +2,23 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] pub struct CallError { pub code: String, pub message: String, pub retryable: bool, } +impl CallError { + pub fn new(code: impl Into, message: impl Into, retryable: bool) -> Self { + Self { + code: code.into(), + message: message.into(), + retryable, + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResponseEnvelope { pub request_id: String, diff --git a/crates/alknet-core/src/call/spec.rs b/crates/alknet-core/src/call/spec.rs index 17a0571..e1efaa8 100644 --- a/crates/alknet-core/src/call/spec.rs +++ b/crates/alknet-core/src/call/spec.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] pub enum OperationType { Query, Mutation, diff --git a/crates/alknet-core/src/config/dynamic_config.rs b/crates/alknet-core/src/config/dynamic_config.rs index e1e85c0..ca25160 100644 --- a/crates/alknet-core/src/config/dynamic_config.rs +++ b/crates/alknet-core/src/config/dynamic_config.rs @@ -229,6 +229,7 @@ impl Default for RateLimitConfig { } #[derive(Debug, Clone)] +#[non_exhaustive] pub struct DynamicConfig { pub auth: AuthPolicy, 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 { self.forwarding = policy; self diff --git a/crates/alknet-core/src/config/forwarding.rs b/crates/alknet-core/src/config/forwarding.rs index 43a34fd..927040a 100644 --- a/crates/alknet-core/src/config/forwarding.rs +++ b/crates/alknet-core/src/config/forwarding.rs @@ -8,12 +8,14 @@ use crate::auth::identity::Identity; use crate::transport::TransportKind; #[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] pub enum ForwardingAction { Allow, Deny, } #[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] pub enum TargetPattern { Any, Host(String), @@ -62,6 +64,7 @@ fn match_cidr(network: &IpNetwork, target: &str) -> bool { } #[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] pub struct ForwardingRule { pub target: TargetPattern, pub action: ForwardingAction, @@ -69,6 +72,22 @@ pub struct ForwardingRule { pub transports: Vec, } +impl ForwardingRule { + pub fn new( + target: TargetPattern, + action: ForwardingAction, + principals: Vec, + transports: Vec, + ) -> Self { + Self { + target, + action, + principals, + transports, + } + } +} + impl ForwardingRule { fn matches_principal(&self, identity: &Identity) -> bool { if self.principals.is_empty() { diff --git a/crates/alknet-core/src/interface/config.rs b/crates/alknet-core/src/interface/config.rs index 7435ca4..9e8cf09 100644 --- a/crates/alknet-core/src/interface/config.rs +++ b/crates/alknet-core/src/interface/config.rs @@ -7,6 +7,7 @@ use crate::auth::IdentityProvider; use crate::config::DynamicConfig; #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] pub enum InterfaceKind { Ssh, RawFraming, @@ -21,6 +22,7 @@ impl std::fmt::Display for InterfaceKind { } } +#[non_exhaustive] pub enum InterfaceConfig { Ssh(SshInterfaceConfig), RawFraming(RawFramingConfig), @@ -28,9 +30,11 @@ pub enum InterfaceConfig { impl InterfaceConfig { pub fn kind(&self) -> InterfaceKind { + #[allow(unreachable_patterns)] match self { InterfaceConfig::Ssh(_) => InterfaceKind::Ssh, InterfaceConfig::RawFraming(_) => InterfaceKind::RawFraming, + _ => InterfaceKind::Ssh, } } } diff --git a/crates/alknet-napi/src/serve.rs b/crates/alknet-napi/src/serve.rs index a18a970..59411eb 100644 --- a/crates/alknet-napi/src/serve.rs +++ b/crates/alknet-napi/src/serve.rs @@ -149,21 +149,7 @@ fn build_forwarding_policy(config: &ForwardingPolicyConfig) -> napi::Result napi::Result<()> { let new_auth_policy = build_auth_policy_from_napi(&auth)?; let current = self.reload_handle.dynamic(); - let new_config = DynamicConfig { - auth: new_auth_policy, - forwarding: current.forwarding.clone(), - rate_limits: current.rate_limits.clone(), - }; + let new_config = DynamicConfig::from_parts( + new_auth_policy, + current.forwarding.clone(), + current.rate_limits.clone(), + ); self.reload_handle.reload(new_config); Ok(()) } @@ -660,11 +646,11 @@ impl AlknetServer { pub fn reload_forwarding(&self, policy: ForwardingPolicyConfig) -> napi::Result<()> { let new_forwarding = build_forwarding_policy(&policy)?; let current = self.reload_handle.dynamic(); - let new_config = DynamicConfig { - auth: current.auth.clone(), - forwarding: new_forwarding, - rate_limits: current.rate_limits.clone(), - }; + let new_config = DynamicConfig::from_parts( + current.auth.clone(), + new_forwarding, + current.rate_limits.clone(), + ); self.reload_handle.reload(new_config); Ok(()) } @@ -678,11 +664,11 @@ impl AlknetServer { let new_auth_policy = build_auth_policy_from_napi(&auth)?; let new_forwarding = build_forwarding_policy(&forwarding)?; let current = self.reload_handle.dynamic(); - let new_config = DynamicConfig { - auth: new_auth_policy, - forwarding: new_forwarding, - rate_limits: current.rate_limits.clone(), - }; + let new_config = DynamicConfig::from_parts( + new_auth_policy, + new_forwarding, + current.rate_limits.clone(), + ); self.reload_handle.reload(new_config); Ok(()) } @@ -755,11 +741,11 @@ pub async fn serve(options: AlknetServeOptions) -> napi::Result { { let current = reload_handle.dynamic(); - let initialized_config = DynamicConfig { - auth: initial_auth_policy, - forwarding: current.forwarding.clone(), - rate_limits: current.rate_limits.clone(), - }; + let initialized_config = DynamicConfig::from_parts( + initial_auth_policy, + current.forwarding.clone(), + current.rate_limits.clone(), + ); drop(current); reload_handle.reload(initialized_config); } @@ -1370,11 +1356,11 @@ mod tests { let initial = arc_swap.load(); assert_eq!(initial.forwarding.default, ForwardingAction::Allow); - let new_config = DynamicConfig { - auth: AuthPolicy::empty(), - forwarding: ForwardingPolicy::deny_all(), - rate_limits: RateLimitConfig::default(), - }; + let new_config = DynamicConfig::from_parts( + AuthPolicy::empty(), + ForwardingPolicy::deny_all(), + RateLimitConfig::default(), + ); handle.reload(new_config); let updated = arc_swap.load(); diff --git a/tasks/cleanup/non-exhaustive-public-api.md b/tasks/cleanup/non-exhaustive-public-api.md index 6609494..f140353 100644 --- a/tasks/cleanup/non-exhaustive-public-api.md +++ b/tasks/cleanup/non-exhaustive-public-api.md @@ -1,7 +1,7 @@ --- id: cleanup/non-exhaustive-public-api name: Add #[non_exhaustive] to public API enums and structs likely to evolve -status: pending +status: completed depends_on: - review/phase1-core-modifications scope: narrow @@ -46,4 +46,4 @@ Several public API types introduced in Phase 1 are likely to gain variants or fi ## Summary -> To be filled on completion \ No newline at end of file +> 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. \ No newline at end of file