use thiserror::Error as ThisError;
-use crate::raw::VarType;
+use crate::{
+ cooked::VarWidth,
+ raw::{self, VarType},
+};
#[derive(ThisError, Debug)]
pub enum Error {
- #[error("Unknown format type {value}")]
+ #[error("Unknown format type {value}.")]
UnknownFormat { value: u16 },
#[error("Output format {0} specifies width {}, but {} requires an even width.", .0.w, .0.format)]
#[error("Numeric variable is not compatible with string format {0}.")]
UnnamedVariableNotCompatibleWithStringFormat(Format),
- #[error("String variable {variable} is not compatible with numeric format {format}.")]
- NamedVariableNotCompatibleWithNumericFormat { variable: String, format: Format },
-
- #[error("Numeric variable {variable} is not compatible with string format {format}.")]
- NamedVariableNotCompatibleWithStringFormat { variable: String, format: Format },
-
#[error("String variable {variable} with width {width} is not compatible with format {bad_spec}. Use format {good_spec} instead.")]
NamedStringVariableBadSpecWidth {
variable: String,
AHex,
}
-pub const MAX_STRING: Width = 32767;
+pub type Width = u16;
+pub type SignedWidth = i16;
-type Width = u16;
-type SignedWidth = i16;
-
-type Decimals = u8;
+pub type Decimals = u8;
impl Format {
pub fn max_width(self) -> Width {
match self {
Self::P | Self::PK | Self::PIBHex | Self::RBHex => 16,
Self::IB | Self::PIB | Self::RB => 8,
- Self::A => MAX_STRING,
- Self::AHex => MAX_STRING * 2,
+ Self::A => 32767,
+ Self::AHex => 32767 * 2,
_ => 40,
}
}
pub fn var_type(self) -> VarType {
match self {
Self::A | Self::AHex => VarType::String,
- _ => VarType::Number,
+ _ => VarType::Numeric,
}
}
- pub fn check_type_compatibility(
- self,
- variable: Option<&str>,
- var_type: VarType,
- ) -> Result<(), Error> {
+ /// Checks whether this format is valid for a variable with the given
+ /// `var_type`.
+ pub fn check_type_compatibility(self, var_type: VarType) -> Result<(), Error> {
let my_type = self.var_type();
match (my_type, var_type) {
- (VarType::Number, VarType::String) => {
- if let Some(variable) = variable {
- Err(Error::NamedVariableNotCompatibleWithNumericFormat {
- variable: variable.into(),
- format: self,
- })
- } else {
- Err(Error::UnnamedVariableNotCompatibleWithNumericFormat(self))
- }
+ (VarType::Numeric, VarType::String) => {
+ Err(Error::UnnamedVariableNotCompatibleWithNumericFormat(self))
}
- (VarType::String, VarType::Number) => {
- if let Some(variable) = variable {
- Err(Error::NamedVariableNotCompatibleWithStringFormat {
- variable: variable.into(),
- format: self,
- })
- } else {
- Err(Error::UnnamedVariableNotCompatibleWithStringFormat(self))
- }
+ (VarType::String, VarType::Numeric) => {
+ Err(Error::UnnamedVariableNotCompatibleWithStringFormat(self))
}
_ => Ok(()),
}
self.d
}
+ pub fn default_for_width(var_width: VarWidth) -> Self {
+ match var_width {
+ VarWidth::Numeric => Spec {
+ format: Format::F,
+ w: 8,
+ d: 2,
+ },
+ VarWidth::String(w) => Spec {
+ format: Format::A,
+ w,
+ d: 0,
+ },
+ }
+ }
+
pub fn fixed_from(source: &UncheckedSpec) -> Self {
let UncheckedSpec { format, w, d } = *source;
let (min, max) = format.width_range().into_inner();
Self { format, w, d }
}
- pub fn var_width(self) -> Width {
+ pub fn var_width(self) -> VarWidth {
match self.format {
- Format::A => self.w,
- Format::AHex => self.w / 2,
- _ => 0,
+ Format::A => VarWidth::String(self.w),
+ Format::AHex => VarWidth::String(self.w / 2),
+ _ => VarWidth::Numeric,
}
}
self.format.var_type()
}
- pub fn check_width_compatibility(self, variable: Option<&str>, w: Width) -> Result<(), Error> {
- self.format.check_type_compatibility(variable, self.var_type())?;
- let expected_width = self.var_width();
- if w != expected_width {
- let bad_spec = self;
- let good_spec = if self.format == Format::A {
- Spec { w, ..self }
- } else {
- Spec { w: w * 2, ..self }
- };
- if let Some(variable) = variable {
- Err(Error::NamedStringVariableBadSpecWidth {
- variable: variable.into(),
- width: w,
- bad_spec,
- good_spec,
- })
- } else {
- Err(Error::UnnamedStringVariableBadSpecWidth {
+ /// Checks whether this format specification is valid for a variable with
+ /// width `var_width`.
+ pub fn check_width_compatibility(self, var_width: VarWidth) -> Result<Self, Error> {
+ // Verify that the format is right for the variable's type.
+ self.format.check_type_compatibility(var_width.into())?;
+
+ if let VarWidth::String(w) = var_width {
+ if var_width != self.var_width() {
+ let bad_spec = self;
+ let good_spec = if self.format == Format::A {
+ Spec { w, ..self }
+ } else {
+ Spec { w: w * 2, ..self }
+ };
+ return Err(Error::UnnamedStringVariableBadSpecWidth {
width: w,
bad_spec,
good_spec,
- })
+ });
}
- } else {
- Ok(())
}
+
+ Ok(self)
}
}
pub d: Decimals,
}
-impl TryFrom<u32> for UncheckedSpec {
+impl TryFrom<raw::Spec> for UncheckedSpec {
type Error = Error;
- fn try_from(source: u32) -> Result<Self, Self::Error> {
- let raw_format = (source >> 16) as u16;
+ fn try_from(raw: raw::Spec) -> Result<Self, Self::Error> {
+ let raw = raw.0;
+ let raw_format = (raw >> 16) as u16;
let format = raw_format.try_into()?;
- let w = ((source >> 8) & 0xff) as Width;
- let d = (source & 0xff) as Decimals;
+ let w = ((raw >> 8) & 0xff) as Width;
+ let d = (raw & 0xff) as Decimals;
Ok(Self { format, w, d })
}
}