• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

simonrw / rust-fitsio / 10017017409

20 Jul 2024 02:56AM UTC coverage: 76.86%. First build
10017017409

Pull #330

github

web-flow
Merge 6d5178e26 into 91721ef27
Pull Request #330: Add vector type support

88 of 124 new or added lines in 5 files covered. (70.97%)

930 of 1210 relevant lines covered (76.86%)

3.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.27
/fitsio/src/tables.rs
1
//! Table-related code
2
use crate::errors::{check_status, Error, FitsError, IndexError, Result};
3
use crate::fitsfile::FitsFile;
4
use crate::hdu::{FitsHdu, HduInfo};
5
use crate::longnam::*;
6
use crate::stringutils::status_to_string;
7
use crate::types::DataType;
8
use std::ffi;
9
use std::mem::size_of;
10
use std::ops::Range;
11
use std::ptr;
12
use std::str::FromStr;
13

14
/// Trait for reading a fits column
15
pub trait ReadsCol {
16
    #[doc(hidden)]
17
    fn read_col_range<T: Into<String>>(
18
        fits_file: &mut FitsFile,
19
        name: T,
20
        range: &Range<usize>,
21
    ) -> Result<Vec<Self>>
22
    where
23
        Self: Sized;
24

25
    #[doc(hidden)]
26
    fn read_cell_value<T>(fits_file: &mut FitsFile, name: T, idx: usize) -> Result<Self>
27
    where
28
        T: Into<String>,
29
        Self: Sized;
30

31
    #[doc(hidden)]
32
    fn read_col<T: Into<String>>(fits_file: &mut FitsFile, name: T) -> Result<Vec<Self>>
16✔
33
    where
34
        Self: Sized,
35
    {
36
        match fits_file.fetch_hdu_info() {
32✔
37
            Ok(HduInfo::TableInfo { num_rows, .. }) => {
16✔
38
                let range = 0..num_rows;
16✔
39
                Self::read_col_range(fits_file, name, &range)
32✔
40
            }
41
            Err(e) => Err(e),
×
42
            _ => panic!("Unknown error occurred"),
×
43
        }
44
    }
45
}
46

47
macro_rules! reads_col_impl {
48
    ($t:ty, $func:ident, $nullval:expr) => {
49
        impl ReadsCol for $t {
50
            fn read_col_range<T: Into<String>>(
14✔
51
                fits_file: &mut FitsFile,
52
                name: T,
53
                range: &Range<usize>,
54
            ) -> Result<Vec<Self>> {
55
                match fits_file.fetch_hdu_info() {
28✔
56
                    Ok(HduInfo::TableInfo {
57
                        column_descriptions,
14✔
58
                        ..
59
                    }) => {
60
                        let num_output_rows = range.end - range.start;
28✔
61
                        let test_name = name.into();
14✔
62
                        let column_number = column_descriptions
42✔
63
                            .iter()
64
                            .position(|ref desc| desc.name == test_name)
28✔
65
                            .ok_or(Error::Message(format!(
28✔
66
                                "Cannot find column {:?}",
67
                                test_name
68
                            )))?;
69
                        let col_desc = &column_descriptions[column_number];
28✔
70
                        let repeat = if col_desc.data_type.typ == ColumnDataType::Bit {
28✔
71
                            col_desc.data_type.repeat / (size_of::<$t>() * 8)
4✔
72
                        } else {
73
                            col_desc.data_type.repeat
12✔
74
                        };
75
                        let mut out = vec![$nullval; num_output_rows * repeat];
28✔
76
                        let mut status = 0;
14✔
77
                        unsafe {
78
                            $func(
14✔
79
                                fits_file.fptr.as_mut() as *mut _,
14✔
80
                                (column_number + 1) as i32,
14✔
81
                                (range.start + 1) as i64,
14✔
82
                                1,
83
                                (num_output_rows * repeat) as _,
14✔
84
                                $nullval,
85
                                out.as_mut_ptr(),
14✔
86
                                ptr::null_mut(),
87
                                &mut status,
88
                            );
89
                        }
90

91
                        match status {
14✔
92
                            0 => Ok(out),
14✔
93
                            307 => Err(IndexError {
1✔
94
                                message: "given indices out of range".to_string(),
1✔
95
                                given: range.clone(),
1✔
96
                            }
97
                            .into()),
1✔
98
                            e => Err(FitsError {
×
99
                                status: e,
100
                                message: status_to_string(e).unwrap().unwrap(),
×
101
                            }
102
                            .into()),
×
103
                        }
104
                    }
105
                    Err(e) => Err(e),
×
106
                    _ => panic!("Unknown error occurred"),
×
107
                }
108
            }
109

110
            fn read_cell_value<T>(fits_file: &mut FitsFile, name: T, idx: usize) -> Result<Self>
2✔
111
            where
112
                T: Into<String>,
113
                Self: Sized,
114
            {
115
                match fits_file.fetch_hdu_info() {
4✔
116
                    Ok(HduInfo::TableInfo {
117
                        column_descriptions,
2✔
118
                        ..
119
                    }) => {
120
                        let mut out = $nullval;
121
                        let test_name = name.into();
2✔
122
                        let column_number = column_descriptions
6✔
123
                            .iter()
124
                            .position(|ref desc| desc.name == test_name)
4✔
125
                            .ok_or(Error::Message(format!(
4✔
126
                                "Cannot find column {:?}",
127
                                test_name
128
                            )))?;
129
                        let repeat = column_descriptions[column_number].data_type.repeat;
4✔
130
                        if repeat > 1 {
2✔
NEW
131
                            unimplemented!(
×
132
                                "reading a single cell of a vector value (e.g., TFORM1 = 100E) is unimplemented. Call read_col() or read_col_range()."
133
                            )
134
                        }
135
                        let mut status = 0;
2✔
136

137
                        unsafe {
138
                            $func(
2✔
139
                                fits_file.fptr.as_mut() as *mut _,
2✔
140
                                (column_number + 1) as i32,
2✔
141
                                (idx + 1) as i64,
2✔
142
                                1,
143
                                1,
144
                                $nullval,
145
                                &mut out,
146
                                ptr::null_mut(),
147
                                &mut status,
148
                            );
149
                        }
150

151
                        check_status(status).map(|_| out)
6✔
152
                    }
153
                    Err(e) => Err(e),
×
154
                    _ => panic!("Unknown error occurred"),
×
155
                }
156
            }
157
        }
158
    };
159
}
160

