Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "rust"
version = "0.1.0"
edition = "2021"

[dependencies]
csv = "1.1"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
32 changes: 31 additions & 1 deletion rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,37 @@

## read_TBE

(Add your content here)
### Getting Started

#### Prerequisites

- **Rust**: Make sure you have [Rust](https://www.rust-lang.org/learn/get-started) installed.
- **Cargo**: Rust's package manager, which is bundled with the Rust installation.
- **Install VS Code extensions**: Rust Extension Pack and rust-analyzer and Cargo
- **Check environment variables**: /home/<your-username>/.cargo/bin (for example)
- Check the installations:
rustc --version and cargo --version


### Installation

1. Clone the repository to your local machine: git clone https://github.com/oss-slu/tbe.git
2. Go to tbe/rust/
3. Initialize a New Cargo Project - cargo init (if it's not existed)
4. This will create a Cargo.toml file
5. Then , the folder structure should be like
rust/
├── Cargo.toml
├── src/
│ └── main.rs
6. Ensure you've the necessary dependencies in Cargo.toml
7. Change the path to your sample_data/example.csv as required
8. Run the project from the rust/ directory using - cargo run
9. When you run it'll build the project - Cargo.lock and target/ directory
10. If you already have Cargo.lock and no target/ dir then use " cargo build " to build the project




## strip_header

Expand Down
1 change: 1 addition & 0 deletions rust/src/functions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod read_tbe; // Expose the read_TBE module
90 changes: 90 additions & 0 deletions rust/src/functions/read_TBE.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead, BufReader};

#[derive(Debug, Default)]
pub struct Table {
pub data: Vec<HashMap<String, String>>, // Main table data
pub att: HashMap<String, Vec<String>>, // Attribute data linked to table
pub cmt: HashMap<String, Vec<String>>, // Comment data linked to table
}

pub fn parse_tbe(file_path: &str) -> io::Result<HashMap<String, Table>> {
let file = File::open(file_path)?;
let reader = BufReader::new(file);

let mut tables: HashMap<String, Table> = HashMap::new();
let mut current_table_name = String::new();
let mut headers: Vec<String> = Vec::new();
let mut capturing_data = false;

for line in reader.lines() {
let line = line?;
let line = line.trim();

// Handle table section
if line.starts_with("TBL") {
let parts: Vec<&str> = line.split(',').collect();
if parts.len() > 1 {
current_table_name = parts[0].split_whitespace().nth(1).unwrap_or_default().to_string();
headers = parts.iter().skip(1).map(|s| s.trim().to_string()).collect();

println!("Parsed table: {} with headers: {:?}", current_table_name, headers); // Debug log
tables.entry(current_table_name.clone()).or_insert_with(Table::default);
}
}
// Start reading table data
else if line.starts_with("BGN") {
capturing_data = true;
}
// End of table data
else if line.starts_with("EOT") {
capturing_data = false;
}
// Handle ATT (Attribute) section
else if line.starts_with("ATT") {
let parts: Vec<&str> = line.split(',').collect();
if parts.len() > 2 {
let att_name = parts[1].trim().to_string();
let att_values: Vec<String> = parts.iter().skip(2).map(|s| s.trim().to_string()).collect();

if let Some(table) = tables.get_mut(&current_table_name) {
table.att.insert(att_name, att_values);
}
}
}
// Handle CMT (Comment) section
else if line.starts_with("CMT") {
let parts: Vec<&str> = line.split(',').collect();
if parts.len() > 2 {
let cmt_name = parts[1].trim().to_string();
let cmt_values: Vec<String> = parts.iter().skip(2).map(|s| s.trim().to_string()).collect();

if let Some(table) = tables.get_mut(&current_table_name) {
table.cmt.insert(cmt_name, cmt_values);
}
}
}
// Parse table rows
else if capturing_data {
let parts: Vec<&str> = line.split(',').collect();
if parts.len() == headers.len() {
let row_data: HashMap<String, String> = headers
.iter()
.zip(parts.iter().map(|s| s.trim().to_string()))
.map(|(h, v)| (h.clone(), v))
.collect();

// Push the cloned row into the table and log the captured row
if let Some(table) = tables.get_mut(&current_table_name) {
table.data.push(row_data.clone()); // Clone to avoid moving
println!("Captured row: {:?}", row_data); // Debug log
}
} else {
eprintln!("Skipping malformed row: {:?}", parts); // Debug log for malformed rows
}
}
}

Ok(tables)
}
29 changes: 29 additions & 0 deletions rust/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
mod functions; // Include the functions module
use functions::read_tbe::parse_tbe; // Import the parse_tbe function

use std::io; // Import io for Result
use std::path::PathBuf; // Import PathBuf for path management

mod models; // Import models where the Site struct is defined


fn main() -> io::Result<()> {
let file_path = PathBuf::from("../sample_data/saq_bluesky_bgd_20211001_20230430_inv_tbe.csv"); // Path to the sample TBE file
// let file_path = PathBuf::from("../sample_data/saq_bluesky_dku_20210715_20230131_inv_tbe.csv"); // Path to the sample TBE file
// let file_path = PathBuf::from("../sample_data/saq_bluesky_npl_20220830_20230404_inv_tbe.csv"); // Path to the sample TBE file
println!("Parsing TBE file: {}", file_path.display()); // Use .display() to print the PathBuf

let result = parse_tbe(file_path.to_str().unwrap());

match result {
Ok(sites) => {
// Now sites is a vector of `Site` structs
for site in sites {
println!("{:?}", site); // Print each site
}
}
Err(e) => eprintln!("Error parsing file: {}", e),
}

Ok(())
}
22 changes: 22 additions & 0 deletions rust/src/models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// models.rs
// use serde_derive::Deserialize; // Add the import for the macro

#[allow(dead_code)]
pub struct Site {
pub zone: String,
pub country: String,
pub sitename: String,
pub utc_offset: String,
pub pm25_scale: String,
pub pm25_offset: String,
pub serial_number: String,
pub plocation: String,
pub latitude: String,
pub longitude: String,
pub is_indoors: String,
pub measurements: String,
pub nrecords: String,
pub date_start: String,
pub date_end: String,
pub time: String,
}