Skip to content

Commit

Permalink
Dropped data iterators since they seemed like an unnecessary abstract…
Browse files Browse the repository at this point in the history
…ion. Switched focus to headers for the bluejay utility. Still needs extended header support and support for additional bluefile type codes.
  • Loading branch information
billallen256 committed Oct 25, 2024
1 parent 4185ad0 commit 18c8367
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 576 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bluefile"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
license = "Apache-2.0"
readme = "README.md"
Expand Down
31 changes: 1 addition & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,7 @@ Add the following to your project's `Cargo.toml`:
bluefile = "*"
```

### Reading Type 2000 frames

```rust
use std::path::PathBuf;

use bluefile::bluefile::BluefileReader;
use bluefile::data_type::DataValue;
use bluefile::type2000::Type2000Reader;

fn main() {
let args: Vec<String> = std::env::args().collect();
let path = PathBuf::from(&args[1]);
let reader = Type2000Reader::new(&path).unwrap();
let adj_header = &reader.get_adj_header();
let frame_size: usize = adj_header.subsize.try_into().unwrap();
let data_reader = &mut reader.get_data_iter().unwrap();

loop {
let frame: Vec<DataValue> = data_reader.take(frame_size).collect();

if frame.len() < frame_size {
break;
}

dbg!(frame);
}
}
```

More examples can be found in the `tests` directory.
Examples can be found in the `tests` directory.

## Running Tests

Expand Down
39 changes: 2 additions & 37 deletions src/bluefile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::endian::Endianness;
use crate::error::Error;
use crate::header::Header;
use crate::result::Result;
use crate::util::{bytes_to_i16, bytes_to_i32, open_file};
use crate::util::{bytes_to_i16, bytes_to_i32};

pub(crate) const ADJUNCT_HEADER_OFFSET: usize = 256;
pub(crate) const ADJUNCT_HEADER_SIZE: usize = 256;
Expand Down Expand Up @@ -54,8 +54,7 @@ pub struct ExtHeaderIter {

/// Additional functions for the extended header iterator.
impl ExtHeaderIter {
fn new(path: PathBuf, offset: usize, size: usize, endianness: Endianness) -> Result<Self> {
let file = open_file(&path)?;
fn new(file: File, offset: usize, size: usize, endianness: Endianness) -> Result<Self> {
let mut reader = BufReader::new(file);

match reader.seek(SeekFrom::Start(offset as u64)) {
Expand Down Expand Up @@ -100,40 +99,6 @@ impl Iterator for ExtHeaderIter {
}
}

/// Defines a trait that all bluefile readers should implement.
pub trait BluefileReader {
type AdjHeader;
type DataIter;

fn new<P: AsRef<Path>>(path: P) -> Result<Self> where Self: Sized;
fn get_header(&self) -> Header;
fn get_ext_size(&self) -> usize;
fn get_ext_start(&self) -> usize;
fn get_ext_path(&self) -> PathBuf;
fn get_adj_header(&self) -> Self::AdjHeader;
fn get_data_path(&self) -> PathBuf;
fn get_data_start(&self) -> usize;
fn get_data_size(&self) -> usize;
fn get_header_endianness(&self) -> Endianness;
fn get_data_endianness(&self) -> Endianness;

fn get_ext_iter(&self) -> Result<ExtHeaderIter> {
ExtHeaderIter::new(
self.get_ext_path(),
self.get_ext_start(),
self.get_ext_size(),
self.get_header_endianness(),
)
}

fn get_data_iter(&self) -> Result<Self::DataIter>;
}

//fn new_reader(path: &Path) -> Result<Box<dyn BluefileReader>> {
// let file = open_file(path)?;
// let header = read_header(&file);
//}

/// Raw extended header keyword properties.
pub struct ExtKeyword {
pub length: usize,
Expand Down
103 changes: 77 additions & 26 deletions src/bluejay.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
use std::env;
use std::fs::File;
use std::path::PathBuf;
use std::process::exit;

use bluefile::header::read_header;
use bluefile::bluefile::TypeCode;
use bluefile::header::{
Header,
read_header,
read_type1000_adjunct_header,
read_type2000_adjunct_header,
};
use bluefile::error::Error;
use bluefile::result::Result;
use bluefile::util::open_file;

struct Config {
file: File,
path: PathBuf,
}

Expand All @@ -16,20 +23,78 @@ fn get_config() -> Result<Config> {

if args.len() != 2 {
println!("Configuration error");
return Err(Error::BluestatConfigError);
return Err(Error::BluejayConfigError);
}

let path_str = args[1].trim();

if path_str.len() == 0 {
println!("Bluefile path is empty string");
return Err(Error::BluestatConfigError);
return Err(Error::BluejayConfigError);
}

let mut path_buf = PathBuf::new();
path_buf.push(path_str);

Ok(Config{path: path_buf})
let file = match File::open(&path_buf) {
Ok(x) => x,
Err(_) => return Err(Error::FileOpenError(path_buf.display().to_string())),
};

Ok(Config{
file: file,

Check warning on line 45 in src/bluejay.rs

View workflow job for this annotation

GitHub Actions / test

redundant field names in struct initialization

warning: redundant field names in struct initialization --> src/bluejay.rs:45:9 | 45 | file: file, | ^^^^^^^^^^ help: replace it with: `file` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names = note: `#[warn(clippy::redundant_field_names)]` on by default
path: path_buf,
})
}