161
reads_col_impl!(u8, fits_read_col_byt, 0);
162
reads_col_impl!(i8, fits_read_col_sbyt, 0);
163
reads_col_impl!(i16, fits_read_col_sht, 0);
164
reads_col_impl!(u16, fits_read_col_usht, 0);
165
reads_col_impl!(i32, fits_read_col_int, 0);
1✔
166
reads_col_impl!(u32, fits_read_col_uint, 0);
167
reads_col_impl!(i64, fits_read_col_lnglng, 0);
1✔
168
reads_col_impl!(u64, fits_read_col_ulnglng, 0);
169
reads_col_impl!(f32, fits_read_col_flt, 0.0);
170
reads_col_impl!(f64, fits_read_col_dbl, 0.0);
171

172
impl ReadsCol for bool {
173
    fn read_col_range<T: Into<String>>(
1✔
174
        fits_file: &mut FitsFile,
175
        name: T,
176
        range: &Range<usize>,
177
    ) -> Result<Vec<Self>> {
178
        match fits_file.fetch_hdu_info() {
2✔
179
            Ok(HduInfo::TableInfo {
1✔
NEW
180
                column_descriptions,
×
NEW
181
                ..
×
NEW
182
            }) => {
×
183
                let num_output_rows = range.end - range.start;
2✔
184
                let test_name = name.into();
1✔
185
                let column_number = column_descriptions
3✔
186
                    .iter()
187
                    .position(|ref desc| desc.name == test_name)
2✔
188
                    .ok_or(Error::Message(format!(
2✔
NEW
189
                        "Cannot find column {:?}",
×
NEW
190
                        test_name
×
191
                    )))?;
192
                let col_desc = &column_descriptions[column_number];
2✔
193
                let repeat = col_desc.data_type.repeat;
1✔
194
                let num_bits = num_output_rows * repeat;
1✔
195

196
                let mut out = vec![false; num_bits];
2✔
197
                let mut status = 0;
1✔
198
                unsafe {
199
                    fits_read_col_bit(
200
                        fits_file.fptr.as_mut() as *mut _,
1✔
201
                        (column_number + 1) as i32,
1✔
202
                        (range.start + 1) as i64,
1✔
203
                        1,
NEW
204
                        num_bits as _,
×
205
                        out.as_mut_ptr() as *mut c_char,
1✔
NEW
206
                        &mut status,
×
207
                    );
208
                }
209

210
                match status {
1✔
211
                    0 => Ok(out),
1✔
NEW
212
                    307 => Err(IndexError {
×
NEW
213
                        message: "given indices out of range".to_string(),
×
NEW
214
                        given: range.clone(),
×
215
                    }
NEW
216
                    .into()),
×
NEW
217
                    e => Err(FitsError {
×
NEW
218
                        status: e,
×
NEW
219
                        message: status_to_string(e).unwrap().unwrap(),
×
220
                    }
NEW
221
                    .into()),
×
222
                }
223
            }
NEW
224
            Err(e) => Err(e),
×
NEW
225
            _ => panic!("Unknown error occurred"),
×
226
        }
227
    }
228

229
    fn read_cell_value<T>(fits_file: &mut FitsFile, name: T, idx: usize) -> Result<Self>
230
    where
231
        T: Into<String>,
232
        Self: Sized,
233
    {
234
        // XXX Ineffient but works
NEW
235
        Self::read_col_range(fits_file, name, &(idx..idx + 1)).map(|v| v[0].clone())
×
236
    }
237
}
238

239
impl ReadsCol for String {
240
    fn read_col_range<T: Into<String>>(
2✔
241
        fits_file: &mut FitsFile,
242
        name: T,
243
        range: &Range<usize>,
244
    ) -> Result<Vec<Self>> {
245
        match fits_file.fetch_hdu_info() {
4✔
246
            Ok(HduInfo::TableInfo {
2✔
247
                column_descriptions,
×
248
                ..
×
249
            }) => {
×
250
                let num_output_rows = range.end - range.start;
4✔
251
                let test_name = name.into();
2✔
252
                let column_number = column_descriptions
4✔
253
                    .iter()
254
                    .position(|desc| desc.name == test_name)
4✔
255
                    .ok_or_else(|| Error::Message(format!("Cannot find column {:?}", test_name)))?;
×
256

257
                /* Set up the storage arrays for the column string values */
258
                let mut raw_char_data: Vec<*mut libc::c_char> = Vec::with_capacity(num_output_rows);
2✔
259

260
                let mut status = 0;
2✔
261
                let width = column_display_width(fits_file, column_number)?;
4✔
262

263
                let mut vecs: Vec<Vec<libc::c_char>> = Vec::with_capacity(num_output_rows);
2✔
264
                for _ in 0..num_output_rows {
6✔
265
                    let mut data: Vec<libc::c_char> = vec![0; width as _];
4✔
266
                    let data_p = data.as_mut_ptr();
4✔
267
                    vecs.push(data);
2✔
268
                    raw_char_data.push(data_p);
2✔
269
                }
270

271
                unsafe {
272
                    fits_read_col_str(
273
                        fits_file.fptr.as_mut() as *mut _,
2✔
274
                        (column_number + 1) as _,
2✔
275
                        (range.start + 1) as _,
2✔
276
                        1,
277
                        raw_char_data.len() as _,
2✔
278
                        ptr::null_mut(),
×
279
                        raw_char_data.as_ptr() as *mut *mut _,
2✔
280
                        ptr::null_mut(),
×
281
                        &mut status,
×
282
                    );
283
                }
284

285
                check_status(status)?;
4✔
286

287
                let mut out = Vec::with_capacity(num_output_rows);
2✔
288
                for val in &vecs {
8✔
289
                    let bytes: Vec<u8> =
12✔
290
                        val.iter().filter(|v| **v != 0).map(|v| *v as u8).collect();
×
291
                    let cstr = String::from_utf8(bytes)?;
2✔
292
                    out.push(cstr);
2✔
293
                }
294
                Ok(out)
2✔
295
            }
296
            Err(e) => Err(e),
×
297
            _ => panic!("Unknown error occurred"),
×
298
        }
299
    }
300

301
    fn read_cell_value<T>(fits_file: &mut FitsFile, name: T, idx: usize) -> Result<Self>
2✔
302
    where
303
        T: Into<String>,
304
        Self: Sized,
305
    {
306
        // XXX Ineffient but works
307
        Self::read_col_range(fits_file, name, &(idx..idx + 1)).map(|v| v[0].clone())
6✔
308
    }
309
}
310

