Skip to main content

Header

Struct Header 

Source
pub struct Header { /* private fields */ }
Expand description

An ordered FITS header unit: Records in appearance order, with strict keyword access (via Key) and CRUD.

A Header is an in-memory value. set, append, remove, set_many, and set_comment change it in memory only — nothing is written to disk. Persist it with update_file to edit an existing file’s header in place (the common case), or write_to_file to create a new file from a header you built plus pixel data you already have. to_header_bytes is the lower-level building block behind both, for callers who assemble the file bytes themselves.

Equality is semantic (records compare by content, not by retained bytes).

Implementations§

Source§

impl Header

Source

pub fn new() -> Self

An empty header.

§Examples
let h = Header::new();
assert_eq!(h.count("OBJECT"), 0);
Source

pub fn parse(bytes: &[u8]) -> Result<Header>

Parse one FITS header unit from raw bytes.

Reads 80-byte cards in order, stops at END, and retains every card (including commentary, HIERARCH, and unrecognized cards) so untouched cards serialize verbatim. CONTINUE runs are reassembled into a single logical value. Bytes after END (the data unit, later HDUs) are ignored — this crate is header-only and never inspects them.

§Examples
use fits_header::Header;

let mut bytes = Vec::new();
for card in ["OBJECT  = 'M31     '", "EXPTIME =                120.0", "END"] {
    let mut c = card.as_bytes().to_vec();
    c.resize(80, b' ');
    bytes.extend(c);
}

let header = Header::parse(&bytes).unwrap();
assert_eq!(header.get_str("OBJECT").unwrap(), Some("M31"));
assert_eq!(header.get::<f64>("EXPTIME").unwrap(), Some(120.0));
Source

pub fn read_from_file<P: AsRef<Path>>(path: P) -> Result<Header>

Read a FITS header from a file on disk.

Reads the whole file and parses it; parsing already stops at END, so the data unit and any later HDUs are read but never interpreted.

§Examples
use fits_header::Header;

let mut bytes = Vec::new();
for card in ["OBJECT  = 'M31     '", "END"] {
    let mut c = card.as_bytes().to_vec();
    c.resize(80, b' ');
    bytes.extend(c);
}
while bytes.len() % fits_header::BLOCK_LEN != 0 {
    bytes.push(b' ');
}
bytes.extend_from_slice(&[0u8; 4]); // stand-in pixel data

let path = std::env::temp_dir().join("fits-header-doctest-read_from_file.fits");
std::fs::write(&path, &bytes).unwrap();

let header = Header::read_from_file(&path).unwrap();
assert_eq!(header.get_str("OBJECT").unwrap(), Some("M31"));

std::fs::remove_file(&path).ok();
Source

pub fn update_file<P: AsRef<Path>>( path: P, edit: impl FnOnce(&mut Header) -> Result<()>, ) -> Result<()>

Edit a FITS file’s header in place, preserving its data unit (and any later HDUs) byte-for-byte.

Reads the whole file, locates the header region by scanning for the END card, parses only that region, runs edit on it, then writes the re-serialized header back followed by every byte that came after the original header — untouched, regardless of what edit did.

The write is atomic and edits the real file in place: it writes a temp file in the target’s directory and renames it over the target. It follows symlinks (a symlinked path stays a symlink; its target is edited) and, on Unix, preserves the target’s file mode. A crash or interruption cannot leave a truncated file.

Errors with FitsError::MissingEnd if the file has no END card, or FitsError::TruncatedHeader if it has one but ends before the header’s 2880-byte block is complete.

§Examples
use fits_header::Header;

let mut bytes = Vec::new();
for card in ["OBJECT  = 'M31     '", "END"] {
    let mut c = card.as_bytes().to_vec();
    c.resize(80, b' ');
    bytes.extend(c);
}
while bytes.len() % fits_header::BLOCK_LEN != 0 {
    bytes.push(b' ');
}
let data = [1u8, 2, 3, 4]; // stand-in pixel data
bytes.extend_from_slice(&data);

let path = std::env::temp_dir().join("fits-header-doctest-update_file.fits");
std::fs::write(&path, &bytes).unwrap();

Header::update_file(&path, |h| {
    h.set("OBJECT", "NGC 7000")?;
    Ok(())
})
.unwrap();