fn header_lines(header: &Header, lines: &mut Vec<String>) {
lines.push(format!(" \"type_code\": \"{}\"", header.type_code));
lines.push(format!(" \"header_endianness\": \"{}\"", header.header_endianness));
lines.push(format!(" \"data_endianness\": \"{}\"", header.data_endianness));
lines.push(format!(" \"ext_header_start\": {}", header.ext_start));
lines.push(format!(" \"ext_header_size\": {}", header.ext_size));
lines.push(format!(" \"data_start\": {}", header.data_start));
lines.push(format!(" \"data_size\": {}", header.data_size));
lines.push(format!(" \"data_type\": \"{}\"", header.raw_data_type));
lines.push(format!(" \"data_rank\": \"{}\"", header.data_type.rank));
lines.push(format!(" \"data_format\": \"{}\"", header.data_type.format));
lines.push(format!(" \"timecode\": {}", header.timecode));
}

fn adjunct_lines(file: &File, header: &Header, lines: &mut Vec<String>) {
if header.type_code == TypeCode::Type1000(1000) {
let adj = match read_type1000_adjunct_header(file, header) {
Ok(a) => a,
Err(_) => {
println!("Error reading adjunct header");
return;
}
};

lines.push(format!(" \"xstart\": {}", adj.xstart));
lines.push(format!(" \"xdelta\": {}", adj.xdelta));
lines.push(format!(" \"xunits\": {}", adj.xunits));
return;
}

if header.type_code == TypeCode::Type2000(2000) {
let adj = match read_type2000_adjunct_header(file, header) {
Ok(a) => a,
Err(_) => {
println!("Error reading adjunct header");
return;
}
};

lines.push(format!(" \"xstart\": {}", adj.xstart));
lines.push(format!(" \"xdelta\": {}", adj.xdelta));
lines.push(format!(" \"xunits\": {}", adj.xunits));
lines.push(format!(" \"subsize\": {}", adj.subsize));
lines.push(format!(" \"ystart\": {}", adj.ystart));
lines.push(format!(" \"ydelta\": {}", adj.ydelta));
lines.push(format!(" \"yunits\": {}", adj.yunits));
return;
}
}