311
/// Trait representing the ability to write column data
312
pub trait WritesCol {
313
    #[doc(hidden)]
314
    fn write_col_range<T: Into<String>>(
315
        fits_file: &mut FitsFile,
316
        hdu: &FitsHdu,
317
        col_name: T,
318
        col_data: &[Self],
319
        rows: &Range<usize>,
320
    ) -> Result<FitsHdu>
321
    where
322
        Self: Sized;
323

324
    #[doc(hidden)]
325
    fn write_col<T: Into<String>>(
11✔
326
        fits_file: &mut FitsFile,
327
        hdu: &FitsHdu,
328
        col_name: T,
329
        col_data: &[Self],
330
    ) -> Result<FitsHdu>
331
    where
332
        Self: Sized,
333
    {
334
        match fits_file.fetch_hdu_info() {
22✔
335
            Ok(HduInfo::TableInfo { .. }) => {
×
336
                let num_rows = col_data.len();
11✔
337
                let row_range = 0..num_rows;
11✔
338
                Self::write_col_range(fits_file, hdu, col_name, col_data, &row_range)
22✔
339
            }
340
            Ok(HduInfo::ImageInfo { .. }) => Err("Cannot write column data to FITS image".into()),
2✔
341
            Ok(HduInfo::AnyInfo { .. }) => {
×
342
                Err("Cannot determine HDU type, so cannot write column data".into())
×
343
            }
344
            Err(e) => Err(e),
×
345
        }
346
    }
347
}
348

349
macro_rules! writes_col_impl {
350
    ($t:ty, $data_type:expr) => {
351
        impl WritesCol for $t {
352
            fn write_col_range<T: Into<String>>(
9✔
353
                fits_file: &mut FitsFile,
354
                hdu: &FitsHdu,
355
                col_name: T,
356
                col_data: &[Self],
357
                rows: &Range<usize>,
358
            ) -> Result<FitsHdu> {
359
                match fits_file.fetch_hdu_info() {
18✔
360
                    Ok(HduInfo::TableInfo { .. }) => {
361
                        let colno = hdu.get_column_no(fits_file, col_name.into())?;
18✔
362
                        // TODO: check that the column exists in the file
363
                        let mut status = 0;
9✔
364
                        let n_rows = rows.end - rows.start;
18✔
365
                        unsafe {
366
                            fits_write_col(
9✔
367
                                fits_file.fptr.as_mut() as *mut _,
9✔
368
                                $data_type.into(),
9✔
369
                                (colno + 1) as _,
9✔
370
                                (rows.start + 1) as _,
9✔
371
                                1,
372
                                n_rows as _,
373
                                col_data.as_ptr() as *mut _,
374
                                &mut status,
375
                            );
376
                        }
377
                        check_status(status).and_then(|_| fits_file.current_hdu())
27✔
378
                    }
379
                    Ok(HduInfo::ImageInfo { .. }) => {
380
                        Err("Cannot write column data to FITS image".into())
×
381
                    }
382
                    Ok(HduInfo::AnyInfo { .. }) => {
383
                        Err("Cannot determine HDU type, so cannot write column data".into())
×
384
                    }
385
                    Err(e) => Err(e),
×
386
                }
387
            }
388
        }
389
    };
390
}
391

392
writes_col_impl!(u8, DataType::TBYTE);
2✔
393
writes_col_impl!(i8, DataType::TSBYTE);
1✔
394
writes_col_impl!(i32, DataType::TINT);
2✔
395
writes_col_impl!(u32, DataType::TUINT);
396
writes_col_impl!(i64, DataType::TLONGLONG);
1✔
397
writes_col_impl!(u64, DataType::TULONGLONG);
1✔
398
writes_col_impl!(f32, DataType::TFLOAT);
1✔
399
writes_col_impl!(f64, DataType::TDOUBLE);
1✔
400

401
impl WritesCol for bool {
402
    fn write_col_range<T: Into<String>>(
1✔
403
        fits_file: &mut FitsFile,
404
        hdu: &FitsHdu,
405
        col_name: T,
406
        col_data: &[Self],
407
        rows: &Range<usize>,
408
    ) -> Result<FitsHdu> {
409
        match fits_file.fetch_hdu_info() {
2✔
410
            Ok(HduInfo::TableInfo {
1✔
NEW
411
                column_descriptions,
×
NEW
412
                ..
×
NEW
413
            }) => {
×
414
                let colno = hdu.get_column_no(fits_file, col_name.into())?;
2✔
415
                // TODO: check that the column exists in the file
416
                let col_desc = &column_descriptions[colno];
2✔
417
                let repeat = col_desc.data_type.repeat;
1✔
418
                let num_bits = (rows.end - rows.start) * repeat;
1✔
419

420
                let mut status = 0;
1✔
421
                unsafe {
422
                    fits_write_col_bit(
423
                        fits_file.fptr.as_mut() as *mut _,
1✔
424
                        (colno + 1) as _,
1✔
425
                        (rows.start + 1) as _,
1✔
426
                        1,
NEW
427
                        num_bits as _,
×
NEW
428
                        col_data.as_ptr() as *mut _,
×
NEW
429
                        &mut status as *mut _,
×
430
                    );
431
                }
432
                check_status(status).and_then(|_| fits_file.current_hdu())
3✔
433
            }
NEW
434
            Ok(HduInfo::ImageInfo { .. }) => Err("Cannot write column data to FITS image".into()),
×
NEW
435
            Ok(HduInfo::AnyInfo { .. }) => {
×
NEW
436
                Err("Cannot determine HDU type, so cannot write column data".into())
×
437
            }
NEW
438
            Err(e) => Err(e),
×
439
        }
440
    }
441
}
442