let after = std::fs::read(&path).unwrap();
let header = Header::parse(&after).unwrap();
assert_eq!(header.get_str("OBJECT").unwrap(), Some("NGC 7000"));
assert_eq!(&after[after.len() - data.len()..], &data, "data unit preserved");

std::fs::remove_file(&path).ok();
Source

pub fn write_to_file<P: AsRef<Path>>(&self, path: P, data: &[u8]) -> Result<()>

Create a new FITS file: the serialized header block followed by data.

This is the convenience for the rarer case where you already have pixel data and are writing it for the first time. It creates path and errors if it already exists — via OpenOptions::create_new, which is race-free — so this method can never overwrite or corrupt an existing file’s contents. To edit a file that already exists, use update_file instead.

data is the caller’s own pixel bytes, written immediately after the header block; pass &[] for a header-only file. This crate is header-only and never fabricates pixel data itself.

Errors with FitsError::Io if the write fails, including when path already exists (io::ErrorKind::AlreadyExists).

§Examples
use fits_header::Header;

let mut header = Header::new();
header.set("OBJECT", "M31").unwrap();

let path = std::env::temp_dir().join("fits-header-doctest-write_to_file.fits");
header.write_to_file(&path, &[0u8; 4]).unwrap();

let bytes = std::fs::read(&path).unwrap();
assert_eq!(&bytes[bytes.len() - 4..], &[0u8; 4], "pixel data survived");
let back = Header::parse(&bytes).unwrap();
assert_eq!(back.get_str("OBJECT").unwrap(), Some("M31"));

// Writing to the same path again errors instead of overwriting it.
assert!(header.write_to_file(&path, &[1u8; 4]).is_err());

std::fs::remove_file(&path).ok();
Source

pub fn cards(&self) -> &[Record]

The records in order (read-only escape hatch).

§Examples
let mut h = Header::new();
h.set("OBJECT", "M31").unwrap();
assert_eq!(h.cards()[0].keyword(), Some("OBJECT"));
Source

pub fn iter(&self) -> impl Iterator<Item = &Record>

Iterate the records in order.

§Examples
let mut h = Header::new();
h.set("OBJECT", "M31").unwrap();
h.set("EXPTIME", 120.0).unwrap();
let names: Vec<&str> = h.iter().filter_map(|r| r.keyword()).collect();
assert_eq!(names, vec!["OBJECT", "EXPTIME"]);
Source

pub fn count(&self, name: &str) -> usize

How many records carry this keyword.

§Examples
let mut h = Header::new();
h.append("HISTORY", "dark subtracted").unwrap();
h.append("HISTORY", "flat fielded").unwrap();
assert_eq!(h.count("HISTORY"), 2);
assert_eq!(h.count("OBJECT"), 0);
Source

pub fn get<T: FromCard>(&self, key: impl Into<Key>) -> Result<Option<T>>

Read a keyword as T. Err only on an ambiguous bare name; Ok(None) when absent or the value does not convert; never panics.

§Examples
let mut h = Header::new();
h.set("EXPTIME", 120.0).unwrap();
assert_eq!(h.get::<f64>("EXPTIME").unwrap(), Some(120.0));
assert_eq!(h.get::<i64>("MISSING").unwrap(), None);
Source

pub fn get_str(&self, key: impl Into<Key>) -> Result<Option<&str>>

Borrow a keyword’s string value (Str content, non-empty); None for empty or a literal.

§Examples
let mut h = Header::new();
h.set("OBJECT", "M31").unwrap();
h.set("EXPTIME", 120.0).unwrap(); // a Literal, not Str content
assert_eq!(h.get_str("OBJECT").unwrap(), Some("M31"));
assert_eq!(h.get_str("EXPTIME").unwrap(), None);
Source

pub fn get_all<T: FromCard>(&self, name: &str) -> Vec<T>

Every value for a keyword, in order. Unlike get, never errors on a duplicated keyword — that is the point of calling it.

§Examples
let mut h = Header::new();
h.append("HISTORY", "dark subtracted").unwrap();
h.append("HISTORY", "flat fielded").unwrap();
assert_eq!(
    h.get_all::<String>("HISTORY"),
    vec!["dark subtracted".to_string(), "flat fielded".to_string()]
);
Source

pub fn set(&mut self, key: impl Into<Key>, value: impl IntoValue) -> Result<()>

