use anyhow::Context;
use anyhow::Result;
use serde::Deserialize;
use serde::Serialize;
use std::io::Write;
use std::ops::Range;
use std::path::Path;
use std::path::PathBuf;
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct Layout {
pub files: Vec<InputFile>,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct InputFile {
pub path: PathBuf,
pub archive_entry: Option<ArchiveEntryInfo>,
pub sections: Vec<Option<Section>>,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct ArchiveEntryInfo {
pub range: Range<usize>,
pub identifier: Vec<u8>,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct Section {
pub mem_range: Range<u64>,
}
impl Layout {
pub fn write(&self, writer: &mut impl Write) -> Result<()> {
postcard::to_io(self, writer)?;
Ok(())
}
pub fn to_bytes(&self) -> Result<Vec<u8>> {
Ok(postcard::to_stdvec(self)?)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
postcard::from_bytes(bytes).context("Invalid linker layout")
}
}
#[must_use]
pub fn layout_path(base_path: &Path) -> PathBuf {
let mut s = base_path.as_os_str().to_owned();
s.push(".layout");
PathBuf::from(s)
}
impl std::fmt::Display for InputFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.path.display().fmt(f)?;
if let Some(e) = self.archive_entry.as_ref() {
write!(f, " @ {}", String::from_utf8_lossy(&e.identifier))?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_round_trip() {
let layout = Layout {
files: vec![InputFile {
path: PathBuf::new(),
archive_entry: None,
sections: vec![Some(Section { mem_range: 42..48 })],
}],
};
let bytes = layout.to_bytes().unwrap();
let layout2 = Layout::from_bytes(&bytes).unwrap();
assert_eq!(layout, layout2);
}
}