443
impl WritesCol for String {
444
    fn write_col_range<T: Into<String>>(
1✔
445
        fits_file: &mut FitsFile,
446
        hdu: &FitsHdu,
447
        col_name: T,
448
        col_data: &[Self],
449
        rows: &Range<usize>,
450
    ) -> Result<FitsHdu> {
451
        match fits_file.fetch_hdu_info() {
2✔
452
            Ok(HduInfo::TableInfo { .. }) => {
×
453
                let colno = hdu.get_column_no(fits_file, col_name.into())?;
2✔
454
                let mut status = 0;
2✔
455

456
                let start = rows.start;
2✔
457
                let end = rows.end;
2✔
458
                let n_elements = end - start;
4✔
459
                let mut ptr_array = Vec::with_capacity(n_elements);
4✔
460

461
                let rows = rows.clone();
4✔
462

463
                // Have to free the memory for these pointers at the end
464
                for i in rows {
6✔
465
                    let s = ffi::CString::new(col_data[i].clone())?;
4✔
466
                    ptr_array.push(s.into_raw());
4✔
467
                }
468

469
                unsafe {
470
                    fits_write_col_str(
471
                        fits_file.fptr.as_mut() as *mut _,
2✔
472
                        (colno + 1) as _,
2✔
473
                        (start + 1) as _,
2✔
474
                        1,
475
                        n_elements as _,
×
476
                        ptr_array.as_mut_ptr() as _,
2✔
477
                        &mut status,
×
478
                    );
479
                }
480

481
                let hdu = check_status(status).and_then(|_| fits_file.current_hdu());
6✔
482

483
                // Free the memory in ptr_array
484
                for ptr in ptr_array {
8✔
485
                    assert!(!ptr.is_null());
4✔
486
                    let _ = unsafe { ffi::CString::from_raw(ptr) };
4✔
487
                }
488

489
                hdu
2✔
490
            }
491
            Ok(HduInfo::ImageInfo { .. }) => Err("Cannot write column data to FITS image".into()),
×
492
            Ok(HduInfo::AnyInfo { .. }) => {
×
493
                Err("Cannot determine HDU type, so cannot write column data".into())
×
494
            }
495
            Err(e) => Err(e),
×
496
        }
497
    }
498
}
499

500
/// Trait derivable with custom derive
501
pub trait FitsRow: ::std::default::Default {
502
    #[doc(hidden)]
503
    fn from_table(tbl: &FitsHdu, fits_file: &mut FitsFile, idx: usize) -> Result<Self>
504
    where
505
        Self: Sized;
506
}
507

508
/// Helper function to get the display width of a column
509
pub(crate) fn column_display_width(
2✔
510
    fits_file: &mut FitsFile,
511
    column_number: usize,
512
) -> Result<usize> {
513
    let mut status = 0;
2✔
514
    let mut width = 0;
2✔
515
    unsafe {
516
        fits_get_col_display_width(
517
            fits_file.fptr.as_mut() as *mut _,
2✔
518
            (column_number + 1) as _,
2✔
519
            &mut width,
520
            &mut status,
521
        );
522
    }
523
    check_status(status).map(|_| width as usize)
6✔
524
}
525

526
/// Description for new columns
527
#[derive(Debug, Clone)]
528
pub struct ColumnDescription {
529
    /// Name of the column
530
    pub name: String,
531

532
    /// Type of the data, see the cfitsio documentation
533
    pub data_type: Option<ColumnDataDescription>,
534
}
535

536
/// Concrete representation of the description of a column
537
#[derive(Debug, Clone, PartialEq, Eq)]
538
pub struct ConcreteColumnDescription {
539
    /// Name of the column
540
    pub name: String,
541

542
    /// Type of the data, see the cfitsio documentation
543
    pub data_type: ColumnDataDescription,
544
}
545

546
impl ColumnDescription {
547
    /// Create a new [`ColumnDescription`](struct.ColumnDescription.html) from a name
548
    pub fn new<T: Into<String>>(name: T) -> Self {
3✔
549
        ColumnDescription {
550
            name: name.into(),
4✔
551
            data_type: None,
552
        }
553
    }
554

555
    /// Add a data type to the column description
556
    pub fn with_type(&mut self, typ: ColumnDataType) -> &mut ColumnDescription {
3✔
557
        self.data_type = Some(ColumnDataDescription::scalar(typ));
5✔
558
        self
559
    }
560

561
    /// Make the column repeat
562
    pub fn that_repeats(&mut self, repeat: usize) -> &mut ColumnDescription {
5✔
563
        if let Some(ref mut desc) = self.data_type {
3✔
564
            desc.repeat = repeat;
5✔
565
        }
566
        self
567
    }
568

569
    /// Define the column width
570
    pub fn with_width(&mut self, width: usize) -> &mut ColumnDescription {
×
571
        if let Some(ref mut desc) = self.data_type {
×
572
            desc.width = width;
×
573
        }
574
        self
575
    }
576

577
    /// Render the [`ColumnDescription`](struct.ColumnDescription.html) into a
578
    /// [`ConcreteColumnDescription`](struct.ConcreteColumnDescription.html)
579
    pub fn create(&self) -> Result<ConcreteColumnDescription> {
3✔
580
        match self.data_type {
5✔
581
            Some(ref d) => Ok(ConcreteColumnDescription {
8✔
582
                name: self.name.clone(),
5✔
583
                data_type: d.clone(),
3✔
584
            }),
585
            None => {
586
                Err("No data type given. Ensure the `with_type` method has been called.".into())
1✔
587
            }
588
        }
589
    }
590
}
591

592
/// Description of the column data
593
#[derive(Debug, Clone, PartialEq, Eq)]
594
pub struct ColumnDataDescription {
595
    /// Does the column contain multiple values?
596
    pub repeat: usize,
597

598
    /// How wide is the column?
599
    pub width: usize,
600

601
    /// What data type does the column store?
602
    pub typ: ColumnDataType,
603
}
604

605
impl ColumnDataDescription {
606
    /// Create a new column data description
607
    pub fn new(typ: ColumnDataType, repeat: usize, width: usize) -> Self {
3✔
608
        ColumnDataDescription { repeat, width, typ }
609
    }
610

611
    /// Shortcut for creating a scalar column
612
    pub fn scalar(typ: ColumnDataType) -> Self {
3✔
613
        ColumnDataDescription::new(typ, 1, 1)
5✔
614
    }
615

616
    /// Shortcut for creating a vector column
617
    pub fn vector(typ: ColumnDataType, repeat: usize) -> Self {
×
618
        ColumnDataDescription::new(typ, repeat, 1)
×
619
    }
620
}
621

622
impl From<ColumnDataDescription> for String {
623
    fn from(orig: ColumnDataDescription) -> String {
3✔
624
        match orig.typ {
5✔
625
            ColumnDataType::Text => {
626
                if orig.width > 1 {
×
627
                    format!(
×
628
                        "{repeat}{data_type}{width}",
629
                        data_type = String::from(orig.typ),
×
630
                        repeat = orig.repeat,
631
                        width = orig.width
632
                    )
633
                } else {
634
                    format!(
×
635
                        "{repeat}{data_type}",
636
                        data_type = String::from(orig.typ),
×
637
                        repeat = orig.repeat
638
                    )
639
                }
640
            }
641
            _ => format!(
11✔
642
                "{repeat}{data_type}",
643
                data_type = String::from(orig.typ),
3✔
644
                repeat = orig.repeat
645
            ),
646
        }
647
    }
648
}
649

