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