Updates the addressed record (or appends when the unique name is absent) in the in-memory header; nothing is written to disk — see Header to persist. The keyword must be FITS-standard (≤8, A-Z 0-9 - _); use set_raw for vendor keys.

§Examples
let mut h = Header::new();
h.set("OBJECT", "M31").unwrap(); // appends
h.set("OBJECT", "NGC 7000").unwrap(); // updates the in-memory record
assert_eq!(h.count("OBJECT"), 1);

let err = h.set("object", 1); // lowercase is not FITS-standard
assert!(matches!(err, Err(FitsError::InvalidKeyword { .. })));
Source

pub fn set_raw(&mut self, keyword: &str, value: impl IntoValue) -> Result<()>

Like set but accepts any ≤8-char printable-ASCII keyword (vendor escape hatch).

§Examples
let mut h = Header::new();
h.set_raw("pi.name", "Jane Doe").unwrap(); // lowercase, not FITS-standard
assert_eq!(h.get_str("pi.name").unwrap(), Some("Jane Doe"));
Source

pub fn append(&mut self, name: &str, value: impl IntoValue) -> Result<()>

Always add a record to the in-memory header (a value card, or a commentary card for COMMENT/HISTORY/blank).

§Examples
let mut h = Header::new();
h.append("HISTORY", "dark subtracted").unwrap();
h.append("HISTORY", "flat fielded").unwrap();
assert_eq!(h.get_all::<String>("HISTORY").len(), 2);
Source

pub fn set_comment( &mut self, key: impl Into<Key>, comment: impl Into<String>, ) -> Result<()>

Set or replace the addressed value card’s inline comment. No-op if the keyword is absent or not a value card.

§Examples
let mut h = Header::new();
h.set("EXPTIME", 120.0).unwrap();
h.set_comment("EXPTIME", "seconds").unwrap();
assert_eq!(h.cards()[0].comment(), Some("seconds"));
Source

pub fn remove(&mut self, key: impl Into<Key>) -> Result<bool>

Remove the addressed record from the in-memory header. Returns whether anything was removed.

§Examples
let mut h = Header::new();
h.set("AIRMASS", 1.2).unwrap();
assert!(h.remove("AIRMASS").unwrap());
assert!(!h.remove("AIRMASS").unwrap());
Source

pub fn set_many<K, V>( &mut self, entries: impl IntoIterator<Item = (K, V)>, ) -> Result<()>
where K: Into<Key>, V: IntoValue,

Apply several mutations to the in-memory header atomically: validate every entry first, then apply all or none.

§Examples
let mut h = Header::new();
h.set_many([("FILTER", "Ha"), ("TELESCOP", "EdgeHD 8")]).unwrap();

// A rejected batch leaves the header untouched.
assert!(h.set_many([("GAIN", "1"), ("TOOLONGKEY", "2")]).is_err());
assert_eq!(h.count("GAIN"), 0);
Source

pub fn remove_many<K: Into<Key>>( &mut self, keys: impl IntoIterator<Item = K>, ) -> Result<usize>

Remove several keys atomically (validation only guards ambiguity). Returns the count removed.

§Examples
let mut h = Header::new();
h.set("OBJECT", "M31").unwrap();
h.set("EXPTIME", 120.0).unwrap();
assert_eq!(h.remove_many(["OBJECT", "MISSING"]).unwrap(), 1);
assert_eq!(h.count("OBJECT"), 0);
Source

pub fn to_header_bytes(&self) -> Vec<u8>

Serialize the header block only (cards, END, padded to a 2880 multiple).

This is the lower-level building block: the bytes alone, for callers who assemble the rest of the file themselves. To write a header plus pixel data straight to a new file, use write_to_file instead — it wraps this and errors rather than overwriting an existing path. update_file edits an existing file’s header while preserving its data unit.

§Examples
let mut h = Header::new();
h.set("OBJECT", "M31").unwrap();
let bytes = h.to_header_bytes();
assert_eq!(bytes.len() % fits_header::BLOCK_LEN, 0);

Trait Implementations§

Source§

impl Clone for Header

Source§

fn clone(&self) -> Header

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Header

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Header

Source§

fn default() -> Header

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for Header

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for Header

Source§

fn eq(&self, other: &Header) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for Header

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Header

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.