650
/// Types a column can represent
651
#[allow(missing_docs)]
652
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
653
pub enum ColumnDataType {
654
    Bit,
655
    Byte,
656
    SignedByte,
657
    Short,
658
    UnsignedShort,
659
    Int,
660
    Long,
661
    UnsignedLong,
662
    Float,
663
    Text,
664
    Double,
665
    LongLong,
666
    UnsignedLongLong,
667
    String,
668
}
669

670
impl From<ColumnDataType> for String {
671
    fn from(orig: ColumnDataType) -> String {
5✔
672
        use self::ColumnDataType::*;
673

674
        match orig {
8✔
675
            Bit => "X",
1✔
676
            Byte => "B",
1✔
677
            SignedByte => "S",
1✔
NEW
678
            Short => "I",
×
NEW
679
            UnsignedShort => "U",
×
680
            Int | Long => "J",
2✔
NEW
681
            UnsignedLong => "U",
×
682
            Float => "E",
1✔
683
            Text | String => "A",
2✔
684
            Double => "D",
1✔
685
            LongLong => "K",
1✔
686
            UnsignedLongLong => "W",
1✔
687
        }
688
        .to_string()
689
    }
690
}
691

692
impl FromStr for ColumnDataDescription {
693
    type Err = Box<dyn (::std::error::Error)>;
694

695
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
7✔
696
        let chars: Vec<_> = s.chars().collect();
6✔
697

698
        let mut repeat_str = Vec::new();
7✔
699
        let mut last_position = 0;
6✔
700
        for c in &chars {
22✔
701
            if c.is_ascii_digit() {
6✔
702
                repeat_str.push(c);
2✔
703
                last_position += 1;
2✔
704
            } else {
705
                break;
706
            }
707
        }
708

709
        let repeat = if repeat_str.is_empty() {
19✔
710
            1
7✔
711
        } else {
712
            let repeat_str: String = repeat_str.into_iter().collect();
4✔
713
            repeat_str.parse::<usize>()?
4✔
714
        };
715

716
        let data_type_char = chars[last_position];
13✔
717
        last_position += 1;
7✔
718

719
        let mut width_str = Vec::new();
6✔
720
        for c in chars.iter().skip(last_position) {
14✔
721
            if c.is_ascii_digit() {
1✔
722
                width_str.push(c);
2✔
723
            } else {
724
                break;
725
            }
726
        }
727

728
        let width = if width_str.is_empty() {
19✔
729
            1
7✔
730
        } else {
731
            let width_str: String = width_str.into_iter().collect();
2✔
732
            width_str.parse::<usize>()?
2✔
733
        };
734

735
        let data_type = match data_type_char {
7✔
736
            'X' => ColumnDataType::Bit,
2✔
737
            'B' => ColumnDataType::Byte,
1✔
NEW
738
            'S' => ColumnDataType::SignedByte,
×
739
            'E' => ColumnDataType::Float,
4✔
740
            'J' => ColumnDataType::Int,
4✔
741
            'D' => ColumnDataType::Double,
4✔
742
            'I' => ColumnDataType::Short,
1✔
743
            'K' => ColumnDataType::LongLong,
1✔
744
            'A' => ColumnDataType::String,
3✔
NEW
745
            'U' => ColumnDataType::UnsignedShort,
×
NEW
746
            'V' => ColumnDataType::UnsignedLong,
×
NEW
747
            'W' => ColumnDataType::UnsignedLongLong,
×
748
            _ => panic!(
×
749
                "Have not implemented str -> ColumnDataType for {}",
750
                data_type_char
751
            ),
752
        };
753

754
        Ok(ColumnDataDescription {
8✔
755
            repeat,
5✔
756
            typ: data_type,
8✔
757
            width,
5✔
758
        })
759
    }
760
}
761

762
/// Way of describing a column location
763
pub trait DescribesColumnLocation {
764
    /// Method by which the column number can be computed
765
    fn get_column_no(&self, hdu: &FitsHdu, fptr: &mut FitsFile) -> Result<i32>;
766
}
767

768
impl DescribesColumnLocation for usize {
769
    fn get_column_no(&self, _: &FitsHdu, _: &mut FitsFile) -> Result<i32> {
1✔
770
        Ok(*self as i32)
1✔
771
    }
772
}
773

774
impl<'a> DescribesColumnLocation for &'a str {
775
    fn get_column_no(&self, hdu: &FitsHdu, fits_file: &mut FitsFile) -> Result<i32> {
1✔
776
        match hdu.get_column_no(fits_file, *self) {
1✔
777
            Ok(value) => Ok(value as _),
1✔
778
            Err(e) => Err(e),
×
779
        }
780
    }
781
}
782

783
macro_rules! datatype_into_impl {
784
    ($t:ty) => {
785
        impl From<DataType> for $t {
786
            fn from(original: DataType) -> $t {
9✔
787
                match original {
7✔
788
                    DataType::TBIT => 1,
1✔
789
                    DataType::TBYTE => 11,
3✔
790
                    DataType::TSBYTE => 12,
2✔
791
                    DataType::TLOGICAL => 14,
1✔
792
                    DataType::TSTRING => 16,
1✔
793
                    DataType::TUSHORT => 20,
2✔
794
                    DataType::TSHORT => 21,
2✔
795
                    DataType::TUINT => 30,
2✔
796
                    DataType::TINT => 31,
4✔
797
                    DataType::TULONG => 40,
1✔
798
                    DataType::TLONG => 41,
2✔
799
                    DataType::TFLOAT => 42,
2✔
800
                    DataType::TULONGLONG => 80,
1✔
801
                    DataType::TLONGLONG => 81,
1✔
802
                    DataType::TDOUBLE => 82,
2✔
803
                    DataType::TCOMPLEX => 83,
×
804
                    DataType::TDBLCOMPLEX => 163,
×
805
                }
806
            }
807
        }
808
    };
809
}
810

811
datatype_into_impl!(u8);
812
datatype_into_impl!(i32);
813
datatype_into_impl!(u32);
814
datatype_into_impl!(i64);
815
datatype_into_impl!(u64);
816

817
/// Columns of different types
818
#[allow(missing_docs)]
819
pub enum Column {
820
    Int32 { name: String, data: Vec<i32> },
821
    Int64 { name: String, data: Vec<i64> },
822
    Float { name: String, data: Vec<f32> },
823
    Double { name: String, data: Vec<f64> },
824
    String { name: String, data: Vec<String> },
825
}
826

