Skip to main content

zerodds_rpc/
evolution_rules.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! Service-evolution compatibility rules (Spec §7.7).
5//!
6//! Per mapping (basic + enhanced), the spec defines which
7//! service evolutions (add/remove/reorder operation, change
8//! signature, etc.) are backward-compatible. This module encodes
9//! these rules as const tables and provides helper functions that
10//! a codegen/audit tool checks against evolved service definitions.
11
12extern crate alloc;
13
14use alloc::vec::Vec;
15
16/// Service-Evolution-Operation.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum Evolution {
19    /// Spec §7.7.1.1 / §7.7.2.1.
20    AddOperation,
21    /// Spec §7.7.1.1 / §7.7.2.2.
22    RemoveOperation,
23    /// Spec §7.7.1.2 / §7.7.2.3.
24    ReorderOperations,
25    /// Spec §7.7.1.2.
26    ReorderBaseInterfaces,
27    /// Spec §7.7.1.3 — operation signature changes.
28    ChangeSignature,
29    /// Spec §7.7.2.4 — Duck-Typing.
30    DuckTyping,
31    /// Spec §7.7.2.5.1.
32    AddRemoveParameter,
33    /// Spec §7.7.2.5.2.
34    ReorderParameters,
35    /// Spec §7.7.2.5.3.
36    ChangeParameterType,
37    /// Spec §7.7.2.5.4.
38    AddRemoveReturnType,
39    /// Spec §7.7.2.5.5.
40    ChangeReturnType,
41}
42
43/// Mapping-Profil.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum Mapping {
46    /// Basic-Mapping (Spec §7.7.1).
47    Basic,
48    /// Enhanced-Mapping (Spec §7.7.2).
49    Enhanced,
50}
51
52/// Spec §7.7: Compatibility-Tabelle pro (Mapping, Evolution).
53///
54/// Returns `true` if the evolution is backward-compatible under the
55/// given mapping (an old client can talk to a new service).
56#[must_use]
57pub fn is_compatible(mapping: Mapping, evolution: Evolution) -> bool {
58    match (mapping, evolution) {
59        // Basic mapping: every structural change is breaking
60        // (Spec §7.7.1.x).
61        (Mapping::Basic, _) => false,
62
63        // Enhanced mapping (Spec §7.7.2.x): most
64        // structural changes are compat-by-default.
65        (Mapping::Enhanced, Evolution::AddOperation) => true,
66        (Mapping::Enhanced, Evolution::RemoveOperation) => true, // with a caveat (§7.7.2.2)
67        (Mapping::Enhanced, Evolution::ReorderOperations) => true,
68        (Mapping::Enhanced, Evolution::ReorderBaseInterfaces) => true,
69        (Mapping::Enhanced, Evolution::DuckTyping) => true,
70        (Mapping::Enhanced, Evolution::AddRemoveParameter) => true,
71        (Mapping::Enhanced, Evolution::ReorderParameters) => true,
72        (Mapping::Enhanced, Evolution::AddRemoveReturnType) => true,
73
74        // A change of type (even in enhanced) is breaking.
75        (Mapping::Enhanced, Evolution::ChangeSignature) => false,
76        (Mapping::Enhanced, Evolution::ChangeParameterType) => false,
77        (Mapping::Enhanced, Evolution::ChangeReturnType) => false,
78    }
79}
80
81/// Returns all evolutions that are **compatible** under the given
82/// mapping.
83#[must_use]
84pub fn compatible_evolutions(mapping: Mapping) -> Vec<Evolution> {
85    [
86        Evolution::AddOperation,
87        Evolution::RemoveOperation,
88        Evolution::ReorderOperations,
89        Evolution::ReorderBaseInterfaces,
90        Evolution::ChangeSignature,
91        Evolution::DuckTyping,
92        Evolution::AddRemoveParameter,
93        Evolution::ReorderParameters,
94        Evolution::ChangeParameterType,
95        Evolution::AddRemoveReturnType,
96        Evolution::ChangeReturnType,
97    ]
98    .iter()
99    .copied()
100    .filter(|e| is_compatible(mapping, *e))
101    .collect()
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    // ---- Spec §7.7.1.1-3 basic mapping (all breaking) -------------
109
110    #[test]
111    fn basic_mapping_add_remove_operation_is_breaking() {
112        // Spec §7.7.1.1.
113        assert!(!is_compatible(Mapping::Basic, Evolution::AddOperation));
114        assert!(!is_compatible(Mapping::Basic, Evolution::RemoveOperation));
115    }
116
117    #[test]
118    fn basic_mapping_reorder_operations_is_breaking() {
119        // Spec §7.7.1.2.
120        assert!(!is_compatible(Mapping::Basic, Evolution::ReorderOperations));
121        assert!(!is_compatible(
122            Mapping::Basic,
123            Evolution::ReorderBaseInterfaces
124        ));
125    }
126
127    #[test]
128    fn basic_mapping_change_signature_is_breaking() {
129        // Spec §7.7.1.3.
130        assert!(!is_compatible(Mapping::Basic, Evolution::ChangeSignature));
131    }
132
133    #[test]
134    fn basic_mapping_has_no_compatible_evolutions() {
135        assert!(compatible_evolutions(Mapping::Basic).is_empty());
136    }
137
138    // ---- Spec §7.7.2.1-5 Enhanced-Mapping (compat by default) -----
139
140    #[test]
141    fn enhanced_mapping_add_operation_is_compatible() {
142        // Spec §7.7.2.1.
143        assert!(is_compatible(Mapping::Enhanced, Evolution::AddOperation));
144    }
145
146    #[test]
147    fn enhanced_mapping_remove_operation_is_compatible_with_caveat() {
148        // Spec §7.7.2.2.
149        assert!(is_compatible(Mapping::Enhanced, Evolution::RemoveOperation));
150    }
151
152    #[test]
153    fn enhanced_mapping_reorder_operations_is_compatible() {
154        // Spec §7.7.2.3.
155        assert!(is_compatible(
156            Mapping::Enhanced,
157            Evolution::ReorderOperations
158        ));
159        assert!(is_compatible(
160            Mapping::Enhanced,
161            Evolution::ReorderBaseInterfaces
162        ));
163    }
164
165    #[test]
166    fn enhanced_mapping_duck_typing_is_compatible() {
167        // Spec §7.7.2.4.
168        assert!(is_compatible(Mapping::Enhanced, Evolution::DuckTyping));
169    }
170
171    #[test]
172    fn enhanced_mapping_add_remove_param_is_compatible() {
173        // Spec §7.7.2.5.1.
174        assert!(is_compatible(
175            Mapping::Enhanced,
176            Evolution::AddRemoveParameter
177        ));
178    }
179
180    #[test]
181    fn enhanced_mapping_reorder_params_is_compatible() {
182        // Spec §7.7.2.5.2.
183        assert!(is_compatible(
184            Mapping::Enhanced,
185            Evolution::ReorderParameters
186        ));
187    }
188
189    #[test]
190    fn enhanced_mapping_change_param_type_is_breaking() {
191        // Spec §7.7.2.5.3 — type changes are breaking even in enhanced,
192        // because the wire format is not compatible.
193        assert!(!is_compatible(
194            Mapping::Enhanced,
195            Evolution::ChangeParameterType
196        ));
197    }
198
199    #[test]
200    fn enhanced_mapping_add_remove_return_type_is_compatible() {
201        // Spec §7.7.2.5.4.
202        assert!(is_compatible(
203            Mapping::Enhanced,
204            Evolution::AddRemoveReturnType
205        ));
206    }
207
208    #[test]
209    fn enhanced_mapping_change_return_type_is_breaking() {
210        // Spec §7.7.2.5.5.
211        assert!(!is_compatible(
212            Mapping::Enhanced,
213            Evolution::ChangeReturnType
214        ));
215    }
216
217    #[test]
218    fn enhanced_compatible_evolutions_includes_8_of_11() {
219        let compats = compatible_evolutions(Mapping::Enhanced);
220        // Add/Remove/Reorder-Op + Reorder-Bases + DuckTyping +
221        // Add/Remove-Param + Reorder-Params + Add/Remove-Return = 8.
222        assert_eq!(compats.len(), 8);
223    }
224}