fn main() {
Expand All @@ -38,34 +103,20 @@ fn main() {
Err(_) => exit(1),
};


let file = match open_file(&config.path) {
Ok(f) => f,
Err(_) => {
println!("Could not open file at {}", config.path.display());
exit(1);
},
};

let header = match read_header(&file) {
let header = match read_header(&config.file) {
Ok(h) => h,
Err(_) => {
println!("Could not read header from {}", config.path.display());
exit(1);
},
};

let mut lines: Vec<String> = vec![];
header_lines(&header, &mut lines);
adjunct_lines(&config.file, &header, &mut lines);
let all_lines = lines.join(",\n");

println!("{{");
println!(" \"type_code\": \"{}\",", header.type_code);
println!(" \"header_endianness\": \"{}\",", header.header_endianness);
println!(" \"data_endianness\": \"{}\",", header.data_endianness);
println!(" \"ext_header_start\": {},", header.ext_start);
println!(" \"ext_header_size\": {},", header.ext_size);
println!(" \"data_start\": {},", header.data_start);
println!(" \"data_size\": {},", header.data_size);
println!(" \"data_type\": \"{}\",", header.raw_data_type);
println!(" \"data_rank\": \"{}\",", header.data_type.rank);
println!(" \"data_format\": \"{}\",", header.data_type.format);
println!(" \"timecode\": {}", header.timecode);
println!("{}", all_lines);
println!("}}");
}
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ pub enum Error {
ExtHeaderKeywordLengthParseError,
ExtHeaderKeywordReadError,
DataSeekError,
BluestatConfigError,
BluejayConfigError,
}
88 changes: 87 additions & 1 deletion src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ use std::io::Seek;
use std::io::SeekFrom;
use std::str::from_utf8;

use crate::bluefile::TypeCode;
use crate::bluefile::{
ADJUNCT_HEADER_OFFSET,
ADJUNCT_HEADER_SIZE,
TypeCode,
};
use crate::data_type::{DataType, Format, Rank};
use crate::endian::Endianness;
use crate::error::Error;
Expand Down Expand Up @@ -40,6 +44,24 @@ pub struct Header {
pub keywords: Vec<HeaderKeyword>,
}

#[derive(Clone, Debug)]
pub struct Type1000Adjunct {
pub xstart: f64,
pub xdelta: f64,
pub xunits: i32,
}

#[derive(Clone, Debug)]
pub struct Type2000Adjunct {
pub xstart: f64,
pub xdelta: f64,
pub xunits: i32,
pub subsize: i32,
pub ystart: f64,
pub ydelta: f64,
pub yunits: i32,
}

fn is_blue(v: &[u8]) -> bool {
v[0] == b'B' && v[1] == b'L' && v[2] == b'U' && v[3] == b'E'
}
Expand Down Expand Up @@ -164,3 +186,67 @@ fn parse_type_code(v: &[u8], endianness: Endianness) -> Result<TypeCode> {
Err(Error::UnknownFileTypeCode(t))
}
}

pub fn read_type1000_adjunct_header(mut file: &File, header: &Header) -> Result<Type1000Adjunct> {
match file.seek(SeekFrom::Start(ADJUNCT_HEADER_OFFSET as u64)) {
Ok(x) => x,
Err(_) => return Err(Error::AdjunctHeaderSeekError),
};

let mut data = vec![0_u8; ADJUNCT_HEADER_SIZE];
let n = match file.read(&mut data) {
Ok(x) => x,
Err(_) => return Err(Error::FileReadError),
};

if n < ADJUNCT_HEADER_SIZE {
return Err(Error::NotEnoughAdjunctHeaderBytes(n))
}

let endianness = header.header_endianness;
let xstart: f64 = bytes_to_f64(&data[0..8], endianness)?;
let xdelta: f64 = bytes_to_f64(&data[8..16], endianness)?;
let xunits: i32 = bytes_to_i32(&data[16..20], endianness)?;

Ok(Type1000Adjunct{
xstart,
xdelta,
xunits,
})
}

pub fn read_type2000_adjunct_header(mut file: &File, header: &Header) -> Result<Type2000Adjunct> {
match file.seek(SeekFrom::Start(ADJUNCT_HEADER_OFFSET as u64)) {
Ok(x) => x,
Err(_) => return Err(Error::AdjunctHeaderSeekError),
};

let mut data = vec![0_u8; ADJUNCT_HEADER_SIZE];
let n = match file.read(&mut data) {
Ok(x) => x,
Err(_) => return Err(Error::FileReadError),
};

if n < ADJUNCT_HEADER_SIZE {
return Err(Error::NotEnoughAdjunctHeaderBytes(n))
}

let endianness = header.header_endianness;
let xstart: f64 = bytes_to_f64(&data[0..8], endianness)?;
let xdelta: f64 = bytes_to_f64(&data[8..16], endianness)?;
let xunits: i32 = bytes_to_i32(&data[16..20], endianness)?;
let subsize: i32 = bytes_to_i32(&data[20..24], endianness)?;
let ystart: f64 = bytes_to_f64(&data[24..32], endianness)?;
let ydelta: f64 = bytes_to_f64(&data[32..40], endianness)?;
let yunits: i32 = bytes_to_i32(&data[40..44], endianness)?;

Ok(Type2000Adjunct{
xstart,
xdelta,
xunits,
subsize,
ystart,
ydelta,
yunits,
})
}
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@ pub mod error;
pub mod endian;
pub mod header;
pub mod result;
pub mod type1000;
pub mod type2000;
pub mod util;
Loading

0 comments on commit 18c8367

Please sign in to comment.