Skip to main content

emboss_runtime/
typestate.rs

1// Copyright 2026 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Compile-time typestate markers and traits for Emboss views and writers.
16
17/// Unchecked view or field state. Requires runtime-checked `try_` accessors.
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
19pub struct UncheckedState;
20
21/// Complete view state. All fields in the current layout fit within storage.
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
23pub struct CompleteState;
24
25/// Ok view state. All fields in the current layout fit within storage and are valid.
26#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
27pub struct OkState;
28
29/// Alias for `OkState`.
30pub type Ok = OkState;
31
32/// Typestate transition trait for layout and validation mutations.
33pub trait State {
34    type OnLayoutMutation: State;
35    type OnValidMutation: State;
36}
37
38impl State for UncheckedState {
39    type OnLayoutMutation = UncheckedState;
40    type OnValidMutation = UncheckedState;
41}
42
43impl State for CompleteState {
44    type OnLayoutMutation = UncheckedState;
45    type OnValidMutation = CompleteState;
46}
47
48impl State for OkState {
49    type OnLayoutMutation = UncheckedState;
50    type OnValidMutation = CompleteState;
51}
52
53/// Marker trait for states that guarantee full layout completeness.
54pub trait IsComplete: State {}
55impl IsComplete for CompleteState {}
56impl IsComplete for OkState {}
57
58/// Marker trait for states that guarantee layout completeness and validity.
59pub trait IsOk: IsComplete {}
60impl IsOk for OkState {}
61
62/// Infallible read operation for views in a verified typestate (`IsOk`).
63pub trait InfallibleRead {
64    type ReadValue;
65    fn read(&self) -> Self::ReadValue;
66}
67
68/// Infallible write operation for views in a verified typestate (`IsComplete`), consuming self and transitioning to the mutated state.
69pub trait InfallibleWrite {
70    type WriteValue;
71    type Output;
72    fn write(self, val: Self::WriteValue) -> Self::Output;
73}
74
75/// Trait for verifying dynamic layout completeness at runtime, upgrading the typestate to `CompleteState`.
76pub trait CheckComplete {
77    type Completed;
78    fn check_complete(self) -> Result<Self::Completed, crate::Error>;
79}
80
81/// Trait for verifying validity at runtime, upgrading the typestate to `OkState`.
82pub trait CheckOk {
83    type Ok;
84    fn check_ok(self) -> Result<Self::Ok, crate::Error>;
85}
86
87/// Trait for converting a mutable view or storage into a self-consuming typestate Writer.
88pub trait IntoWriter {
89    type Writer;
90    fn into_writer(self) -> Self::Writer;
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::{LittleEndian, Storage, UInt};
97
98    const TEST_VALUE: u32 = 0x12345678;
99
100    fn assert_state<S: State>() {}
101    fn assert_complete<S: IsComplete>() {}
102    fn assert_ok<S: IsOk>() {}
103
104    #[test]
105    fn test_state_marker_traits() {
106        assert_state::<UncheckedState>();
107        assert_state::<CompleteState>();
108        assert_state::<OkState>();
109
110        assert_complete::<CompleteState>();
111        assert_complete::<OkState>();
112
113        assert_ok::<OkState>();
114    }
115
116    #[test]
117    fn test_on_layout_mutation_transitions() {
118        fn assert_is_unchecked<S: Storage>(_view: &UInt<32, LittleEndian, S, UncheckedState>) {}
119
120        let mut buf = [0u8; 4];
121        let ok_view =
122            UInt::<32, LittleEndian, &mut [u8; 4], OkState>::new(&mut buf);
123        assert_eq!(ok_view.read(), 0);
124
125        // Performing a write consumes the OkState view and degrades its state to UncheckedState.
126        let mutated_view = ok_view.write(TEST_VALUE);
127        assert_is_unchecked(&mutated_view);
128
129        // The degraded view in UncheckedState must use checked reading.
130        assert_eq!(mutated_view.try_read().expect("valid read"), TEST_VALUE);
131    }
132
133    #[test]
134    fn test_infallible_uint_read_write() {
135        let mut buf = [0u8; 4];
136        let uint_view = UInt::<32, LittleEndian, &mut [u8; 4], OkState>::new(&mut buf);
137        let _ = uint_view.write(TEST_VALUE);
138        let read_view = UInt::<32, LittleEndian, &[u8; 4], OkState>::new(&buf);
139        assert_eq!(read_view.read(), TEST_VALUE);
140    }
141
142    #[test]
143    fn test_into_writer_conversion() {
144        struct MockMut<S>(S);
145        struct MockWriter<S, ST: State>(S, core::marker::PhantomData<ST>);
146
147        impl<S> IntoWriter for MockMut<S> {
148            type Writer = MockWriter<S, UncheckedState>;
149            fn into_writer(self) -> Self::Writer {
150                MockWriter(self.0, core::marker::PhantomData)
151            }
152        }
153
154        impl<S> CheckComplete for MockWriter<S, UncheckedState> {
155            type Completed = MockWriter<S, CompleteState>;
156            fn check_complete(self) -> Result<Self::Completed, crate::Error> {
157                Ok(MockWriter(self.0, core::marker::PhantomData))
158            }
159        }
160
161        impl<S> CheckOk for MockWriter<S, UncheckedState> {
162            type Ok = MockWriter<S, OkState>;
163            fn check_ok(self) -> Result<Self::Ok, crate::Error> {
164                Ok(MockWriter(self.0, core::marker::PhantomData))
165            }
166        }
167
168        let mut buf = [0u8; 4];
169        let mock_mut = MockMut(&mut buf);
170        let _complete_writer: MockWriter<&mut [u8; 4], CompleteState> =
171            mock_mut.into_writer().check_complete().expect("complete writer");
172
173        let mock_mut2 = MockMut(&mut buf);
174        let _ok_writer: MockWriter<&mut [u8; 4], OkState> =
175            mock_mut2.into_writer().check_ok().expect("ok writer");
176    }
177}