Files
wfdb_corrosion/src/main.rs
2025-10-14 12:01:21 +02:00

154 lines
4.9 KiB
Rust

use std::{env::{self}, os::linux::raw, path::Path, str::FromStr};
use std::fs;
pub mod headparse; // The HEAder parsing
pub mod signal_data;
use crate::{headparse::{Header, SignalSpec}, signal_data::SignalData};
/// Use for handling possible formats of the WFDB data
#[derive(Debug, Clone, Copy, PartialEq)]
enum SignalFormat {
Format16 = 16,
Format212 = 212,
Invalid = -1,
Unimpl = 0
}
fn main() -> Result<(), String>{
let args: Vec<String> = env::args().collect();
if args.len() <= 1 || args.contains(&"help".to_string()) {
help();
return Ok(());
}
let filepath = Path::new(&args[1]);
if !filepath.is_file() {
println!("Path argument provided is not a valid file");
return Ok(());
}
if filepath.extension().unwrap() != "hea" {
println!("File provided is not a .hea file");
return Ok(());
}
// Parse the header information
let header: Header;
{
let hea_file_result = fs::read_to_string(filepath);
match hea_file_result {
Ok(file_data) => {
match headparse::parse_header(file_data.as_str()) {
Ok(h) => {header = h}
Err(e) => {return Err(e.to_string())}
}
}
Err(e) => {return Err("error: Provided file cannot be opened".to_string())}
}
}
let path_parent: String;
{
// This is cursed lol
match filepath.parent() {
Some(p) => {
match p.to_str() {
Some(str) => {
path_parent = str.to_string();
}
None => {path_parent = "".to_string()}
}
}
None => {path_parent = "".to_string()}
}
}
let parsed_signal_result = get_all_data(header, path_parent);
println!("Hello, world!");
Ok(())
}
fn help() {
println!("Conversion of WFDB files to a more human readable format. By default to a CSV.");
println!("\nUse in the format \"wfdb_corrosion (.hea filename)\"")
}
fn get_signal_data(spec: &SignalSpec, offset: u64, data_length: usize, raw: &Vec<u64>) -> SignalData {
todo!()
}
fn get_all_data(header: Header, header_root: String) -> Result<Vec<SignalData>, String> {
let signal_counts = header.signals_per_file();
for (fname, sigcount) in signal_counts {
let mut full = header_root.to_owned(); // Consider refactoring these to avoid this massive amount of ownership bs
full.push('/');
let fname = fname.to_owned();
full.push_str(&fname);
let filepath = Path::new(&full);
if !filepath.is_file() {
return Err("Error: Could not open signal .dat file. Check if the
header points to the right .dat file and if files are present".to_string());
}
if filepath.extension().unwrap() != "dat" {
println!("warn: Header file defines signal files with an extension
other than .dat.
Check if the header points to the right files, reading will continue
but correct data is not guaranteed");
}
// Time to actually do the work
let signals = header.signals_in_file(fname);
let format = file_signal_format_check(signals);
if format == SignalFormat::Invalid {
return Err("Error: Signals in single file use multiple formats.
Is your header file correctly formatted?".to_string());
}
if format == SignalFormat::Unimpl {
println!("Warn: Signals in single file use an unimplemented format,
and will be ignored");
continue;
}
let raw_bytes = fs::read(filepath).unwrap();
match format {
SignalFormat::Format16 => {
let mut buffer: u64 = 0;
let mut do_append = false;
let mut raw_converted: Vec<u64> = Vec::with_capacity(raw_bytes.len() / 2);
for byte in raw_bytes {
if do_append {
buffer = (buffer & 0xFF) + (byte as u64) << 8;
raw_converted.push(buffer);
do_append = false;
} else {
buffer = byte as u64;
do_append = true;
}
}
dbg!(raw_converted);
},
_ => {
panic!("Somehow reached actual file and format processing with
unimplemented format. Please make a bug report")
}
}
}
todo!()
}
fn file_signal_format_check(signals: Vec<&SignalSpec>) -> SignalFormat {
let first_sig = signals[0];
let format = first_sig.format;
for sig in signals {
if sig.format != format {
return SignalFormat::Invalid;
}
}
return format;
}