827
/// Iterator type for columns
828
pub struct ColumnIterator<'a> {
829
    current: usize,
830
    column_descriptions: Vec<ConcreteColumnDescription>,
831
    fits_file: &'a mut FitsFile,
832
}
833

834
impl<'a> ColumnIterator<'a> {
835
    pub(crate) fn new(fits_file: &'a mut FitsFile) -> Self {
1✔
836
        match fits_file.fetch_hdu_info() {
1✔
837
            Ok(HduInfo::TableInfo {
1✔
838
                column_descriptions,
×
839
                num_rows: _num_rows,
×
840
            }) => ColumnIterator {
841
                current: 0,
842
                column_descriptions,
843
                fits_file,
844
            },
845
            Err(e) => panic!("{:?}", e),
×
846
            _ => panic!("Unknown error occurred"),
×
847
        }
848
    }
849
}
850

851
impl<'a> Iterator for ColumnIterator<'a> {
852
    type Item = Column;
853

854
    fn next(&mut self) -> Option<Self::Item> {
1✔
855
        let ncols = self.column_descriptions.len();
1✔
856

857
        if self.current < ncols {
3✔
858
            let description = &self.column_descriptions[self.current];
1✔
859
            let current_name = description.name.as_str();
1✔
860
            // let current_type = typechar_to_data_type(description.data_type.as_str());
861
            let current_type = description.data_type.typ;
1✔
862

863
            let retval = match current_type {
1✔
864
                ColumnDataType::Int => i32::read_col(self.fits_file, current_name)
1✔
865
                    .map(|data| Column::Int32 {
2✔
866
                        name: current_name.to_string(),
1✔
867
                        data,
1✔
868
                    })
869
                    .ok(),
870
                ColumnDataType::Long => i64::read_col(self.fits_file, current_name)
×
871
                    .map(|data| Column::Int64 {
×
872
                        name: current_name.to_string(),
×
873
                        data,
×
874
                    })
875
                    .ok(),
876
                ColumnDataType::Float => f32::read_col(self.fits_file, current_name)
1✔
877
                    .map(|data| Column::Float {
2✔
878
                        name: current_name.to_string(),
1✔
879
                        data,
1✔
880
                    })
881
                    .ok(),
882
                ColumnDataType::Double => f64::read_col(self.fits_file, current_name)
1✔
883
                    .map(|data| Column::Double {
2✔
884
                        name: current_name.to_string(),
1✔
885
                        data,
1✔
886
                    })
887
                    .ok(),
888
                ColumnDataType::String => String::read_col(self.fits_file, current_name)
1✔
889
                    .map(|data| Column::String {
2✔
890
                        name: current_name.to_string(),
1✔
891
                        data,
1✔
892
                    })
893
                    .ok(),
894
                _ => unimplemented!(),
895
            };
896

897
            self.current += 1;
1✔
898

899
            retval
1✔
900
        } else {
901
            None
1✔
902
        }
903
    }
904
}
905

