1use crate::FieldId;
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct FixedPoint {
12 pub min_q: i64,
14 pub max_q: i64,
16 pub scale: u32,
18}
19
20impl FixedPoint {
21 #[must_use]
23 pub const fn new(min_q: i64, max_q: i64, scale: u32) -> Self {
24 Self {
25 min_q,
26 max_q,
27 scale,
28 }
29 }
30}
31
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum FieldCodec {
36 Bool,
38
39 UInt { bits: u8 },
41
42 SInt { bits: u8 },
44
45 VarUInt,
47
48 VarSInt,
50
51 FixedPoint(FixedPoint),
53}
54
55impl FieldCodec {
56 #[must_use]
58 pub const fn bool() -> Self {
59 Self::Bool
60 }
61
62 #[must_use]
64 pub const fn uint(bits: u8) -> Self {
65 Self::UInt { bits }
66 }
67
68 #[must_use]
70 pub const fn sint(bits: u8) -> Self {
71 Self::SInt { bits }
72 }
73
74 #[must_use]
76 pub const fn var_uint() -> Self {
77 Self::VarUInt
78 }
79
80 #[must_use]
82 pub const fn var_sint() -> Self {
83 Self::VarSInt
84 }
85
86 #[must_use]
88 pub const fn fixed_point(min_q: i64, max_q: i64, scale: u32) -> Self {
89 Self::FixedPoint(FixedPoint::new(min_q, max_q, scale))
90 }
91}
92
93#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum ChangePolicy {
97 Always,
99 Threshold { threshold_q: u32 },
101}
102
103#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub struct FieldDef {
107 pub id: FieldId,
108 pub codec: FieldCodec,
109 pub change: ChangePolicy,
110}
111
112impl FieldDef {
113 #[must_use]
115 pub const fn new(id: FieldId, codec: FieldCodec) -> Self {
116 Self {
117 id,
118 codec,
119 change: ChangePolicy::Always,
120 }
121 }
122
123 #[must_use]
125 pub const fn with_threshold(id: FieldId, codec: FieldCodec, threshold_q: u32) -> Self {
126 Self {
127 id,
128 codec,
129 change: ChangePolicy::Threshold { threshold_q },
130 }
131 }
132
133 #[must_use]
135 pub const fn change(mut self, change: ChangePolicy) -> Self {
136 self.change = change;
137 self
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::FieldId;
145
146 #[test]
147 fn fixed_point_construction() {
148 let fp = FixedPoint::new(-100, 200, 100);
149 assert_eq!(fp.min_q, -100);
150 assert_eq!(fp.max_q, 200);
151 assert_eq!(fp.scale, 100);
152 }
153
154 #[test]
155 fn field_codec_variants() {
156 assert!(matches!(FieldCodec::bool(), FieldCodec::Bool));
157 assert!(matches!(FieldCodec::uint(8), FieldCodec::UInt { bits: 8 }));
158 assert!(matches!(FieldCodec::sint(8), FieldCodec::SInt { bits: 8 }));
159 assert!(matches!(FieldCodec::var_uint(), FieldCodec::VarUInt));
160 assert!(matches!(FieldCodec::var_sint(), FieldCodec::VarSInt));
161 assert!(matches!(
162 FieldCodec::fixed_point(-10, 10, 100),
163 FieldCodec::FixedPoint(_)
164 ));
165 }
166
167 #[test]
168 fn field_def_default_change_policy() {
169 let id = FieldId::new(1).unwrap();
170 let field = FieldDef::new(id, FieldCodec::bool());
171 assert_eq!(field.change, ChangePolicy::Always);
172 }
173
174 #[test]
175 fn field_def_threshold_policy() {
176 let id = FieldId::new(2).unwrap();
177 let field = FieldDef::with_threshold(id, FieldCodec::uint(12), 5);
178 assert_eq!(field.change, ChangePolicy::Threshold { threshold_q: 5 });
179 }
180
181 #[test]
182 fn field_def_change_override() {
183 let id = FieldId::new(3).unwrap();
184 let field = FieldDef::new(id, FieldCodec::uint(8))
185 .change(ChangePolicy::Threshold { threshold_q: 2 });
186 assert_eq!(field.change, ChangePolicy::Threshold { threshold_q: 2 });
187 }
188}