1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq)]
5pub enum Value {
6 String(String),
8 U8(u8),
10 U16(u16),
12 U32(u32),
14 I16(i16),
16 I32(i32),
18 URational(u32, u32),
20 IRational(i32, i32),
22 F32(f32),
24 F64(f64),
26 Binary(Vec<u8>),
28 List(Vec<Value>),
30 Undefined(Vec<u8>),
32}
33
34impl Value {
35 pub fn to_display_string(&self) -> String {
37 match self {
38 Value::String(s) => s.clone(),
39 Value::U8(v) => v.to_string(),
40 Value::U16(v) => v.to_string(),
41 Value::U32(v) => v.to_string(),
42 Value::I16(v) => v.to_string(),
43 Value::I32(v) => v.to_string(),
44 Value::URational(n, d) => {
45 if *d == 0 {
46 if *n == 0 { "undef".to_string() } else { "inf".to_string() }
47 } else if *n % *d == 0 {
48 (*n / *d).to_string()
49 } else {
50 format!("{}/{}", n, d)
51 }
52 }
53 Value::IRational(n, d) => {
54 if *d == 0 {
55 if *n >= 0 { "inf".to_string() } else { "-inf".to_string() }
56 } else if *n % *d == 0 {
57 (*n / *d).to_string()
58 } else {
59 format!("{}/{}", n, d)
60 }
61 }
62 Value::F32(v) => format!("{}", v),
63 Value::F64(v) => format!("{}", v),
64 Value::Binary(data) => format!("(Binary data {} bytes)", data.len()),
65 Value::List(items) => {
66 items
67 .iter()
68 .map(|v| v.to_display_string())
69 .collect::<Vec<_>>()
70 .join(", ")
71 }
72 Value::Undefined(data) => format!("(Undefined {} bytes)", data.len()),
73 }
74 }
75
76 pub fn as_f64(&self) -> Option<f64> {
78 match self {
79 Value::U8(v) => Some(*v as f64),
80 Value::U16(v) => Some(*v as f64),
81 Value::U32(v) => Some(*v as f64),
82 Value::I16(v) => Some(*v as f64),
83 Value::I32(v) => Some(*v as f64),
84 Value::F32(v) => Some(*v as f64),
85 Value::F64(v) => Some(*v),
86 Value::URational(n, d) if *d != 0 => Some(*n as f64 / *d as f64),
87 Value::IRational(n, d) if *d != 0 => Some(*n as f64 / *d as f64),
88 _ => None,
89 }
90 }
91
92 pub fn as_str(&self) -> Option<&str> {
94 match self {
95 Value::String(s) => Some(s),
96 _ => None,
97 }
98 }
99
100 pub fn as_u64(&self) -> Option<u64> {
102 match self {
103 Value::U8(v) => Some(*v as u64),
104 Value::U16(v) => Some(*v as u64),
105 Value::U32(v) => Some(*v as u64),
106 _ => None,
107 }
108 }
109}
110
111impl fmt::Display for Value {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 write!(f, "{}", self.to_display_string())
114 }
115}
116
117pub fn format_g15(v: f64) -> String {
120 format_g_prec(v, 15)
121}
122
123pub fn format_g_prec(v: f64, prec: usize) -> String {
126 if v == 0.0 {
127 return "0".to_string();
128 }
129 let abs_v = v.abs();
130 let exp = abs_v.log10().floor() as i32;
131 if exp >= -4 && exp < prec as i32 {
132 let decimal_places = ((prec as i32 - 1 - exp).max(0)) as usize;
134 let s = format!("{:.prec$}", v, prec = decimal_places);
135 if s.contains('.') {
136 s.trim_end_matches('0').trim_end_matches('.').to_string()
137 } else {
138 s
139 }
140 } else {
141 let decimal_places = prec - 1;
143 let s = format!("{:.prec$e}", v, prec = decimal_places);
144 let (mantissa_part, exp_part) = if let Some(e_pos) = s.find('e') {
148 (&s[..e_pos], &s[e_pos+1..])
149 } else {
150 return s;
151 };
152 let mantissa_trimmed = if mantissa_part.contains('.') {
153 mantissa_part.trim_end_matches('0').trim_end_matches('.')
154 } else {
155 mantissa_part
156 };
157 let exp_val: i32 = exp_part.parse().unwrap_or(0);
159 let exp_str = if exp_val >= 0 {
160 format!("e+{:02}", exp_val)
161 } else {
162 format!("e-{:02}", -exp_val)
163 };
164 format!("{}{}", mantissa_trimmed, exp_str)
165 }
166}