906
#[cfg(test)]
907
mod test {
908
    use super::*;
909
    use crate::testhelpers::{
910
        duplicate_test_file, floats_close_f32, floats_close_f64, with_temp_file,
911
    };
912

913
    #[test]
914
    fn test_parsing() {
915
        let s = "1E";
916
        assert_eq!(
917
            s.parse::<ColumnDataDescription>().unwrap(),
918
            ColumnDataDescription {
919
                repeat: 1,
920
                width: 1,
921
                typ: ColumnDataType::Float,
922
            }
923
        );
924
    }
925

926
    #[test]
927
    fn test_parse_many_repeats() {
928
        let s = "100E";
929
        assert_eq!(
930
            s.parse::<ColumnDataDescription>().unwrap(),
931
            ColumnDataDescription {
932
                repeat: 100,
933
                width: 1,
934
                typ: ColumnDataType::Float,
935
            }
936
        );
937
    }
938

939
    #[test]
940
    fn test_parse_with_width() {
941
        let s = "1E26";
942
        assert_eq!(
943
            s.parse::<ColumnDataDescription>().unwrap(),
944
            ColumnDataDescription {
945
                repeat: 1,
946
                width: 26,
947
                typ: ColumnDataType::Float,
948
            }
949
        );
950
    }
951

952
    #[test]
953
    fn test_creating_data_description() {
954
        let concrete_desc = ColumnDescription::new("FOO")
955
            .with_type(ColumnDataType::Int)
956
            .that_repeats(10)
957
            .create()
958
            .unwrap();
959
        assert_eq!(concrete_desc.name, "FOO".to_string());
960
        assert_eq!(concrete_desc.data_type.repeat, 10);
961
        assert_eq!(concrete_desc.data_type.width, 1);
962

963
        /* Do not call `with_type` */
964
        let bad_desc = ColumnDescription::new("FOO").create();
965
        assert!(bad_desc.is_err());
966
    }
967

968
    #[test]
969
    fn test_fetching_column_width() {
970
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
971
        f.hdu(1).unwrap();
972
        let width = column_display_width(&mut f, 3).unwrap();
973
        assert_eq!(width, 7);
974
    }
975

976
    #[test]
977
    fn test_read_columns() {
978
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
979
        let hdu = f.hdu(1).unwrap();
980
        let intcol_data: Vec<i32> = hdu.read_col(&mut f, "intcol").unwrap();
981
        assert_eq!(intcol_data[0], 18);
982
        assert_eq!(intcol_data[15], 10);
983
        assert_eq!(intcol_data[49], 12);
984

985
        let floatcol_data: Vec<f32> = hdu.read_col(&mut f, "floatcol").unwrap();
986
        assert!(
987
            floats_close_f32(floatcol_data[0], 17.496801),
988
            "{:?} != {:?}",
989
            floatcol_data[0],
990
            17.496801
991
        );
992
        assert!(
993
            floats_close_f32(floatcol_data[15], 19.570272),
994
            "{:?} != {:?}",
995
            floatcol_data[15],
996
            19.570272
997
        );
998
        assert!(
999
            floats_close_f32(floatcol_data[49], 10.217053),
1000
            "{:?} != {:?}",
1001
            floatcol_data[49],
1002
            10.217053
1003
        );
1004

1005
        let doublecol_data: Vec<f64> = hdu.read_col(&mut f, "doublecol").unwrap();
1006
        assert!(
1007
            floats_close_f64(doublecol_data[0], 16.959972808730814),
1008
            "{:?} != {:?}",
1009
            doublecol_data[0],
1010
            16.959972808730814
1011
        );
1012
        assert!(
1013
            floats_close_f64(doublecol_data[15], 19.013522579233065),
1014
            "{:?} != {:?}",
1015
            doublecol_data[15],
1016
            19.013522579233065
1017
        );
1018
        assert!(
1019
            floats_close_f64(doublecol_data[49], 16.61153656123406),
1020
            "{:?} != {:?}",
1021
            doublecol_data[49],
1022
            16.61153656123406
1023
        );
1024
    }
1025

1026
    #[test]
1027
    fn test_read_string_col() {
1028
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
1029
        let hdu = f.hdu(1).unwrap();
1030
        let strcol: Vec<String> = hdu.read_col(&mut f, "strcol").unwrap();
1031
        assert_eq!(strcol.len(), 50);
1032
        assert_eq!(strcol[0], "value0");
1033
        assert_eq!(strcol[15], "value15");
1034
        assert_eq!(strcol[49], "value49");
1035
    }
1036

1037
    #[test]
1038
    fn test_read_column_regions() {
1039
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
1040
        let hdu = f.hdu(1).unwrap();
1041
        let intcol_data: Vec<i32> = hdu.read_col_range(&mut f, "intcol", &(0..2)).unwrap();
1042
        assert_eq!(intcol_data.len(), 2);
1043
        assert_eq!(intcol_data[0], 18);
1044
        assert_eq!(intcol_data[1], 13);
1045
    }
1046

1047
    #[test]
1048
    fn test_read_invalid_column_range() {
1049
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
1050
        let hdu = f.hdu(1).unwrap();
1051
        match hdu.read_col_range::<i32>(&mut f, "intcol", &(0..1024)) {
1052
            Err(Error::Index(IndexError { message, given })) => {
1053
                assert_eq!(message, "given indices out of range".to_string());
1054
                assert_eq!(given, (0..1024));
1055
            }
1056
            _ => panic!("Should be error"),
1057
        }
1058
    }
1059

1060
    #[test]
1061
    fn test_read_string_column_regions() {
1062
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
1063
        let hdu = f.hdu(1).unwrap();
1064
        let intcol_data: Vec<String> = hdu.read_col_range(&mut f, "strcol", &(0..2)).unwrap();
1065
        assert_eq!(intcol_data.len(), 2);
1066
        assert_eq!(intcol_data[0], "value0");
1067
        assert_eq!(intcol_data[1], "value1");
1068
    }
1069

1070
    #[test]
1071
    fn test_read_column_region_check_ranges() {
1072
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
1073
        let hdu = f.hdu(1).unwrap();
1074
        let result_data: Result<Vec<i32>> = hdu.read_col_range(&mut f, "intcol", &(0..2_000_000));
1075
        assert!(result_data.is_err());
1076
    }
1077

1078
    #[test]
1079
    fn test_column_iterator() {
1080
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
1081
        let hdu = f.hdu(1).unwrap();
1082
        let column_names: Vec<String> = hdu
1083
            .columns(&mut f)
1084
            .map(|col| match col {
1085
                Column::Int32 { name, .. } => name,
1086
                Column::Int64 { name, .. } => name,
1087
                Column::Float { name, .. } => name,
1088
                Column::Double { name, .. } => name,
1089
                Column::String { name, .. } => name,
1090
            })
1091
            .collect();
1092

1093
        assert_eq!(
1094
            column_names,
1095
            vec![
1096
                "intcol".to_string(),
1097
                "floatcol".to_string(),
1098
                "doublecol".to_string(),
1099
                "strcol".to_string(),
1100
            ]
1101
        );
1102
    }
1103

1104
    #[test]
1105
    fn test_column_number() {
1106
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
1107
        let hdu = f.hdu("testext").unwrap();
1108
        assert_eq!(hdu.get_column_no(&mut f, "intcol").unwrap(), 0);
1109
        assert_eq!(hdu.get_column_no(&mut f, "floatcol").unwrap(), 1);
1110
        assert_eq!(hdu.get_column_no(&mut f, "doublecol").unwrap(), 2);
1111
    }
1112

1113
    #[test]
1114
    fn test_write_to_image() {
1115
        duplicate_test_file(|filename| {
1116
            let data_to_write: Vec<i32> = vec![10101; 10];
1117
            {
1118
                let mut f = FitsFile::edit(filename).unwrap();
1119
                let hdu = f.primary_hdu().unwrap();
1120
                match hdu.write_col(&mut f, "bar", &data_to_write) {
1121
                    Err(Error::Message(s)) => {
1122
                        assert_eq!(s, "Cannot write column data to FITS image");
1123
                    }
1124
                    s => unreachable!("should error: {:?}", s),
1125
                }
1126
            }
1127
        });
1128
    }
1129

1130
    #[test]
1131
    fn test_write_column_data() {
1132
        with_temp_file(|filename| {
1133
            let data_to_write: Vec<i32> = vec![10101; 10];
1134
            {
1135
                let mut f = FitsFile::create(filename).open().unwrap();
1136
                let table_description = vec![ColumnDescription::new("bar")
1137
                    .with_type(ColumnDataType::Int)
1138
                    .create()
1139
                    .unwrap()];
1140
                let hdu = f
1141
                    .create_table("foo".to_string(), &table_description)
1142
                    .unwrap();
1143

1144
                hdu.write_col(&mut f, "bar", &data_to_write).unwrap();
1145
            }
1146

1147
            let mut f = FitsFile::open(filename).unwrap();
1148
            let hdu = f.hdu("foo").unwrap();
1149
            let data: Vec<i32> = hdu.read_col(&mut f, "bar").unwrap();
1150
            assert_eq!(data, data_to_write);
1151
        });
1152
    }
1153

1154
    #[test]
1155
    fn test_write_column_subset() {
1156
        with_temp_file(|filename| {
1157
            let data_to_write: Vec<i32> = vec![10101; 10];
1158
            {
1159
                let mut f = FitsFile::create(filename).open().unwrap();
1160
                let table_description = vec![ColumnDescription::new("bar")
1161
                    .with_type(ColumnDataType::Int)
1162
                    .create()
1163
                    .unwrap()];
1164
                let hdu = f
1165
                    .create_table("foo".to_string(), &table_description)
1166
                    .unwrap();
1167

1168
                hdu.write_col_range(&mut f, "bar", &data_to_write, &(0..5))
1169
                    .unwrap();
1170
            }
1171

1172
            let mut f = FitsFile::open(filename).unwrap();
1173
            let hdu = f.hdu("foo").unwrap();
1174
            let data: Vec<i32> = hdu.read_col(&mut f, "bar").unwrap();
1175
            assert_eq!(data.len(), 5);
1176
            assert_eq!(data[..], data_to_write[0..5]);
1177
        });
1178
    }
1179

1180
    #[test]
1181
    fn test_write_string_col() {
1182
        with_temp_file(|filename| {
1183
            let mut data_to_write: Vec<String> = Vec::new();
1184
            for i in 0..50 {
1185
                data_to_write.push(format!("value{}", i));
1186
            }
1187

1188
            {
1189
                let mut f = FitsFile::create(filename).open().unwrap();
1190
                let table_description = vec![ColumnDescription::new("bar")
1191
                    .with_type(ColumnDataType::String)
1192
                    .that_repeats(7)
1193
                    .create()
1194
                    .unwrap()];
1195
                let hdu = f
1196
                    .create_table("foo".to_string(), &table_description)
1197
                    .unwrap();
1198

1199
                hdu.write_col(&mut f, "bar", &data_to_write).unwrap();
1200
            }
1201

1202
            let mut f = FitsFile::open(filename).unwrap();
1203
            let hdu = f.hdu("foo").unwrap();
1204
            let data: Vec<String> = hdu.read_col(&mut f, "bar").unwrap();
1205
            assert_eq!(data.len(), data_to_write.len());
1206
            assert_eq!(data[0], "value0");
1207
            assert_eq!(data[49], "value49");
1208
        });
1209
    }
1210

1211
    #[test]
1212
    fn test_write_string_col_range() {
1213
        with_temp_file(|filename| {
1214
            let mut data_to_write: Vec<String> = Vec::new();
1215
            for i in 0..50 {
1216
                data_to_write.push(format!("value{}", i));
1217
            }
1218

1219
            let range = 0..20;
1220
            {
1221
                let mut f = FitsFile::create(filename).open().unwrap();
1222
                let table_description = vec![ColumnDescription::new("bar")
1223
                    .with_type(ColumnDataType::String)
1224
                    .that_repeats(7)
1225
                    .create()
1226
                    .unwrap()];
1227
                let hdu = f
1228
                    .create_table("foo".to_string(), &table_description)
1229
                    .unwrap();
1230

1231
                hdu.write_col_range(&mut f, "bar", &data_to_write, &range)
1232
                    .unwrap();
1233
            }
1234

1235
            let mut f = FitsFile::open(filename).unwrap();
1236
            let hdu = f.hdu("foo").unwrap();
1237
            let data: Vec<String> = hdu.read_col(&mut f, "bar").unwrap();
1238
            assert_eq!(data.len(), range.end - range.start);
1239
            assert_eq!(data[0], "value0");
1240
            assert_eq!(data[19], "value19");
1241
        });
1242
    }
1243

1244
    #[test]
1245
    fn test_inserting_columns() {
1246
        duplicate_test_file(|filename| {
1247
            let mut f = FitsFile::edit(filename).unwrap();
1248
            let hdu = f.hdu("TESTEXT").unwrap();
1249

1250
            let coldesc = ColumnDescription::new("abcdefg")
1251
                .with_type(ColumnDataType::Int)
1252
                .create()
1253
                .unwrap();
1254

1255
            let newhdu = hdu.insert_column(&mut f, 0, &coldesc).unwrap();
1256

1257
            match newhdu.info {
1258
                HduInfo::TableInfo {
1259
                    column_descriptions,
1260
                    ..
1261
                } => {
1262
                    assert_eq!(column_descriptions[0].name, "abcdefg");
1263
                }
1264
                _ => panic!("ERROR"),
1265
            }
1266
        });
1267
    }
1268

1269
    #[test]
1270
    fn test_appending_columns() {
1271
        duplicate_test_file(|filename| {
1272
            let mut f = FitsFile::edit(filename).unwrap();
1273
            let hdu = f.hdu("TESTEXT").unwrap();
1274

1275
            let coldesc = ColumnDescription::new("abcdefg")
1276
                .with_type(ColumnDataType::Int)
1277
                .create()
1278
                .unwrap();
1279

1280
            let newhdu = hdu.append_column(&mut f, &coldesc).unwrap();
1281

1282
            match newhdu.info {
1283
                HduInfo::TableInfo {
1284
                    column_descriptions,
1285
                    ..
1286
                } => {
1287
                    assert_eq!(
1288
                        column_descriptions[column_descriptions.len() - 1].name,
1289
                        "abcdefg"
1290
                    );
1291
                }
1292
                _ => panic!("ERROR"),
1293
            }
1294
        });
1295
    }
1296

1297
    #[test]
1298
    fn test_deleting_columns_by_name() {
1299
        duplicate_test_file(|filename| {
1300
            let mut f = FitsFile::edit(filename).unwrap();
1301
            let hdu = f.hdu("TESTEXT").unwrap();
1302
            let newhdu = hdu.delete_column(&mut f, "intcol").unwrap();
1303

1304
            match newhdu.info {
1305
                HduInfo::TableInfo {
1306
                    column_descriptions,
1307
                    ..
1308
                } => {
1309
                    for col in column_descriptions {
1310
                        assert!(col.name != "intcol");
1311
                    }
1312
                }
1313
                _ => panic!("ERROR"),
1314
            }
1315
        });
1316
    }
1317

1318
    #[test]
1319
    fn test_deleting_columns_by_number() {
1320
        duplicate_test_file(|filename| {
1321
            let mut f = FitsFile::edit(filename).unwrap();
1322
            let hdu = f.hdu("TESTEXT").unwrap();
1323
            let newhdu = hdu.delete_column(&mut f, 0).unwrap();
1324

1325
            match newhdu.info {
1326
                HduInfo::TableInfo {
1327
                    column_descriptions,
1328
                    ..
1329
                } => {
1330
                    for col in column_descriptions {
1331
                        assert!(col.name != "intcol");
1332
                    }
1333
                }
1334
                _ => panic!("ERROR"),
1335
            }
1336
        });
1337
    }
1338

1339
    #[test]
1340
    fn test_read_single_table_value() {
1341
        let filename = "../testdata/full_example.fits[TESTEXT]";
1342
        let mut f = FitsFile::open(filename).unwrap();
1343
        let tbl_hdu = f.hdu("TESTEXT").unwrap();
1344

1345
        let result: i64 = tbl_hdu.read_cell_value(&mut f, "intcol", 4).unwrap();
1346
        assert_eq!(result, 16);
1347

1348
        let result: String = tbl_hdu.read_cell_value(&mut f, "strcol", 4).unwrap();
1349
        assert_eq!(result, "value4".to_string());
1350
    }
1351
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc