1use anyhow::Context;
4use anyhow::Result;
5use serde::Deserialize;
6use serde::Serialize;
7use std::io::Write;
8use std::ops::Range;
9use std::path::Path;
10use std::path::PathBuf;
11
12#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
13pub struct Layout {
14 pub files: Vec<InputFile>,
16}
17
18#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
19pub struct InputFile {
20 pub path: PathBuf,
22
23 pub archive_entry: Option<ArchiveEntryInfo>,
26
27 pub sections: Vec<Option<Section>>,
30}
31
32#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
33pub struct ArchiveEntryInfo {
34 pub range: Range<usize>,
36
37 pub identifier: Vec<u8>,
38}
39
40#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
41pub struct Section {
42 pub mem_range: Range<u64>,
43}
44
45impl Layout {
46 pub fn write(&self, writer: &mut impl Write) -> Result<()> {
47 postcard::to_io(self, writer)?;
48 Ok(())
49 }
50
51 pub fn to_bytes(&self) -> Result<Vec<u8>> {
52 Ok(postcard::to_stdvec(self)?)
53 }
54
55 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
56 postcard::from_bytes(bytes).context("Invalid linker layout")
57 }
58}
59
60#[must_use]
61pub fn layout_path(base_path: &Path) -> PathBuf {
62 let mut s = base_path.as_os_str().to_owned();
65 s.push(".layout");
66 PathBuf::from(s)
67}
68
69impl std::fmt::Display for InputFile {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 self.path.display().fmt(f)?;
72 if let Some(e) = self.archive_entry.as_ref() {
73 write!(f, " @ {}", String::from_utf8_lossy(&e.identifier))?;
74 }
75 Ok(())
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_round_trip() {
85 let layout = Layout {
86 files: vec![InputFile {
87 path: PathBuf::new(),
88 archive_entry: None,
89 sections: vec![Some(Section { mem_range: 42..48 })],
90 }],
91 };
92 let bytes = layout.to_bytes().unwrap();
93 let layout2 = Layout::from_bytes(&bytes).unwrap();
94 assert_eq!(layout, layout2);
95 }
96}