@sjcrh/proteinpaint-rust 2.195.0 → 2.196.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/readH5.rs DELETED
@@ -1,781 +0,0 @@
1
- // readH5.rs - validate/read HDF5 file
2
- //
3
- // READ:
4
- // Extracts matrix from HDF5 files.
5
- // Matrix dataset is hardcoded as "matrix". row_dataset(samples) and col_dataset(genesets)
6
- // row_dataset and col_dataset are stored as VarLenUnicode.
7
- // Supports f32, f64, i32 and i64 matrix datatypes.
8
- //
9
- // Features:
10
- // - Hardcoded "matrix" dataset
11
- // - Supports f32, f64, i32 and i64 matrix datatypes
12
- // - Parallel processing with dynamic thread count
13
- // - JSON output with timing metrics
14
-
15
- // Usage
16
- // echo '{"query":["HALLMARK_ADIPOGENESIS", "HALLMARK_ANGIOGENESIS"],"hdf5_file":"matrix.h5"}' | ./target/release/readH5
17
- //
18
- //
19
- // VLIDATE:
20
- // output: JSON with {"samples":[]}
21
- // Usage
22
- // echo '{"validate":true,"hdf5_file":"matrix.h5"}' | ./target/release/readH5
23
-
24
- use hdf5::types::VarLenUnicode;
25
- use hdf5::{File, Result, Selection};
26
- use json::JsonValue;
27
- use ndarray::Dim;
28
- use rayon::prelude::*;
29
- use serde_json::{Map, Value, json};
30
- use std::io;
31
- use std::sync::Arc;
32
- use std::time::Instant;
33
-
34
- /// Creates an error JSON response
35
- fn error_response(message: impl Into<String>) -> Value {
36
- json!({
37
- "status": "error",
38
- "message": message.into()
39
- })
40
- }
41
-
42
- /// **Bulk mode**: Reads the entire matrix once, then extracts requested rows (genes).
43
- /// Much faster than many small slice reads when querying many genes (>50–100).
44
- pub fn bulk_query_hdf5(filename: &str, query_items: &[String]) -> Result<Map<String, Value>> {
45
- let start = Instant::now();
46
-
47
- let file = match File::open(filename) {
48
- Ok(f) => f,
49
- Err(err) => {
50
- return Err(hdf5::Error::Internal(format!("Failed to open HDF5 file: {}", err)));
51
- }
52
- };
53
-
54
- // Read all items (genes)
55
- let items_dataset = match file.dataset("item") {
56
- Ok(ds) => ds,
57
- Err(err) => {
58
- return Err(hdf5::Error::Internal(format!("Failed to open items dataset: {}", err)));
59
- }
60
- };
61
-
62
- // Read items as VarLenAscii
63
- let items_varlen = match items_dataset.read_1d::<VarLenUnicode>() {
64
- Ok(g) => g,
65
- Err(err) => {
66
- return Err(hdf5::Error::Internal(format!("Failed to read items(genes): {}", err)));
67
- }
68
- };
69
-
70
- let item_names: Vec<String> = items_varlen.iter().map(|g| g.to_string()).collect();
71
- //let num_items = item_names.len();
72
-
73
- // Read all samples names ("samples")
74
- let samples_dataset = match file.dataset("samples") {
75
- Ok(ds) => ds,
76
- Err(err) => {
77
- return Err(hdf5::Error::Internal(format!(
78
- "Failed to open samples dataset: {}",
79
- err
80
- )));
81
- }
82
- };
83
-
84
- let samples_varlen = match samples_dataset.read_1d::<VarLenUnicode>() {
85
- Ok(s) => s,
86
- Err(err) => {
87
- return Err(hdf5::Error::Internal(format!("Failed to read sample names: {}", err)));
88
- }
89
- };
90
-
91
- let all_samples: Vec<String> = samples_varlen.iter().map(|s| s.to_string()).collect();
92
-
93
- // Find row indices
94
- let mut row_indices: Vec<usize> = Vec::with_capacity(query_items.len());
95
- let mut found_items: Vec<String> = Vec::with_capacity(query_items.len());
96
-
97
- for item in query_items {
98
- if let Some(idx) = item_names.iter().position(|i| i == item) {
99
- row_indices.push(idx);
100
- found_items.push(item.clone());
101
- } else {
102
- return Err(hdf5::Error::Internal(format!("Item '{}' not found", item)));
103
- }
104
- }
105
-
106
- // Read the ENTIRE matrix once (the key for speed in bulk)
107
- let matrix_ds = match file.dataset("matrix") {
108
- Ok(ds) => ds,
109
- Err(err) => {
110
- return Err(hdf5::Error::Internal(format!(
111
- "Failed to open 'matrix' dataset: {}",
112
- err
113
- )));
114
- }
115
- };
116
-
117
- let shape = matrix_ds.shape();
118
- if shape.len() != 2 {
119
- return Err(hdf5::Error::Internal("Matrix must be 2D".into()));
120
- };
121
-
122
- let all_data = match matrix_ds.read::<f64, Dim<[usize; 2]>>() {
123
- Ok(data) => data,
124
- Err(err) => {
125
- return Err(hdf5::Error::Internal(format!(
126
- "Failed to read expression data: {}",
127
- err
128
- )));
129
- }
130
- };
131
-
132
- // build the same output structure as query_dataset
133
- let mut query_output = Map::new();
134
- let mut timings = Map::new();
135
-
136
- for (i, &row_idx) in row_indices.iter().enumerate() {
137
- let item = &found_items[i];
138
- let query_start = Instant::now();
139
-
140
- let mut sample_map = Map::new();
141
- for (s_idx, sample) in all_samples.iter().enumerate() {
142
- let val = all_data[[row_idx, s_idx]];
143
- sample_map.insert(
144
- sample.replace("\\", ""),
145
- if val.is_finite() { json!(val) } else { Value::Null },
146
- );
147
- }
148
- query_output.insert(
149
- item.replace("\\", ""),
150
- json!({
151
- "dataId": item,
152
- "samples": sample_map
153
- }),
154
- );
155
- let elapsed = query_start.elapsed().as_millis() as u64;
156
- query_output.insert(format!("{}_ms", item), Value::from(elapsed));
157
- }
158
-
159
- timings.insert(
160
- "read_full_matrix_ms".to_string(),
161
- Value::from(start.elapsed().as_millis() as u64),
162
- );
163
- timings.insert("query_count".to_string(), Value::from(query_items.len()));
164
-
165
- Ok(query_output)
166
- }
167
-
168
- /// h5 file validation
169
- // Detects if the HDF5 file contains a valid matrix dataset
170
- pub fn detect_hdf5_format(
171
- hdf5_filename: &str,
172
- matrix_name: &str,
173
- row_dataset: &str,
174
- col_dataset: &str,
175
- ) -> Result<&'static str> {
176
- let file = File::open(hdf5_filename)?;
177
-
178
- // Check for matrix dataset (must be 2D)
179
- let matrix_ok = file
180
- .dataset(matrix_name)
181
- .map(|dataset| dataset.shape().len() == 2)
182
- .unwrap_or(false);
183
-
184
- // Check for row dataset (must exist and contain VarLenAscii)
185
- let row_ok = file
186
- .dataset(row_dataset)
187
- .and_then(|ds| ds.read_1d::<hdf5::types::VarLenUnicode>())
188
- .is_ok();
189
-
190
- // Check for column dataset (must exist and contain VarLenAscii)
191
- let col_ok = file
192
- .dataset(col_dataset)
193
- .and_then(|ds| ds.read_1d::<hdf5::types::VarLenUnicode>())
194
- .is_ok();
195
-
196
- if matrix_ok && row_ok && col_ok {
197
- Ok("matrix")
198
- } else {
199
- Ok("unknown")
200
- }
201
- }
202
-
203
- pub fn validate_hdf5_file(hdf5_filename: String, include_items: bool) -> Result<()> {
204
- let file = File::open(&hdf5_filename)?;
205
- let matrix_name = "matrix";
206
- let row_dataset = "samples";
207
- let col_dataset = "item";
208
- let file_format = detect_hdf5_format(&hdf5_filename, matrix_name, row_dataset, col_dataset)?;
209
-
210
- let output = match file_format {
211
- "matrix" => {
212
- let dataset = file.dataset(matrix_name)?;
213
- let matrix_shape = dataset.shape();
214
- let datatype = dataset.dtype()?;
215
-
216
- // Read row_dataset as VarLenUnicode
217
- let row_dataset_data = file.dataset(row_dataset)?;
218
- let row_data: Vec<String> = row_dataset_data
219
- .read_1d::<VarLenUnicode>()?
220
- .iter()
221
- .map(|s| s.to_string())
222
- .collect();
223
-
224
- // Validate matrix data
225
- let matrix_valid = if matrix_shape.len() == 2 && matrix_shape[0] > 0 && matrix_shape[1] > 0 {
226
- // Create a selection for a 1x1 slice at (0,0)
227
- let selection = hdf5::Selection::from((0..1, 0..1));
228
-
229
- if datatype.is::<f64>() {
230
- dataset.read_slice_2d::<f64, _>(selection).is_ok()
231
- } else if datatype.is::<f32>() {
232
- dataset.read_slice_2d::<f32, _>(selection).is_ok()
233
- } else if datatype.is::<i32>() {
234
- dataset.read_slice_2d::<i32, _>(selection).is_ok()
235
- } else if datatype.is::<i64>() {
236
- dataset.read_slice_2d::<i64, _>(selection).is_ok()
237
- } else {
238
- false
239
- }
240
- } else {
241
- false
242
- };
243
-
244
- let mut result = json!({
245
- "status": if matrix_valid { "success" } else { "failure" },
246
- "message": if matrix_valid {
247
- "HDF5 matrix file loaded successfully"
248
- } else {
249
- "Invalid matrix structure"
250
- },
251
- "file_path": hdf5_filename,
252
- "format": "matrix",
253
- "matrix_dimensions": {
254
- "num_rows": matrix_shape.get(0).unwrap_or(&0),
255
- "num_columns": matrix_shape.get(1).unwrap_or(&0)
256
- },
257
- row_dataset.to_string(): row_data
258
- });
259
-
260
- // Optionally include item names (e.g. isoform IDs) in the response
261
- if include_items {
262
- let col_dataset_data = file.dataset(col_dataset)?;
263
- let col_data: Vec<String> = col_dataset_data
264
- .read_1d::<VarLenUnicode>()?
265
- .iter()
266
- .map(|s| s.to_string())
267
- .collect();
268
- result["items"] = json!(col_data);
269
- }
270
-
271
- result
272
- }
273
- _ => {
274
- json!({
275
- "status": "failure",
276
- "message": format!(
277
- "Missing or invalid required datasets: matrix='{}', row_dataset='{}', col_dataset='{}'",
278
- matrix_name, row_dataset, col_dataset
279
- ),
280
- "file_path": hdf5_filename,
281
- "format": "unknown",
282
- "matrix_dimensions": {
283
- "num_rows": 0,
284
- "num_columns": 0
285
- }
286
- })
287
- }
288
- };
289
- println!("{}", output);
290
- Ok(())
291
- }
292
-
293
- /// read h5 file
294
-
295
- // Trait for converting types to f64, allowing lossy conversions
296
- trait ToF64 {
297
- fn to_f64(&self) -> f64;
298
- }
299
-
300
- impl ToF64 for f32 {
301
- fn to_f64(&self) -> f64 {
302
- *self as f64
303
- }
304
- }
305
-
306
- impl ToF64 for f64 {
307
- fn to_f64(&self) -> f64 {
308
- *self
309
- }
310
- }
311
-
312
- impl ToF64 for i32 {
313
- fn to_f64(&self) -> f64 {
314
- *self as f64
315
- }
316
- }
317
-
318
- impl ToF64 for i64 {
319
- fn to_f64(&self) -> f64 {
320
- if self.abs() > (1 << 53) {
321
- eprintln!("Warning: i64 value {} may lose precision when converted to f64", self);
322
- }
323
- *self as f64
324
- }
325
- }
326
-
327
- // Process matrix data for a given type
328
- fn process_data<T: ToF64 + Copy>(data: &[T], row_data: &[String]) -> Map<String, Value> {
329
- let mut row_data_map = Map::new();
330
- for (i, row) in row_data.iter().enumerate() {
331
- if i < data.len() {
332
- let value = data[i].to_f64();
333
- row_data_map.insert(
334
- row.replace("\\", ""),
335
- if value.is_finite() {
336
- Value::from(value)
337
- } else {
338
- Value::Null
339
- },
340
- );
341
- }
342
- }
343
- row_data_map
344
- }
345
-
346
- // Data query
347
- // Supports f32, f64, i32 and i64 datatypes for the matrix dataset.
348
- // Uses hardcoded "matrix", "samples" and "item" dataset.
349
- // "samples" and "item" datasets are read as VarLenUnicode.
350
- //
351
- // # Arguments
352
- // * `hdf5_filenam` - Path to the HDF5 file
353
- // * `qry` - Query (non-empty array)
354
- //
355
- // # Returns
356
- // Prints a JSON object with matrix data for query data to stdout
357
- fn query_dataset(hdf5_filename: String, qry: Vec<String>) -> Result<()> {
358
- let overall_start_time = Instant::now();
359
- let mut timings = Map::new();
360
- timings.insert("query_count".to_string(), Value::from(qry.len()));
361
-
362
- let file = match File::open(&hdf5_filename) {
363
- Ok(f) => f,
364
- Err(err) => {
365
- println!("{}", error_response(format!("Failed to open HDF5 file: {}", err)));
366
- return Ok(());
367
- }
368
- };
369
-
370
- let col_dataset_name = String::from("item");
371
- let col_dataset = match file.dataset(&col_dataset_name) {
372
- Ok(ds) => ds,
373
- Err(err) => {
374
- println!(
375
- "{}",
376
- error_response(format!("Failed to open {} dataset: {}", col_dataset_name, err))
377
- );
378
- return Ok(());
379
- }
380
- };
381
-
382
- let col_dataset_varlen = match col_dataset.read_1d::<VarLenUnicode>() {
383
- Ok(g) => g,
384
- Err(err) => {
385
- println!(
386
- "{}",
387
- error_response(format!("Failed to read {}: {}", col_dataset_name, err))
388
- );
389
- return Ok(());
390
- }
391
- };
392
- let col_data: Vec<String> = col_dataset_varlen.iter().map(|g| g.to_string()).collect();
393
-
394
- let hashmap_start_time = Instant::now();
395
- let col_data_to_index: std::collections::HashMap<String, usize> =
396
- col_data.iter().enumerate().map(|(i, g)| (g.clone(), i)).collect();
397
- timings.insert(
398
- "build_hashmap_ms".to_string(),
399
- Value::from(hashmap_start_time.elapsed().as_millis() as u64),
400
- );
401
-
402
- let row_dataset_name = String::from("samples");
403
- let row_dataset = match file.dataset(&row_dataset_name) {
404
- Ok(ds) => ds,
405
- Err(err) => {
406
- println!(
407
- "{}",
408
- error_response(format!("Failed to open {} dataset: {}", row_dataset_name, err))
409
- );
410
- return Ok(());
411
- }
412
- };
413
- let row_dataset_varlen = match row_dataset.read_1d::<VarLenUnicode>() {
414
- Ok(s) => s,
415
- Err(err) => {
416
- println!(
417
- "{}",
418
- error_response(format!("Failed to read {}: {}", row_dataset_name, err))
419
- );
420
- return Ok(());
421
- }
422
- };
423
- let row_data: Vec<String> = row_dataset_varlen.iter().map(|s| s.to_string()).collect();
424
-
425
- let matrix_dataset = match file.dataset("matrix") {
426
- Ok(ds) => ds,
427
- Err(err) => {
428
- println!("{}", error_response(format!("Failed to open matrix dataset: {}", err)));
429
- return Ok(());
430
- }
431
- };
432
-
433
- let datatype = match matrix_dataset.dtype() {
434
- Ok(dt) => dt,
435
- Err(err) => {
436
- println!("{}", error_response(format!("Failed to read matrix datatype: {}", err)));
437
- return Ok(());
438
- }
439
- };
440
-
441
- let col_data_map = Arc::new(std::sync::Mutex::new(Map::new()));
442
- let thread_count = std::cmp::min(4, qry.len());
443
- timings.insert("thread_count".to_string(), Value::from(thread_count));
444
-
445
- let results: Vec<(String, Value)> = match rayon::ThreadPoolBuilder::new()
446
- .num_threads(thread_count)
447
- .build()
448
- {
449
- Ok(pool) => pool.install(|| {
450
- qry
451
- .par_iter()
452
- .map(|query| {
453
- let query_start_time = Instant::now();
454
- let result = match col_data_to_index.get(query) {
455
- Some(&index) => {
456
- if index >= matrix_dataset.shape()[0] {
457
- let mut error_map = Map::new();
458
- error_map.insert(
459
- "error".to_string(),
460
- Value::String("Query index out of bounds".to_string()),
461
- );
462
- (query.clone(), Value::Object(error_map))
463
- } else {
464
- let selection = Selection::from((index..index+1, ..));
465
- if datatype.is::<f64>() {
466
- match matrix_dataset.read_slice_2d::<f64,_>(selection) {
467
- Ok(data) => (
468
- query.clone(),
469
- json!({
470
- "dataId": query,
471
- row_dataset_name.clone(): process_data(data.as_slice().unwrap(), &row_data)
472
- }),
473
- ),
474
- Err(err) => {
475
- let mut error_map = Map::new();
476
- error_map.insert(
477
- "error".to_string(),
478
- Value::String(format!("Failed to read f64 matrix values: {}", err)),
479
- );
480
- (query.clone(), Value::Object(error_map))
481
- }
482
- }
483
- } else if datatype.is::<f32>() {
484
- match matrix_dataset.read_slice_2d::<f32,_>(selection) {
485
- Ok(data) => (
486
- query.clone(),
487
- json!({
488
- "dataId": query,
489
- row_dataset_name.clone(): process_data(data.as_slice().unwrap(), &row_data)
490
- }),
491
- ),
492
- Err(err) => {
493
- let mut error_map = Map::new();
494
- error_map.insert(
495
- "error".to_string(),
496
- Value::String(format!("Failed to read f32 matrix values: {}", err)),
497
- );
498
- (query.clone(), Value::Object(error_map))
499
- }
500
- }
501
- } else if datatype.is::<i32>() {
502
- match matrix_dataset.read_slice_2d::<i32,_>(selection) {
503
- Ok(data) => (
504
- query.clone(),
505
- json!({
506
- "dataId": query,
507
- row_dataset_name.clone(): process_data(data.as_slice().unwrap(), &row_data)
508
- }),
509
- ),
510
- Err(err) => {
511
- let mut error_map = Map::new();
512
- error_map.insert(
513
- "error".to_string(),
514
- Value::String(format!("Failed to read i32 matrix values: {}", err)),
515
- );
516
- (query.clone(), Value::Object(error_map))
517
- }
518
- }
519
- } else if datatype.is::<i64>() {
520
- match matrix_dataset.read_slice_2d::<i64,_>(selection) {
521
- Ok(data) => (
522
- query.clone(),
523
- json!({
524
- "dataId": query,
525
- row_dataset_name.clone(): process_data(data.as_slice().unwrap(), &row_data)
526
- }),
527
- ),
528
- Err(err) => {
529
- let mut error_map = Map::new();
530
- error_map.insert(
531
- "error".to_string(),
532
- Value::String(format!("Failed to read i64 matrix values: {}", err)),
533
- );
534
- (query.clone(), Value::Object(error_map))
535
- }
536
- }
537
- } else {
538
- let mut error_map = Map::new();
539
- error_map.insert(
540
- "error".to_string(),
541
- Value::String("Unsupported matrix datatype (expected f64, f32, i64 or i32)".to_string()),
542
- );
543
- (query.clone(), Value::Object(error_map))
544
- }
545
- }
546
- }
547
- None => {
548
- let mut error_map = Map::new();
549
- error_map.insert(
550
- "error".to_string(),
551
- Value::String(format!("Query '{}' not found in {} dataset", query, col_dataset_name)),
552
- );
553
- (query.clone(),Value::Object(error_map))
554
- }
555
- };
556
- let elapsed_time = query_start_time.elapsed().as_millis() as u64;
557
- let mut query_timings = col_data_map.lock().unwrap();
558
- query_timings.insert(
559
- format!("{}_ms", query),
560
- Value::from(elapsed_time),
561
- );
562
- result
563
- })
564
- .collect()
565
- }),
566
- Err(err) => {
567
- timings.insert(
568
- "thread_pool_error".to_string(),
569
- Value::String(format!("Failed to create thread pool: {}", err)),
570
- );
571
- qry
572
- .iter()
573
- .map(|query| {
574
- let query_start_time = Instant::now();
575
- let result = match col_data_to_index.get(query) {
576
- Some(&index) => {
577
- if index >= matrix_dataset.shape()[0] {
578
- let mut error_map = Map::new();
579
- error_map.insert(
580
- "error".to_string(),
581
- Value::String("Query index out of bounds".to_string()),
582
- );
583
- (query.clone(), Value::Object(error_map))
584
- } else {
585
- let selection = Selection::from((index..index+1, ..));
586
- if datatype.is::<f64>() {
587
- match matrix_dataset.read_slice_1d::<f64,_>(selection) {
588
- Ok(data) => (
589
- query.clone(),
590
- json!({
591
- "dataId": query,
592
- row_dataset_name.clone(): process_data(data.as_slice().unwrap(), &row_data)
593
- }),
594
- ),
595
- Err(err) => {
596
- let mut error_map = Map::new();
597
- error_map.insert(
598
- "error".to_string(),
599
- Value::String(format!("Failed to read f64 matrix values: {}", err)),
600
- );
601
- (query.clone(), Value::Object(error_map))
602
- }
603
- }
604
- } else if datatype.is::<f32>() {
605
- match matrix_dataset.read_slice_1d::<f32,_>(selection) {
606
- Ok(data) => (
607
- query.clone(),
608
- json!({
609
- "dataId": query,
610
- row_dataset_name.clone(): process_data(data.as_slice().unwrap(), &row_data)
611
- }),
612
- ),
613
- Err(err) => {
614
- let mut error_map = Map::new();
615
- error_map.insert(
616
- "error".to_string(),
617
- Value::String(format!("Failed to read f32 matrix values: {}", err)),
618
- );
619
- (query.clone(), Value::Object(error_map))
620
- }
621
- }
622
- } else if datatype.is::<i32>() {
623
- match matrix_dataset.read_slice_1d::<i32,_>(selection) {
624
- Ok(data) => (
625
- query.clone(),
626
- json!({
627
- "dataId": query,
628
- row_dataset_name.clone(): process_data(data.as_slice().unwrap(), &row_data)
629
- }),
630
- ),
631
- Err(err) => {
632
- let mut error_map = Map::new();
633
- error_map.insert(
634
- "error".to_string(),
635
- Value::String(format!("Failed to read i32 matrix values: {}", err)),
636
- );
637
- (query.clone(), Value::Object(error_map))
638
- }
639
- }
640
- } else if datatype.is::<i64>() {
641
- match matrix_dataset.read_slice_1d::<i64,_>(selection) {
642
- Ok(data) => (
643
- query.clone(),
644
- json!({
645
- "dataId": query,
646
- row_dataset_name.clone(): process_data(data.as_slice().unwrap(), &row_data)
647
- }),
648
- ),
649
- Err(err) => {
650
- let mut error_map = Map::new();
651
- error_map.insert(
652
- "error".to_string(),
653
- Value::String(format!("Failed to read i64 matrix values: {}", err)),
654
- );
655
- (query.clone(), Value::Object(error_map))
656
- }
657
- }
658
- } else {
659
- let mut error_map = Map::new();
660
- error_map.insert(
661
- "error".to_string(),
662
- Value::String("Unsupported matrix datatype (expected f64, f32, i64 or i32)".to_string()),
663
- );
664
- (query.clone(), Value::Object(error_map))
665
- }
666
- }
667
- }
668
- None => {
669
- let mut error_map = Map::new();
670
- error_map.insert(
671
- "error".to_string(),
672
- Value::String(format!("Query '{}' not found in {} dataset", query, col_dataset_name)),
673
- );
674
- (query.clone(),Value::Object(error_map))
675
- }
676
- };
677
- let elapsed_time = query_start_time.elapsed().as_millis() as u64;
678
- let mut query_timings = col_data_map.lock().unwrap();
679
- query_timings.insert(
680
- format!("{}_ms", query),
681
- Value::from(elapsed_time),
682
- );
683
- result
684
- })
685
- .collect()
686
- }
687
- };
688
-
689
- let mut col_data_map = col_data_map.lock().unwrap();
690
- for (query, query_data) in results {
691
- col_data_map.insert(query, query_data);
692
- }
693
-
694
- let output_json = json!({
695
- "query_output": *col_data_map,
696
- "timings": timings,
697
- "total_time_ms": overall_start_time.elapsed().as_millis() as u64
698
- });
699
- println!("{}", output_json);
700
- Ok(())
701
- }
702
-
703
- /// Main function to handle both validation and read of h5 file
704
- fn main() -> Result<()> {
705
- let mut input = String::new();
706
- match io::stdin().read_line(&mut input) {
707
- Ok(_bytes_read) => {
708
- let input_json = match json::parse(&input) {
709
- Ok(json) => json,
710
- Err(_err) => {
711
- panic!("Invalid JSON input");
712
- }
713
- };
714
-
715
- // Extract HDF5 filename
716
- let hdf5_filename = match input_json["hdf5_file"].as_str() {
717
- Some(x) => x.to_string(),
718
- None => {
719
- println!("{}", error_response("HDF5 filename not provided"));
720
- return Ok(());
721
- }
722
- };
723
-
724
- let read_mode = input_json["read_mode"].as_str().unwrap_or("default").to_lowercase();
725
-
726
- // h5 file validation
727
- if input_json.has_key("validate") {
728
- let v: bool = match input_json["validate"].as_bool() {
729
- Some(x) => x,
730
- None => false,
731
- };
732
- if v {
733
- let include_items = input_json["include_items"].as_bool().unwrap_or(false);
734
- let _ = validate_hdf5_file(hdf5_filename, include_items);
735
- } else {
736
- println!("{}", error_response("The value of validate is invalid"));
737
- }
738
- return Ok(());
739
- };
740
-
741
- // Get query items
742
- let qry: Vec<String> = match &input_json["query"] {
743
- JsonValue::Array(arr) => arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect(),
744
- _ => vec![],
745
- };
746
-
747
- if qry.is_empty() {
748
- println!("{}", error_response(format!("query is empty")));
749
- return Ok(());
750
- }
751
-
752
- let overall_start = Instant::now();
753
-
754
- if read_mode == "bulk" {
755
- // ====================== BULK MODE ======================
756
- match bulk_query_hdf5(&hdf5_filename, &qry) {
757
- Ok(query_output) => {
758
- let output_json = json!({
759
- "query_output": query_output,
760
- "timings": {
761
- "query_count": qry.len(),
762
- "read_mode": "bulk"
763
- },
764
- "total_time_ms": overall_start.elapsed().as_millis() as u64
765
- });
766
- println!("{}", output_json);
767
- }
768
- Err(e) => {
769
- println!("{}", error_response(format!("Bulk query failed: {}", e)));
770
- }
771
- }
772
- } else {
773
- let _ = query_dataset(hdf5_filename, qry);
774
- }
775
- }
776
- Err(error) => {
777
- println!("{}", error_response(format!("Error reading input: {}", error)));
778
- }
779
- }
780
- Ok(())
781
- }