Skip to main content

libsurfer/
trace_style.rs

1use derive_more::{Display, FromStr};
2use enum_iterator::Sequence;
3use num::Zero as _;
4use serde::{Deserialize, Serialize};
5use surfer_translation_types::VariableValue;
6
7/// Selects the drawing style for digital waveform traces.
8///
9/// - `Default`: Standard trace drawing with both upper and lower lines for all values.
10/// - `Dinotrace`: Dinotrace-inspired style. All-zero vectors have no upper line and a bold lower
11///   line. All-one vectors have a bold upper line and no lower line.
12/// - `Zero`: All-zero vectors are drawn without the upper line. Other vectors use standard drawing.
13#[derive(
14    Clone, Copy, Debug, Default, Deserialize, Display, FromStr, PartialEq, Eq, Sequence, Serialize,
15)]
16pub enum TraceStyle {
17    #[default]
18    Default,
19    Dinotrace,
20    Zero,
21}
22
23/// Information about values that enable special drawing of all-0 and all-1 values.
24///
25/// - `Normal`: Standard trace drawing applies.
26/// - `AllZeros`: Value is all zeros; may be drawn with reduced upper line depending on style.
27/// - `AllZerosThick`: Value is all zeros in Dinotrace style; drawn with bold lower line and no upper line.
28/// - `AllOnes`: Value is all ones in Dinotrace style; drawn with bold upper line.
29#[derive(Clone, Copy)]
30pub(crate) enum TraceValue {
31    Normal,
32    AllZeros,
33    AllZerosThick,
34    AllOnes,
35}
36
37impl TraceValue {
38    /// Determines the special trace value representation based on the signal value and style.
39    ///
40    /// For `TraceStyle::Default`, always returns `TraceValue::Normal`.
41    ///
42    /// For `TraceStyle::Dinotrace`:
43    /// - All-zero values return `TraceValue::AllZerosThick`.
44    /// - All-one values (all bits set) return `TraceValue::AllOnes`.
45    /// - Other values return `TraceValue::Normal`.
46    ///
47    /// For `TraceStyle::Zero`:
48    /// - All-zero values return `TraceValue::AllZeros`.
49    /// - Other values return `TraceValue::Normal`.
50    ///
51    /// # Arguments
52    ///
53    /// * `val` - The signal value to analyze.
54    /// * `num_bits` - The bit width of the signal. Required to determine if all bits are set.
55    /// * `trace_style` - The trace drawing style to apply.
56    pub(crate) fn from_value(
57        val: &VariableValue,
58        num_bits: Option<u32>,
59        trace_style: TraceStyle,
60    ) -> Self {
61        if trace_style == TraceStyle::Default {
62            return Self::Normal;
63        }
64        match val {
65            VariableValue::BigUint(u) if u.is_zero() => {
66                if trace_style == TraceStyle::Dinotrace {
67                    TraceValue::AllZerosThick
68                } else {
69                    TraceValue::AllZeros
70                }
71            }
72            VariableValue::BigUint(u)
73                if trace_style == TraceStyle::Dinotrace
74                    && num_bits.is_some_and(|bits| u.count_ones() == u64::from(bits)) =>
75            {
76                TraceValue::AllOnes
77            }
78            VariableValue::BigUint(_) => TraceValue::Normal,
79            VariableValue::String(_) => TraceValue::Normal,
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_default_style_returns_normal() {
90        let val = VariableValue::BigUint(42u32.into());
91        let result = TraceValue::from_value(&val, Some(8), TraceStyle::Default);
92        assert!(matches!(result, TraceValue::Normal));
93    }
94
95    #[test]
96    fn test_dinotrace_all_zeros() {
97        let val = VariableValue::BigUint(0u32.into());
98        let result = TraceValue::from_value(&val, Some(8), TraceStyle::Dinotrace);
99        assert!(matches!(result, TraceValue::AllZerosThick));
100    }
101
102    #[test]
103    fn test_dinotrace_all_ones_8bit() {
104        let val = VariableValue::BigUint(255u32.into()); // 0xFF = 8 bits all set
105        let result = TraceValue::from_value(&val, Some(8), TraceStyle::Dinotrace);
106        assert!(matches!(result, TraceValue::AllOnes));
107    }
108
109    #[test]
110    fn test_dinotrace_all_ones_32bit() {
111        let val = VariableValue::BigUint(u32::MAX.into());
112        let result = TraceValue::from_value(&val, Some(32), TraceStyle::Dinotrace);
113        assert!(matches!(result, TraceValue::AllOnes));
114    }
115
116    #[test]
117    fn test_dinotrace_partial_value() {
118        let val = VariableValue::BigUint(127u32.into()); // 0x7F = not all ones in 8 bits
119        let result = TraceValue::from_value(&val, Some(8), TraceStyle::Dinotrace);
120        assert!(matches!(result, TraceValue::Normal));
121    }
122
123    #[test]
124    fn test_dinotrace_no_num_bits() {
125        let val = VariableValue::BigUint(255u32.into());
126        let result = TraceValue::from_value(&val, None, TraceStyle::Dinotrace);
127        // Without num_bits, cannot determine if all ones, so returns Normal
128        assert!(matches!(result, TraceValue::Normal));
129    }
130
131    #[test]
132    fn test_zero_style_all_zeros() {
133        let val = VariableValue::BigUint(0u32.into());
134        let result = TraceValue::from_value(&val, Some(8), TraceStyle::Zero);
135        assert!(matches!(result, TraceValue::AllZeros));
136    }
137
138    #[test]
139    fn test_zero_style_nonzero() {
140        let val = VariableValue::BigUint(42u32.into());
141        let result = TraceValue::from_value(&val, Some(8), TraceStyle::Zero);
142        assert!(matches!(result, TraceValue::Normal));
143    }
144
145    #[test]
146    fn test_zero_style_all_ones() {
147        let val = VariableValue::BigUint(255u32.into());
148        let result = TraceValue::from_value(&val, Some(8), TraceStyle::Zero);
149        // Zero style doesn't special-case all ones
150        assert!(matches!(result, TraceValue::Normal));
151    }
152
153    #[test]
154    fn test_string_value_is_normal() {
155        let val = VariableValue::String("hello".to_string());
156        let result = TraceValue::from_value(&val, Some(8), TraceStyle::Dinotrace);
157        assert!(matches!(result, TraceValue::Normal));
158    }
159}