hfss_fld 0.1.4

Ansys HFSS `.fld` file parser
Documentation
mod file_header_utils;

use std::fs::File;
use std::io::{prelude::BufRead, BufReader};
use std::path::Path;
use std::str::FromStr;

pub use file_header_utils::{DataCoordinates, DataDimensionality};

#[derive(Debug)]
pub struct FileData {
    pub file_name: String,
    pub geo_points: Vec<[f64; 3]>,
    pub fld_points: Vec<Vec<f64>>,
    pub dimensionality: DataDimensionality,
    pub coordinates: DataCoordinates,
    pub num_points: usize,
}

impl FileData {
    pub fn load_file(
        file_path: String,
        expected_field_data_size: usize,
    ) -> Result<Self, std::io::Error> {
        // file
        let os_file_path = Path::new(&file_path);
        let file_name = retrieve_file_name(&os_file_path);
        let file = File::open(os_file_path)?;

        // file reader buffer
        let file_reader = BufReader::new(file);

        let mut geo_points = Vec::new();
        let mut fld_points = Vec::new();

        let mut dimensionality = DataDimensionality::new();
        let mut coordinates = DataCoordinates::None;

        assert!(
            expected_field_data_size > 0,
            "FileData cannot expect zero sized data!"
        );

        for (i, line) in file_reader.lines().enumerate() {
            match line {
                Ok(line_text) => {
                    match i {
                        0 => {
                            // first header line -> geometric meta-data
                            dimensionality = match DataDimensionality::from_file_header(&line_text)
                            {
                                Ok(data_dimensionality) => data_dimensionality,
                                Err(msg) => panic!(
                                    "Problem Parsing Fld File: `{}` \n Err: {}",
                                    file_name, msg
                                ),
                            };
                            let num_points = dimensionality.total_num_points();

                            // reserve space for appropriate number of points
                            geo_points.reserve(num_points);
                            fld_points.reserve(num_points);
                        }
                        1 => {
                            // second header line -> field type and coordinate system
                            coordinates = match DataCoordinates::from_file_header(&line_text) {
                                Ok(data_coordinates) => data_coordinates,
                                Err(msg) => panic!(
                                    "Problem Parsing Fld File: `{}` \n Err: {}",
                                    file_name, msg
                                ),
                            };
                        }
                        _ => {
                            // remaining lines => field data points
                            let mut line_geo_points = [0.0; 3];
                            let mut line_fld_points = Vec::with_capacity(expected_field_data_size);

                            // split on double space to get text sections for [geo \s\s field] data
                            let line_text_sections = line_text.split("  ");
                            for (s, section) in line_text_sections.enumerate() {
                                // split on singe space to get individual numerical tokens
                                let tokens = section.split(' ');
                                for (t, token) in tokens.enumerate() {
                                    if token == "" {
                                        continue;
                                    }
                                    // attempt to parse each token as an f64
                                    match f64::from_str(token) {
                                        // populate appropriate array with geometric or field data
                                        Ok(value) => match s {
                                            0 => line_geo_points[t] = value,
                                            1 => {
                                                assert!(
                                                    t < expected_field_data_size,
                                                    "Unexpected token on line {} of {}",
                                                    i,
                                                    file_name
                                                );
                                                line_fld_points.push(value);
                                            }
                                            _ => {
                                                panic!(
                                                    "Unexpected token on line {} of {}",
                                                    i, file_name
                                                );
                                            }
                                        },
                                        Err(msg) => panic!(
                                            "Unable to parse value on line {} of {} as f64! \n {}",
                                            i, file_name, msg
                                        ),
                                    }
                                }
                            }

                            // populate data vectors with geo and fld data from line i
                            geo_points.push(line_geo_points);
                            fld_points.push(line_fld_points);
                        }
                    }
                }
                Err(msg) => panic!("Unable to read line {} of {} \n {}", i, file_name, msg),
            }
        }

        geo_points.shrink_to_fit();
        fld_points.shrink_to_fit();

        let num_points = geo_points.len();
        assert_eq!(
            num_points,
            fld_points.len(),
            "Problem Parsing Fld File: `{}` \n Inconsistent Number of geometric and data points!",
            file_name
        );

        dark_yellow!("{} \t", num_points);
        print!("Data Points successfully loaded from: ");
        dark_grey_ln!("`{}`", file_name);

        Ok(Self {
            file_name,
            geo_points,
            fld_points,
            dimensionality,
            coordinates,
            num_points,
        })
    }
}

fn retrieve_file_name(os_file_path: &Path) -> String {
    let name = os_file_path.file_name();

    match name {
        Some(file_name) => file_name.to_string_lossy().into_owned(),
        None => String::from("-UNNAMED FLD FILE-"),
    }
}