Skip to main content

frame_isa/
modifier.rs

1//! Modifier flags for the SAM ISA
2//!
3//! Modifiers control the style and attributes of opcode output. Each modifier is
4//! a 2-byte bit-packed value with multiple fields.
5
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9/// Modifier flags (2 bytes)
10///
11/// Controls styling and attributes of opcode output. Bit layout:
12///
13/// ```text
14/// Bit:  15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
15///       [--VOICE--] [--TONE--] [-WARM-] [--FORMAT--] [ACCURACY] [URGENCY]
16/// ```
17///
18/// - **Voice** (bits 15-14): Speaking style - Neutral, Formal, Casual, Technical
19/// - **Tone** (bits 13-12): Emotional tone - Neutral, Positive, Empathetic, Cautious
20/// - **Warmth** (bits 11-10): Interpersonal warmth - Cold, Neutral, Warm, VeryWarm
21/// - **Format** (bits 9-8): Output format - Prose, Bulleted, Numbered, Structured
22/// - **Accuracy** (bits 7-6): Confidence level - Low, Medium, High, Verified
23/// - **Urgency** (bits 5-4): Priority level - Low, Normal, High, Critical
24/// - **Reserved** (bits 3-0): For future use
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
26#[repr(transparent)]
27pub struct Modifier(pub u16);
28
29impl Modifier {
30    // ========== Voice Styles (bits 15-14) ==========
31    pub const VOICE_NEUTRAL: Self = Self(0x0000);
32    pub const VOICE_FORMAL: Self = Self(0x4000);
33    pub const VOICE_CASUAL: Self = Self(0x8000);
34    pub const VOICE_TECHNICAL: Self = Self(0xC000);
35
36    // ========== Tone (bits 13-12) ==========
37    pub const TONE_NEUTRAL: Self = Self(0x0000);
38    pub const TONE_POSITIVE: Self = Self(0x1000);
39    pub const TONE_EMPATHETIC: Self = Self(0x2000);
40    pub const TONE_CAUTIOUS: Self = Self(0x3000);
41
42    // ========== Warmth (bits 11-10) ==========
43    pub const WARMTH_COLD: Self = Self(0x0000);
44    pub const WARMTH_NEUTRAL: Self = Self(0x0400);
45    pub const WARMTH_WARM: Self = Self(0x0800);
46    pub const WARMTH_VERY_WARM: Self = Self(0x0C00);
47
48    // ========== Format (bits 9-8) ==========
49    pub const FORMAT_PROSE: Self = Self(0x0000);
50    pub const FORMAT_BULLETED: Self = Self(0x0100);
51    pub const FORMAT_NUMBERED: Self = Self(0x0200);
52    pub const FORMAT_STRUCTURED: Self = Self(0x0300);
53
54    // ========== Accuracy (bits 7-6) ==========
55    pub const ACCURACY_LOW: Self = Self(0x0000);
56    pub const ACCURACY_MEDIUM: Self = Self(0x0040);
57    pub const ACCURACY_HIGH: Self = Self(0x0080);
58    pub const ACCURACY_VERIFIED: Self = Self(0x00C0);
59
60    // ========== Urgency (bits 5-4) ==========
61    pub const URGENCY_LOW: Self = Self(0x0000);
62    pub const URGENCY_NORMAL: Self = Self(0x0010);
63    pub const URGENCY_HIGH: Self = Self(0x0020);
64    pub const URGENCY_CRITICAL: Self = Self(0x0030);
65
66    // ========== Bit Masks ==========
67    const VOICE_MASK: u16 = 0xC000;
68    const TONE_MASK: u16 = 0x3000;
69    const WARMTH_MASK: u16 = 0x0C00;
70    const FORMAT_MASK: u16 = 0x0300;
71    const ACCURACY_MASK: u16 = 0x00C0;
72    const URGENCY_MASK: u16 = 0x0030;
73
74    /// Create from raw u16 value
75    #[inline]
76    pub const fn from_u16(value: u16) -> Self {
77        Self(value)
78    }
79
80    /// Get raw u16 value
81    #[inline]
82    pub const fn as_u16(&self) -> u16 {
83        self.0
84    }
85
86    /// Get voice style
87    #[inline]
88    pub const fn voice(&self) -> Voice {
89        match self.0 & Self::VOICE_MASK {
90            0x0000 => Voice::Neutral,
91            0x4000 => Voice::Formal,
92            0x8000 => Voice::Casual,
93            _ => Voice::Technical,
94        }
95    }
96
97    /// Get tone
98    #[inline]
99    pub const fn tone(&self) -> Tone {
100        match self.0 & Self::TONE_MASK {
101            0x0000 => Tone::Neutral,
102            0x1000 => Tone::Positive,
103            0x2000 => Tone::Empathetic,
104            _ => Tone::Cautious,
105        }
106    }
107
108    /// Get warmth level
109    #[inline]
110    pub const fn warmth(&self) -> Warmth {
111        match self.0 & Self::WARMTH_MASK {
112            0x0000 => Warmth::Cold,
113            0x0400 => Warmth::Neutral,
114            0x0800 => Warmth::Warm,
115            _ => Warmth::VeryWarm,
116        }
117    }
118
119    /// Get output format
120    #[inline]
121    pub const fn format(&self) -> Format {
122        match self.0 & Self::FORMAT_MASK {
123            0x0000 => Format::Prose,
124            0x0100 => Format::Bulleted,
125            0x0200 => Format::Numbered,
126            _ => Format::Structured,
127        }
128    }
129
130    /// Get accuracy level
131    #[inline]
132    pub const fn accuracy(&self) -> Accuracy {
133        match self.0 & Self::ACCURACY_MASK {
134            0x0000 => Accuracy::Low,
135            0x0040 => Accuracy::Medium,
136            0x0080 => Accuracy::High,
137            _ => Accuracy::Verified,
138        }
139    }
140
141    /// Get urgency level
142    #[inline]
143    pub const fn urgency(&self) -> Urgency {
144        match self.0 & Self::URGENCY_MASK {
145            0x0000 => Urgency::Low,
146            0x0010 => Urgency::Normal,
147            0x0020 => Urgency::High,
148            _ => Urgency::Critical,
149        }
150    }
151
152    /// Set voice style
153    #[inline]
154    pub const fn with_voice(self, voice: Voice) -> Self {
155        let voice_bits = match voice {
156            Voice::Neutral => 0x0000,
157            Voice::Formal => 0x4000,
158            Voice::Casual => 0x8000,
159            Voice::Technical => 0xC000,
160        };
161        Self((self.0 & !Self::VOICE_MASK) | voice_bits)
162    }
163
164    /// Set tone
165    #[inline]
166    pub const fn with_tone(self, tone: Tone) -> Self {
167        let tone_bits = match tone {
168            Tone::Neutral => 0x0000,
169            Tone::Positive => 0x1000,
170            Tone::Empathetic => 0x2000,
171            Tone::Cautious => 0x3000,
172        };
173        Self((self.0 & !Self::TONE_MASK) | tone_bits)
174    }
175
176    /// Set warmth level
177    #[inline]
178    pub const fn with_warmth(self, warmth: Warmth) -> Self {
179        let warmth_bits = match warmth {
180            Warmth::Cold => 0x0000,
181            Warmth::Neutral => 0x0400,
182            Warmth::Warm => 0x0800,
183            Warmth::VeryWarm => 0x0C00,
184        };
185        Self((self.0 & !Self::WARMTH_MASK) | warmth_bits)
186    }
187
188    /// Set output format
189    #[inline]
190    pub const fn with_format(self, format: Format) -> Self {
191        let format_bits = match format {
192            Format::Prose => 0x0000,
193            Format::Bulleted => 0x0100,
194            Format::Numbered => 0x0200,
195            Format::Structured => 0x0300,
196        };
197        Self((self.0 & !Self::FORMAT_MASK) | format_bits)
198    }
199
200    /// Set accuracy level
201    #[inline]
202    pub const fn with_accuracy(self, accuracy: Accuracy) -> Self {
203        let accuracy_bits = match accuracy {
204            Accuracy::Low => 0x0000,
205            Accuracy::Medium => 0x0040,
206            Accuracy::High => 0x0080,
207            Accuracy::Verified => 0x00C0,
208        };
209        Self((self.0 & !Self::ACCURACY_MASK) | accuracy_bits)
210    }
211
212    /// Set urgency level
213    #[inline]
214    pub const fn with_urgency(self, urgency: Urgency) -> Self {
215        let urgency_bits = match urgency {
216            Urgency::Low => 0x0000,
217            Urgency::Normal => 0x0010,
218            Urgency::High => 0x0020,
219            Urgency::Critical => 0x0030,
220        };
221        Self((self.0 & !Self::URGENCY_MASK) | urgency_bits)
222    }
223
224    /// Create a crisis-appropriate modifier (empathetic, warm, high urgency)
225    pub const fn crisis() -> Self {
226        Self(0x0000)
227            .with_tone(Tone::Empathetic)
228            .with_warmth(Warmth::VeryWarm)
229            .with_urgency(Urgency::High)
230            .with_accuracy(Accuracy::High)
231    }
232
233    /// Create a professional modifier (formal, neutral warmth)
234    pub const fn professional() -> Self {
235        Self(0x0000)
236            .with_voice(Voice::Formal)
237            .with_warmth(Warmth::Neutral)
238            .with_accuracy(Accuracy::High)
239            .with_urgency(Urgency::Normal)
240    }
241
242    /// Create a friendly modifier (casual, warm)
243    pub const fn friendly() -> Self {
244        Self(0x0000)
245            .with_voice(Voice::Casual)
246            .with_tone(Tone::Positive)
247            .with_warmth(Warmth::Warm)
248            .with_urgency(Urgency::Normal)
249    }
250}
251
252impl Default for Modifier {
253    fn default() -> Self {
254        // Neutral voice, tone, warmth; prose format; medium accuracy; normal urgency
255        Self(0x0400 | 0x0040 | 0x0010) // WARMTH_NEUTRAL | ACCURACY_MEDIUM | URGENCY_NORMAL
256    }
257}
258
259impl fmt::Display for Modifier {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        write!(
262            f,
263            "MOD(0x{:04X}: {:?}/{:?}/{:?}/{:?})",
264            self.0,
265            self.voice(),
266            self.tone(),
267            self.warmth(),
268            self.format()
269        )
270    }
271}
272
273impl From<u16> for Modifier {
274    fn from(value: u16) -> Self {
275        Self(value)
276    }
277}
278
279impl From<Modifier> for u16 {
280    fn from(modifier: Modifier) -> Self {
281        modifier.0
282    }
283}
284
285// ========== Field Enums ==========
286
287/// Voice style for output
288#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
289pub enum Voice {
290    /// Default neutral voice
291    Neutral,
292    /// Formal/professional voice
293    Formal,
294    /// Casual/conversational voice
295    Casual,
296    /// Technical/precise voice
297    Technical,
298}
299
300/// Emotional tone
301#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
302pub enum Tone {
303    /// Neutral tone
304    Neutral,
305    /// Positive/upbeat tone
306    Positive,
307    /// Empathetic/understanding tone
308    Empathetic,
309    /// Cautious/careful tone
310    Cautious,
311}
312
313/// Interpersonal warmth level
314#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
315pub enum Warmth {
316    /// Cold/distant
317    Cold,
318    /// Neutral warmth
319    Neutral,
320    /// Warm/friendly
321    Warm,
322    /// Very warm/caring
323    VeryWarm,
324}
325
326/// Output format
327#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
328pub enum Format {
329    /// Prose/paragraph format
330    Prose,
331    /// Bulleted list
332    Bulleted,
333    /// Numbered list
334    Numbered,
335    /// Structured/formatted output
336    Structured,
337}
338
339/// Confidence/accuracy level
340#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
341pub enum Accuracy {
342    /// Low confidence
343    Low,
344    /// Medium confidence
345    Medium,
346    /// High confidence
347    High,
348    /// Verified/certain
349    Verified,
350}
351
352/// Urgency/priority level
353#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
354pub enum Urgency {
355    /// Low priority
356    Low,
357    /// Normal priority
358    Normal,
359    /// High priority
360    High,
361    /// Critical priority
362    Critical,
363}
364
365#[cfg(test)]
366mod tests {
367    use super::*;
368
369    #[test]
370    fn test_default_modifier() {
371        let m = Modifier::default();
372        assert_eq!(m.voice(), Voice::Neutral);
373        assert_eq!(m.tone(), Tone::Neutral);
374        assert_eq!(m.warmth(), Warmth::Neutral);
375        assert_eq!(m.format(), Format::Prose);
376        assert_eq!(m.accuracy(), Accuracy::Medium);
377        assert_eq!(m.urgency(), Urgency::Normal);
378    }
379
380    #[test]
381    fn test_field_extraction() {
382        // Casual(0x8000) + Empathetic(0x2000) + Neutral warmth(0x0400) + Medium accuracy(0x0040)
383        let m = Modifier::from_u16(0xA440);
384        assert_eq!(m.voice(), Voice::Casual);
385        assert_eq!(m.tone(), Tone::Empathetic);
386        assert_eq!(m.warmth(), Warmth::Neutral);
387        assert_eq!(m.accuracy(), Accuracy::Medium);
388    }
389
390    #[test]
391    fn test_field_setting() {
392        let m = Modifier::default()
393            .with_voice(Voice::Formal)
394            .with_tone(Tone::Empathetic)
395            .with_warmth(Warmth::VeryWarm)
396            .with_format(Format::Bulleted);
397
398        assert_eq!(m.voice(), Voice::Formal);
399        assert_eq!(m.tone(), Tone::Empathetic);
400        assert_eq!(m.warmth(), Warmth::VeryWarm);
401        assert_eq!(m.format(), Format::Bulleted);
402    }
403
404    #[test]
405    fn test_crisis_modifier() {
406        let m = Modifier::crisis();
407        assert_eq!(m.tone(), Tone::Empathetic);
408        assert_eq!(m.warmth(), Warmth::VeryWarm);
409        assert_eq!(m.urgency(), Urgency::High);
410        assert_eq!(m.accuracy(), Accuracy::High);
411    }
412
413    #[test]
414    fn test_preset_modifiers() {
415        let pro = Modifier::professional();
416        assert_eq!(pro.voice(), Voice::Formal);
417
418        let friendly = Modifier::friendly();
419        assert_eq!(friendly.voice(), Voice::Casual);
420        assert_eq!(friendly.warmth(), Warmth::Warm);
421    }
422
423    #[test]
424    fn test_serialization() {
425        let modifier = Modifier::crisis();
426        let json = serde_json::to_string(&modifier).unwrap();
427        let deserialized: Modifier = serde_json::from_str(&json).unwrap();
428        assert_eq!(modifier, deserialized);
429    }
430}