1use std::{num::ParseIntError, str::FromStr};
2
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum ParsePermError {
7 #[error("permissions string must be 4 characters long, got {0}")]
8 InvalidLength(String),
9 #[error("invalid character \"{0}\" at position {1}")]
10 InvalidChar(char, usize),
11}
12
13#[derive(Debug, Clone)]
14pub struct Permissions {
15 pub read: bool,
16 pub write: bool,
17 pub execute: bool,
18 pub private: bool,
19}
20
21impl FromStr for Permissions {
22 type Err = ParsePermError;
23
24 fn from_str(s: &str) -> Result<Self, Self::Err> {
25 if s.len() != 4 {
26 return Err(ParsePermError::InvalidLength(s.to_string()));
27 }
28
29 let bytes = s.as_bytes();
30
31 let read = match bytes[0] {
32 b'r' => true,
33 b'-' => false,
34 other => return Err(ParsePermError::InvalidChar(other as char, 0)),
35 };
36 let write = match bytes[1] {
37 b'w' => true,
38 b'-' => false,
39 other => return Err(ParsePermError::InvalidChar(other as char, 1)),
40 };
41 let execute = match bytes[2] {
42 b'x' => true,
43 b'-' => false,
44 other => return Err(ParsePermError::InvalidChar(other as char, 2)),
45 };
46 let private = match bytes[3] {
47 b'p' => true,
48 b's' => false,
49 other => return Err(ParsePermError::InvalidChar(other as char, 3)),
50 };
51
52 Ok(Permissions {
53 read,
54 write,
55 execute,
56 private,
57 })
58 }
59}
60
61#[derive(Error, Debug)]
62pub enum ParseLibraryError {
63 #[error("expected at least 6 whitespace-separated fields, got {0}")]
64 FieldCount(usize),
65 #[error("address field was invalid")]
66 InvalidAddress,
67 #[error("permissions field was invalid")]
68 InvalidPermissions(#[from] ParsePermError),
69 #[error("offset field was invalid")]
70 InvalidOffset(ParseIntError),
71 #[error("inode field was invalid")]
72 InvalidInode(ParseIntError),
73}
74
75#[derive(Debug, Clone)]
76pub struct LibraryInfo {
77 start: usize,
79 end: usize,
81 permissions: Permissions,
83 offset: usize,
85 device: String,
87 inode: u64,
88 pathname: Option<String>,
98}
99
100impl LibraryInfo {
101 pub fn start(&self) -> usize {
102 self.start
103 }
104
105 pub fn end(&self) -> usize {
106 self.end
107 }
108
109 pub fn permissions(&self) -> &Permissions {
110 &self.permissions
111 }
112
113 pub fn offset(&self) -> usize {
114 self.offset
115 }
116
117 pub fn device(&self) -> &str {
118 &self.device
119 }
120
121 pub fn inode(&self) -> u64 {
122 self.inode
123 }
124
125 pub fn path(&self) -> Option<&str> {
126 self.pathname.as_deref()
127 }
128}
129
130impl FromStr for LibraryInfo {
131 type Err = ParseLibraryError;
132
133 fn from_str(s: &str) -> Result<Self, Self::Err> {
134 let mut parts = s.splitn(6, char::is_whitespace).filter(|s| !s.is_empty());
137
138 let address = parts.next().ok_or(ParseLibraryError::FieldCount(0))?;
139 let permissions = parts.next().ok_or(ParseLibraryError::FieldCount(1))?;
140 let offset = parts.next().ok_or(ParseLibraryError::FieldCount(2))?;
141 let device = parts.next().ok_or(ParseLibraryError::FieldCount(3))?;
142 let inode = parts.next().ok_or(ParseLibraryError::FieldCount(4))?;
143 let path = parts.next();
144
145 let Some((start, end)) = address.split_once('-') else {
146 return Err(ParseLibraryError::InvalidAddress);
147 };
148
149 let start =
150 usize::from_str_radix(start, 16).map_err(|_| ParseLibraryError::InvalidAddress)?;
151 let end = usize::from_str_radix(end, 16).map_err(|_| ParseLibraryError::InvalidAddress)?;
152
153 let permissions = permissions.parse()?;
154
155 let offset = usize::from_str_radix(offset, 16).map_err(ParseLibraryError::InvalidOffset)?;
156 let device = device.to_string();
157 let inode = inode.parse().map_err(ParseLibraryError::InvalidInode)?;
158 let pathname = path
159 .map(str::trim)
160 .filter(|s| !s.is_empty())
161 .map(str::to_string);
162
163 Ok(Self {
164 start,
165 end,
166 permissions,
167 offset,
168 device,
169 inode,
170 pathname,
171 })
172 }
173}