pub struct Header { /* private fields */ }Expand description
Implementations§
Source§impl Header
impl Header
Sourcepub fn parse(bytes: &[u8]) -> Result<Header>
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));Sourcepub fn read_from_file<P: AsRef<Path>>(path: P) -> Result<Header>
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();Sourcepub fn update_file<P: AsRef<Path>>(
path: P,
edit: impl FnOnce(&mut Header) -> Result<()>,
) -> Result<()>
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();Sourcepub fn cards(&self) -> &[Record]
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"));Sourcepub fn iter(&self) -> impl Iterator<Item = &Record>
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"]);Sourcepub fn count(&self, name: &str) -> usize
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);Sourcepub fn get<T: FromCard>(&self, key: impl Into<Key>) -> Result<Option<T>>
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);Sourcepub fn get_str(&self, key: impl Into<Key>) -> Result<Option<&str>>
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);Sourcepub fn get_all<T: FromCard>(&self, name: &str) -> Vec<T>
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()]
);Sourcepub fn set(&mut self, key: impl Into<Key>, value: impl IntoValue) -> Result<()>
pub fn set(&mut self, key: impl Into<Key>, value: impl IntoValue) -> Result<()>
Update the addressed record in place, or append when the (unique) name is absent.
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 in place
assert_eq!(h.count("OBJECT"), 1);
let err = h.set("object", 1); // lowercase is not FITS-standard
assert!(matches!(err, Err(FitsError::InvalidKeyword { .. })));Sourcepub fn append(&mut self, name: &str, value: impl IntoValue) -> Result<()>
pub fn append(&mut self, name: &str, value: impl IntoValue) -> Result<()>
Always add a record (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);Sourcepub fn set_comment(
&mut self,
key: impl Into<Key>,
comment: impl Into<String>,
) -> Result<()>
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"));Sourcepub fn remove(&mut self, key: impl Into<Key>) -> Result<bool>
pub fn remove(&mut self, key: impl Into<Key>) -> Result<bool>
Remove the addressed record. 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());Sourcepub fn set_many<K, V>(
&mut self,
entries: impl IntoIterator<Item = (K, V)>,
) -> Result<()>
pub fn set_many<K, V>( &mut self, entries: impl IntoIterator<Item = (K, V)>, ) -> Result<()>
Apply several mutations 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);Sourcepub fn remove_many<K: Into<Key>>(
&mut self,
keys: impl IntoIterator<Item = K>,
) -> Result<usize>
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);Sourcepub fn to_header_bytes(&self) -> Vec<u8> ⓘ
pub fn to_header_bytes(&self) -> Vec<u8> ⓘ
Serialize the header block only (cards, END, padded to a 2880 multiple) for splicing onto
an existing file’s data.
§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);