use std::fmt::{Display, Write};
use itertools::Itertools;
use pest::{Parser, iterators::Pair, RuleType};
type PResult<T> = std::result::Result<T, PError>;
#[derive(Debug)]
pub struct PError;
impl <R: RuleType> From<pest::error::Error<R>> for PError {
fn from(value: pest::error::Error<R>) -> Self {
Self
}
}
impl Display for PError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Format parsing error")
}
}
#[derive(Debug)]
pub enum FieldIndexError {
IndexOutOfRange{index: usize, len: usize},
IsListDirected,
}
impl Display for FieldIndexError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FieldIndexError::IndexOutOfRange { index, len } => {
write!(f, "index {index} is out of range for a FortFormat instance with {len} fields")
},
FieldIndexError::IsListDirected => {
write!(f, "cannot index into a FortFormat::ListDirected instance")
},
}
}
}
impl std::error::Error for FieldIndexError {}
#[derive(Parser)]
#[grammar = "fort.pest"]
pub(crate) struct FortParser;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RealFmt {
D,
E,
F,
G
}
impl Display for RealFmt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
RealFmt::D => "d",
RealFmt::E => "e",
RealFmt::F => "f",
RealFmt::G => "g",
};
write!(f, "{s}")
}
}
impl RealFmt {
pub fn is_d(&self) -> bool {
if let Self::D = self {
true
} else {
false
}
}
pub fn is_e(&self) -> bool {
if let Self::E = self {
true
} else {
false
}
}
pub fn is_f(&self) -> bool {
if let Self::F = self {
true
} else {
false
}
}
pub fn is_g(&self) -> bool {
if let Self::G = self {
true
} else {
false
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntBase {
Decimal,
Octal,
Hexadecimal
}
impl Display for IntBase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
IntBase::Decimal => "i",
IntBase::Octal => "o",
IntBase::Hexadecimal => "z",
};
write!(f, "{s}")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FortField {
Char{width: Option<u32>},
Logical{width: u32},
Integer{width: u32, zeros: Option<u32>, base: IntBase},
Real{width: u32, precision: Option<u32>, fmt: RealFmt, scale: i32},
Skip,
Any
}
impl FortField {
pub fn is_positional(&self) -> bool {
match self {
FortField::Char { width: _ } => false,
FortField::Logical { width: _ } => false,
FortField::Integer { width: _, zeros: _, base: _ } => false,
FortField::Real { width: _, precision: _, fmt: _, scale: _ } => false,
FortField::Any => false,
FortField::Skip => true,
}
}
pub fn width(&self) -> Option<u32> {
match self {
FortField::Char { width } => Some(width.unwrap_or(1)),
FortField::Logical { width } => Some(*width),
FortField::Integer { width, zeros: _, base: _ } => Some(*width),
FortField::Real { width, precision: _, fmt: _, scale: _ } => Some(*width),
FortField::Any => None,
FortField::Skip => Some(1),
}
}
#[cfg(feature = "dataframes")]
pub fn polars_dtype(&self) -> Option<polars::datatypes::DataType> {
match self {
FortField::Char { width: _ } => Some(polars::datatypes::DataType::Utf8),
FortField::Logical { width: _ } => Some(polars::datatypes::DataType::Boolean),
FortField::Integer { width: _, zeros: _, base: _ } => Some(polars::datatypes::DataType::Int64),
FortField::Real { width: _, precision: _, fmt: _, scale: _ } => Some(polars::datatypes::DataType::Float64),
FortField::Any => unimplemented!("polars dtype for list directed format"),
FortField::Skip => None,
}
}
fn display_helper<W: std::fmt::Write>(&self, include_scale: bool, mut f: W) -> std::fmt::Result {
match self {
FortField::Char { width } => write!(f, "a{}", width.unwrap_or(1)),
FortField::Logical { width } => write!(f, "l{width}"),
FortField::Integer { width, zeros, base } => {
if let Some(m) = zeros {
write!(f, "{base}{width}.{m}")
} else {
write!(f, "{base}{width}")
}
},
FortField::Real { width, precision, fmt, scale } => {
let p = if scale == &0 || !include_scale { "".to_owned() } else { format!("{scale}p") };
if let Some(m) = precision {
write!(f, "{p}{fmt}{width}.{m}")
} else {
write!(f, "{p}{fmt}{width}")
}
},
FortField::Any => write!(f, "*"),
FortField::Skip => write!(f, "x"),
}
}
fn as_str_and_scale(&self) -> (String, Option<i32>) {
let scale = match self {
FortField::Char { width } => None,
FortField::Logical { width } => None,
FortField::Integer { width, zeros, base } => None,
FortField::Real { width, precision, fmt, scale } => Some(*scale),
FortField::Skip => None,
FortField::Any => None,
};
let mut s = String::new();
self.display_helper(false, &mut s)
.expect("writing to a string should not fail");
(s, scale)
}
}
impl Display for FortField {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.display_helper(true, f)
}
}
#[derive(Debug, PartialEq)]
pub enum FortValue {
Logical(bool),
Char(String),
Integer(i64),
Real(f64)
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for FortValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de> {
deserializer.deserialize_any(crate::de::FortValueVisitor)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for FortValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer {
match self {
FortValue::Logical(b) => b.serialize(serializer),
FortValue::Char(s) => s.serialize(serializer),
FortValue::Integer(i) => i.serialize(serializer),
FortValue::Real(r) => r.serialize(serializer),
}
}
}
#[cfg(feature = "dataframes")]
impl From<FortValue> for polars::datatypes::AnyValue<'_> {
fn from(value: FortValue) -> Self {
match value {
FortValue::Logical(b) => polars::datatypes::AnyValue::Boolean(b),
FortValue::Char(s) => polars::datatypes::AnyValue::Utf8Owned(s.into()),
FortValue::Integer(i) => polars::datatypes::AnyValue::Int64(i),
FortValue::Real(f) => polars::datatypes::AnyValue::Float64(f),
}
}
}
pub struct FieldIter<'i>{
all: bool,
fields: Option<std::slice::Iter<'i, FortField>>,
}
impl<'i> Iterator for FieldIter<'i> {
type Item = &'i FortField;
fn next(&mut self) -> Option<Self::Item> {
if let Some(fields) = &mut self.fields {
loop {
let element = fields.next();
if !self.all && element.map(|e| e.is_positional()).unwrap_or(false) {
} else {
return element;
}
}
} else {
None
}
}
}
#[derive(Debug, Clone)]
pub enum FortFormat {
Fixed(Vec<FortField>),
ListDirected
}
impl FortFormat {
pub fn is_list_directed(&self) -> bool {
if let Self::ListDirected = self {
true
} else {
false
}
}
pub fn get_field(&self, index: usize) -> Option<&FortField> {
match self {
FortFormat::Fixed(vec) => vec.get(index),
FortFormat::ListDirected => Some(&FortField::Any),
}
}
pub fn set_field(&mut self, index: usize, field: FortField) -> Result<(), FieldIndexError>{
match self {
FortFormat::Fixed(vec) => {
if index < vec.len() {
vec[index] = field;
Ok(())
} else {
Err(FieldIndexError::IndexOutOfRange { index, len: vec.len() })
}
},
FortFormat::ListDirected => Err(FieldIndexError::IsListDirected),
}
}
pub fn insert_field(&mut self, index: usize, field: FortField) -> Result<(), FieldIndexError> {
match self {
FortFormat::Fixed(vec) => {
if index <= vec.len() {
vec.insert(index, field);
Ok(())
} else {
Err(FieldIndexError::IndexOutOfRange { index, len: vec.len() })
}
},
FortFormat::ListDirected => Err(FieldIndexError::IsListDirected),
}
}
pub fn parse(fmt_str: &str) -> PResult<Self> {
if fmt_str.trim() == "*" {
return Ok(Self::ListDirected);
}
let mut fields = vec![];
let tree = FortParser::parse(Rule::format, fmt_str)?.next().unwrap();
let mut stack: Vec<_> = tree.into_inner().rev().collect();
let mut next_repeat: usize = 1;
let mut curr_scale: i32 = 0;
while stack.len() > 0 {
let pair = stack.pop().unwrap();
let kind = match pair.as_rule() {
Rule::format => unreachable!(),
Rule::width => return Err(PError),
Rule::prec => return Err(PError),
Rule::signed => return Err(PError),
Rule::WHITESPACE => continue,
Rule::EOI => break,
Rule::repeat => {
next_repeat = pair.as_str().parse().unwrap();
continue;
},
Rule::scale => {
curr_scale = consume_signed_from_pair(pair).unwrap_or(0);
continue;
},
Rule::field | Rule::element => {
for inner in pair.into_inner().rev() {
stack.push(inner);
}
continue;
},
Rule::expr => {
for _ in 0..next_repeat {
for inner in pair.clone().into_inner().rev() {
stack.push(inner);
}
}
next_repeat = 1;
continue;
}
Rule::skip => FortField::Skip,
Rule::char => FortField::Char { width: consume_width_from_pair(pair) },
Rule::logical => {
let width = consume_width_from_pair(pair)
.ok_or_else(|| PError)?;
FortField::Logical { width }
},
Rule::integer => {
let (width, zeros) = consume_req_width_and_prec_from_pair(pair, "integer")?;
FortField::Integer { width, zeros, base: IntBase::Decimal }
},
Rule::octal => {
let (width, zeros) = consume_req_width_and_prec_from_pair(pair, "octal")?;
FortField::Integer { width, zeros, base: IntBase::Octal }
},
Rule::hex => {
let (width, zeros) = consume_req_width_and_prec_from_pair(pair, "hexadecimal")?;
FortField::Integer { width, zeros, base: IntBase::Hexadecimal }
},
Rule::real => {
let (width, precision) = consume_req_width_and_prec_from_pair(pair, "f-real")?;
FortField::Real { width, precision, fmt: RealFmt::F, scale: curr_scale }
},
Rule::realorexp => {
let (width, precision) = consume_req_width_and_prec_from_pair(pair, "f-real")?;
FortField::Real { width, precision, fmt: RealFmt::G, scale: curr_scale }
},
Rule::exponential => {
let (width, precision) = consume_req_width_and_prec_from_pair(pair, "f-real")?;
FortField::Real { width, precision, fmt: RealFmt::E, scale: curr_scale }
},
Rule::expdouble => {
let (width, precision) = consume_req_width_and_prec_from_pair(pair, "f-real")?;
FortField::Real { width, precision, fmt: RealFmt::D, scale: curr_scale }
},
};
for _ in 0..next_repeat {
fields.push(kind);
}
next_repeat = 1;
}
Ok(Self::Fixed(fields))
}
pub fn into_fields(self) -> Option<Vec<FortField>> {
match self {
FortFormat::Fixed(vec) => Some(vec),
FortFormat::ListDirected => None,
}
}
pub fn iter_fields(&self) -> FieldIter {
match self {
FortFormat::Fixed(vec) => FieldIter{fields: Some(vec.iter()), all: true},
FortFormat::ListDirected => FieldIter { fields: None, all: true },
}
}
pub fn iter_non_pos_fields(&self) -> FieldIter {
match self {
FortFormat::Fixed(vec) => FieldIter{fields: Some(vec.iter()), all: false},
FortFormat::ListDirected => FieldIter { fields: None, all: false },
}
}
pub fn non_pos_len(&self) -> usize {
let n = self.iter_non_pos_fields().count();
n
}
fn collapsed_version(&self, max_group_len: usize) -> Vec<(u32, FortField)> {
let it = self.iter_fields().map(|f| (1, f.to_owned()));
if max_group_len == 0 {
return it.collect();
}
let one_grouped = it.coalesce(|(n, prev_fmt), (m, curr_fmt)| {
if prev_fmt == curr_fmt {
Ok((n + 1, prev_fmt))
} else {
Err(( (n, prev_fmt), (m, curr_fmt) ))
}
});
one_grouped.collect()
}
pub fn fmt_string(&self, max_group_len: usize) -> String {
let mut fmt_str = "(".to_string();
let mut curr_scale = 0;
let mut first_el = true;
for (rep, fmt_el) in self.collapsed_version(max_group_len) {
let (el_str, next_scale) = fmt_el.as_str_and_scale();
let include_scale = if let Some(s) = next_scale {
if s == curr_scale {
false
} else {
curr_scale = s;
true
}
} else {
false
};
let include_rep = rep != 1 || fmt_el.is_positional();
if first_el {
first_el = false;
} else {
fmt_str.push(',');
}
if include_rep && include_scale {
write!(&mut fmt_str, "{rep}({curr_scale}p").expect("writing to a string should not fail");
} else if include_rep {
write!(&mut fmt_str, "{rep}").expect("writing to a string should not fail");
}
write!(&mut fmt_str, "{el_str}").expect("writing to a string should not fail");
if include_rep && include_scale {
write!(&mut fmt_str, ")").expect("writing to a string should not fail");
}
}
fmt_str.push(')');
fmt_str
}
}
fn consume_width_from_pair(pair: Pair<Rule>) -> Option<u32> {
let mut stack: Vec<_> = pair.into_inner().rev().collect();
consume_width(&mut stack)
}
fn consume_width(stack: &mut Vec<Pair<Rule>>) -> Option<u32> {
if let Rule::width = stack.last()?.as_rule() {
let w: u32 = stack.pop().unwrap().as_str().parse().unwrap();
Some(w)
}else {
None
}
}
fn _consume_prec_from_pair(pair: Pair<Rule>) -> Option<u32> {
let mut stack: Vec<_> = pair.into_inner().rev().collect();
consume_prec(&mut stack)
}
fn consume_prec(stack: &mut Vec<Pair<Rule>>) -> Option<u32> {
if let Rule::prec = stack.last()?.as_rule() {
let p: u32 = stack.pop().unwrap().as_str().parse().unwrap();
Some(p)
}else {
None
}
}
fn consume_signed_from_pair(pair: Pair<Rule>) -> Option<i32> {
let mut stack: Vec<_> = pair.into_inner().rev().collect();
consume_signed(&mut stack)
}
fn consume_signed(stack: &mut Vec<Pair<Rule>>) -> Option<i32> {
if let Rule::signed = stack.last()?.as_rule() {
let v: i32 = stack.pop().unwrap().as_str().parse().unwrap();
Some(v)
}else{
None
}
}
fn consume_width_and_prec(stack: &mut Vec<Pair<Rule>>) -> (Option<u32>, Option<u32>) {
let width = if let Some(w) = consume_width(stack) {
Some(w)
}else{
return (None, None)
};
let prec = consume_prec(stack);
(width, prec)
}
fn consume_req_width_and_prec_from_pair(pair: Pair<Rule>, _kind: &str) -> PResult<(u32, Option<u32>)> {
let mut stack: Vec<_> = pair.into_inner().rev().collect();
consume_req_width_and_prec(&mut stack, _kind)
}
fn consume_req_width_and_prec(stack: &mut Vec<Pair<Rule>>, _kind: &str) -> PResult<(u32, Option<u32>)> {
let (w_opt, p) = consume_width_and_prec(stack);
let w = w_opt.ok_or_else(|| PError)?;
Ok((w, p))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_surrounding_whitespace() -> PResult<()> {
FortFormat::parse(" (a) \n")?;
FortFormat::parse(" (a) \r")?;
FortFormat::parse(" (a) \r\n")?;
Ok(())
}
#[test]
fn test_char() -> PResult<()> {
let v = FortFormat::parse("(a)")?.into_fields().unwrap();
assert_eq!(v.len(), 1, "Parsing '(a)' did not return exactly 1 field");
assert_eq!(v.last().unwrap(), &FortField::Char { width: None }, "Parsing '(a)' failed");
let v = FortFormat::parse("(a16)")?.into_fields().unwrap();
assert_eq!(v.len(), 1, "Parsing '(a16)' did not return exactly 1 field");
assert_eq!(v.last().unwrap(), &FortField::Char { width: Some(16) }, "Parsing '(a16)' failed");
let e = FortFormat::parse("(a-16)");
assert!(e.is_err(), "Parsing '(a-16)' (negative width) did not return an error");
Ok(())
}
#[test]
fn test_logical() -> PResult<()> {
let v = FortFormat::parse("(l1)")?.into_fields().unwrap();
assert_eq!(v.len(), 1, "Parsing '(l1)' did not return exactly 1 field");
assert_eq!(v.last().unwrap(), &FortField::Logical { width: 1 }, "Parsing '(l1)' failed");
let e = FortFormat::parse("(l)");
assert!(e.is_err(), "Parsing '(l)' (no width) did not return an error");
let e = FortFormat::parse("(l-1)");
assert!(e.is_err(), "Parsing '(l-1)' (negative width) did not return an error");
Ok(())
}
#[test]
fn test_decimal() -> PResult<()> {
test_integer(IntBase::Decimal)
}
#[test]
fn test_octal() -> PResult<()> {
test_integer(IntBase::Octal)
}
#[test]
fn test_hex() -> PResult<()> {
test_integer(IntBase::Hexadecimal)
}
fn test_integer(base: IntBase) -> PResult<()> {
let v = FortFormat::parse(&format!("({base}8)"))?.into_fields().unwrap();
assert_eq!(v.len(), 1, "Parsing '({base}8)' did not return exactly 1 field");
assert_eq!(v.last().unwrap(), &FortField::Integer { width: 8, zeros: None, base }, "Parsing '({base}8)' failed");
let v = FortFormat::parse(&format!("({base}8.6)"))?.into_fields().unwrap();
assert_eq!(v.len(), 1, "Parsing '(i{base}.6)' did not return exactly 1 field");
assert_eq!(v.last().unwrap(), &FortField::Integer { width: 8, zeros: Some(6), base }, "Parsing '({base}8.6)' failed");
let e = FortFormat::parse(&format!("({base})"));
assert!(e.is_err(), "Parsing '({base})' (no width) did not return an error");
let e = FortFormat::parse(&format!("({base}8.)"));
assert!(e.is_err(), "Parsing '({base}8.)' (missing precision width) did not return an error");
Ok(())
}
#[test]
fn test_float() -> PResult<()> {
test_real(RealFmt::F)
}
#[test]
fn test_double() -> PResult<()> {
test_real(RealFmt::D)
}
#[test]
fn test_exponential() -> PResult<()> {
test_real(RealFmt::E)
}
#[test]
fn test_gfloat() -> PResult<()> {
test_real(RealFmt::G)
}
fn test_real(fmt: RealFmt) -> PResult<()> {
let v = FortFormat::parse(&format!("({fmt}8)"))?.into_fields().unwrap();
assert_eq!(v.len(), 1, "Parsing '({fmt}8)' did not return exactly 1 field");
assert_eq!(v.last().unwrap(), &FortField::Real { width: 8, precision: None, fmt, scale: 0 }, "Parsing '({fmt}8)' failed");
let v = FortFormat::parse(&format!("({fmt}8.6)"))?.into_fields().unwrap();
assert_eq!(v.len(), 1, "Parsing '({fmt}8.6)' did not return exactly 1 field");
assert_eq!(v.last().unwrap(), &FortField::Real { width: 8, precision: Some(6), fmt, scale: 0 }, "Parsing '({fmt}8.6)' failed");
let v = FortFormat::parse(&format!("(2p{fmt}8.6)"))?.into_fields().unwrap();
assert_eq!(v.len(), 1, "Parsing '(2p{fmt}8.6)' did not return exactly 1 field");
assert_eq!(v.last().unwrap(), &FortField::Real { width: 8, precision: Some(6), fmt, scale: 2 }, "Parsing '(2p{fmt}8.6)' failed");
let e = FortFormat::parse(&format!("({fmt})"));
assert!(e.is_err(), "Parsing '({fmt})' (no width) did not return an error");
let e = FortFormat::parse(&format!("({fmt}8.)"));
assert!(e.is_err(), "Parsing '({fmt}8.)' (missing precision width) did not return an error");
Ok(())
}
#[test]
fn test_scales() -> PResult<()> {
let s = "(f7.2 2p f8.3 -3p f5.1 f6 0p f10.3)";
let v = FortFormat::parse(s)?.into_fields().unwrap();
let expected = vec![
FortField::Real { width: 7, precision: Some(2), fmt: RealFmt::F, scale: 0 },
FortField::Real { width: 8, precision: Some(3), fmt: RealFmt::F, scale: 2 },
FortField::Real { width: 5, precision: Some(1), fmt: RealFmt::F, scale: -3 },
FortField::Real { width: 6, precision: None, fmt: RealFmt::F, scale: -3 },
FortField::Real { width: 10, precision: Some(3), fmt: RealFmt::F, scale: 0 },
];
assert_eq!(v, expected, "Parsing {s} failed");
Ok(())
}
#[test]
fn test_elided_scale() -> PResult<()> {
let v = FortFormat::parse("(-2pf13.5)")?.into_fields().unwrap();
let expected = [FortField::Real { width: 13, precision: Some(5), fmt: RealFmt::F, scale: -2 }];
assert_eq!(v, expected);
Ok(())
}
#[test]
fn test_simple_repeats() -> PResult<()> {
let s = "(3i8 2f7.2)";
let v = FortFormat::parse(s)?.into_fields().unwrap();
let expected = vec![
FortField::Integer { width: 8, zeros: None, base: IntBase::Decimal },
FortField::Integer { width: 8, zeros: None, base: IntBase::Decimal },
FortField::Integer { width: 8, zeros: None, base: IntBase::Decimal },
FortField::Real { width: 7, precision: Some(2), fmt: RealFmt::F, scale: 0 },
FortField::Real { width: 7, precision: Some(2), fmt: RealFmt::F, scale: 0 },
];
assert_eq!(v, expected, "Parsing {s} failed");
Ok(())
}
#[test]
fn test_sequence() -> PResult<()> {
let s = "(a32 2x l4 i8 f10.3 e11.4 d12.5 g7.2)";
let v = FortFormat::parse(s)?.into_fields().unwrap();
let expected = vec![
FortField::Char { width: Some(32) },
FortField::Skip,
FortField::Skip,
FortField::Logical { width: 4 },
FortField::Integer { width: 8, zeros: None, base: IntBase::Decimal },
FortField::Real { width: 10, precision: Some(3), fmt: RealFmt::F, scale: 0 },
FortField::Real { width: 11, precision: Some(4), fmt: RealFmt::E, scale: 0 },
FortField::Real { width: 12, precision: Some(5), fmt: RealFmt::D, scale: 0 },
FortField::Real { width: 7, precision: Some(2), fmt: RealFmt::G, scale: 0 },
];
assert_eq!(v, expected, "Parsing {s} failed");
Ok(())
}
#[test]
fn test_nested_one_level() -> PResult<()> {
let s = "(a32 2(1x f7.4))";
let v = FortFormat::parse(s)?.into_fields().unwrap();
let expected = vec![
FortField::Char { width: Some(32) },
FortField::Skip,
FortField::Real { width: 7, precision: Some(4), fmt: RealFmt::F, scale: 0 },
FortField::Skip,
FortField::Real { width: 7, precision: Some(4), fmt: RealFmt::F, scale: 0 },
];
assert_eq!(v, expected, "Parsing {s} failed");
Ok(())
}
#[test]
fn test_nested_two_level() -> PResult<()> {
let s = "(3(a8 1x 2(i4 1x f7.4 1x)))";
let v = FortFormat::parse(s)?.into_fields().unwrap();
let expected = vec![
FortField::Char { width: Some(8) },
FortField::Skip,
FortField::Integer { width: 4, zeros: None, base: IntBase::Decimal },
FortField::Skip,
FortField::Real { width: 7, precision: Some(4), fmt: RealFmt::F, scale: 0 },
FortField::Skip,
FortField::Integer { width: 4, zeros: None, base: IntBase::Decimal },
FortField::Skip,
FortField::Real { width: 7, precision: Some(4), fmt: RealFmt::F, scale: 0 },
FortField::Skip,
];
let expected = expected.repeat(3);
assert_eq!(v, expected, "Parsing {s} failed");
Ok(())
}
#[test]
fn test_grouping_by_one() -> PResult<()> {
let s = "(4i3,1x,a8,2x,2a4,1x,f13.8)";
let ff = FortFormat::parse(&s)?;
let grouped = ff.collapsed_version(1);
let expected = vec![
(4, FortField::Integer { width: 3, zeros: None, base: IntBase::Decimal }),
(1, FortField::Skip),
(1, FortField::Char { width: Some(8) }),
(2, FortField::Skip),
(2, FortField::Char { width: Some(4) }),
(1, FortField::Skip),
(1, FortField::Real { width: 13, precision: Some(8), fmt: RealFmt::F, scale: 0 })
];
assert_eq!(grouped, expected);
Ok(())
}
#[test]
fn test_grouped_fmt_str() -> PResult<()> {
let sorig = "(4i3,1x,a8,2x,2a4,1x,f13.8)";
let ff = FortFormat::parse(&sorig)?;
let stest = ff.fmt_string(1);
assert_eq!(sorig, stest);
Ok(())
}
#[test]
fn test_grouped_fmt_str_with_scale() -> PResult<()> {
let sorig = "(4i3,2x,42(1pe13.8))";
let ff = FortFormat::parse(&sorig)?;
let stest = ff.fmt_string(1);
assert_eq!(sorig, stest);
Ok(())
}
#[test]
fn ggg_runlog_format() -> PResult<()> {
let s = "(a1,a57,1x,2i4,f8.4,f8.3,f9.3,2f8.3,1x,f6.4,f8.3,f7.3,f7.2,3(1x,f5.4),2i9,1x,f14.11,i9,i3,1x,f5.3,i5,1x,a2,2(f6.1,f8.2,f5.1),f7.1,f7.4,f6.1,f6.0,f10.3,f7.0,f7.3)";
FortFormat::parse(s)?;
Ok(())
}
#[test]
fn test_set_field() {
let mut ff1 = FortFormat::parse("(i2)").unwrap();
assert_eq!(ff1.get_field(0), Some(&FortField::Integer { width: 2, zeros: None, base: IntBase::Decimal }));
ff1.set_field(0, FortField::Char { width: Some(9) }).unwrap();
assert_eq!(ff1.get_field(0), Some(&FortField::Char { width: Some(9) }));
let res = ff1.set_field(1, FortField::Skip);
assert!(res.is_err());
let mut ff2 = FortFormat::ListDirected;
let res = ff2.set_field(0, FortField::Skip);
assert!(res.is_err());
}
#[test]
fn test_insert_field() {
let mut ff = FortFormat::parse("(i2,f13.8)").unwrap();
assert_eq!(ff.get_field(0), Some(&FortField::Integer { width: 2, zeros: None, base: IntBase::Decimal }));
assert_eq!(ff.get_field(1), Some(&FortField::Real { width: 13, precision: Some(8), fmt: RealFmt::F, scale: 0 }));
ff.insert_field(0, FortField::Char { width: Some(9) }).unwrap();
assert_eq!(ff.get_field(0), Some(&FortField::Char { width: Some(9) }));
assert_eq!(ff.get_field(1), Some(&FortField::Integer { width: 2, zeros: None, base: IntBase::Decimal }));
assert_eq!(ff.get_field(2), Some(&FortField::Real { width: 13, precision: Some(8), fmt: RealFmt::F, scale: 0 }));
let mut ff = FortFormat::parse("(i2,f13.8)").unwrap();
ff.insert_field(2, FortField::Char { width: Some(9) }).unwrap();
assert_eq!(ff.get_field(0), Some(&FortField::Integer { width: 2, zeros: None, base: IntBase::Decimal }));
assert_eq!(ff.get_field(1), Some(&FortField::Real { width: 13, precision: Some(8), fmt: RealFmt::F, scale: 0 }));
assert_eq!(ff.get_field(2), Some(&FortField::Char { width: Some(9) }));
let mut ff = FortFormat::parse("(i2,f13.8)").unwrap();
let res = ff.set_field(3, FortField::Skip);
assert!(res.is_err());
let mut ff = FortFormat::ListDirected;
let res = ff.set_field(0, FortField::Skip);
assert!(res.is_err());
}
}