ground_motion_lib/readers.rs
1//! # Data Readers for Site Input Files
2//!
3//! This module provides utilities for reading site-specific input data into the ground motion
4//! prediction library. It focuses on deserializing tabular files into [`Vs30Point`] instances
5//! for use in GMPE calculations.
6//!
7//! ## Features
8//!
9//! - Load site location and site condition data (longitude, latitude, Vs30, basin depth, and xvf flag).
10//! - Support for configurable CSV delimiter characters (e.g., tab, comma).
11//! - Assumes no header row in input files.
12//!
13//! ## Primary Functions
14//!
15//! - [`read_vs30_points`]: Reads a delimited text file into a vector of [`Vs30Point`] instances.
16//!
17//! ## Example File Format (tab-delimited)
18//!
19//! ```text
20//! 142.523 52.913 300 250 1
21//! 142.600 50.100 350 150 0
22//! ```
23//!
24//! Columns are interpreted as:
25//!
26//! 1. longitude (f64)
27//! 2. latitude (f64)
28//! 3. Vs30 (f64)
29//! 4. basin depth (optional, f64)
30//! 5. xvf flag (optional, u8)
31//!
32//! ## See Also
33//!
34//! - [`crate::gmm::Vs30Point`]
35//! - [`csv`](https://docs.rs/csv/)
36//!
37//! ## Errors
38//!
39//! This module returns boxed errors for I/O issues or data deserialization failures.
40
41use crate::gmm::Vs30Point;
42use csv::ReaderBuilder;
43use std::error::Error;
44use std::fs::File;
45use std::path::Path;
46
47/// Reads a list of [`Vs30Point`] instances from a delimited text file.
48///
49/// This function loads site-specific input points for ground motion prediction models from a
50/// file. Each line in the file is parsed and deserialized into a [`Vs30Point`] instance, which
51/// are collected into a `Vec`.
52///
53/// The file is assumed to have **no header row**, and the delimiter can be specified to support
54/// flexible file formats (e.g., tab, comma, space).
55///
56/// # Type Parameters
57///
58/// * `P` — A type convertible to a [`Path`] reference (e.g., `&str`, `PathBuf`).
59///
60/// # Arguments
61///
62/// * `path` — Path to the input file.
63/// * `delim` — Delimiter character (e.g., `b'\t'` for tab, `b','` for comma).
64///
65/// # Returns
66///
67/// A `Result` containing a vector of [`Vs30Point`] instances if successful, or a boxed error
68/// if file I/O or parsing fails.
69///
70/// # Example
71///
72/// ```rust
73/// use ground_motion_lib::readers::read_vs30_points;
74///
75/// let points = read_vs30_points("tests/data/testvs30.txt", b'\t').unwrap();
76/// println!("First point: {:?}", points[0]);
77/// ```
78///
79/// # Errors
80///
81/// Returns an error if:
82/// - The file cannot be opened.
83/// - Any row in the file fails to deserialize into a [`Vs30Point`].
84pub fn read_vs30_points<P: AsRef<Path>>(
85 path: P,
86 delim: u8,
87) -> Result<Vec<Vs30Point>, Box<dyn Error>> {
88 let file = File::open(path)?;
89 let mut rdr = ReaderBuilder::new()
90 .delimiter(delim)
91 .has_headers(false)
92 .from_reader(file);
93
94 let mut points = Vec::new();
95
96 for result in rdr.deserialize() {
97 let record: Vs30Point = result?;
98 points.push(record);
99 }
100
101 Ok(points)
102}