1use std::{
2 fmt::Display,
3 fs::File,
4 io::{BufRead as _, BufReader, Lines},
5 path::Path,
6 str::SplitWhitespace,
7};
8
9use crate::util::io::{FileError, open_file};
10
11pub mod config;
12pub mod delinks;
13pub mod link_time_const;
14pub mod module;
15pub mod relocations;
16pub mod section;
17pub mod symbol;
18
19#[derive(Debug, Clone)]
20pub struct ParseContext {
21 pub file_path: String,
22 pub row: usize,
23}
24
25impl Display for ParseContext {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 write!(f, "{}:{}", self.file_path, self.row)
28 }
29}
30
31impl From<&ParseContext> for ParseContext {
33 fn from(val: &ParseContext) -> Self {
34 val.clone()
35 }
36}
37impl From<&mut ParseContext> for ParseContext {
38 fn from(val: &mut ParseContext) -> Self {
39 val.clone()
40 }
41}
42
43pub(crate) fn iter_attributes(words: SplitWhitespace<'_>) -> ParseAttributesIterator<'_> {
44 ParseAttributesIterator { words }
45}
46
47pub(crate) struct ParseAttributesIterator<'a> {
48 words: SplitWhitespace<'a>,
49}
50
51impl<'a> Iterator for ParseAttributesIterator<'a> {
52 type Item = (&'a str, &'a str);
53
54 fn next(&mut self) -> Option<Self::Item> {
55 let word = self.words.next()?;
56 Some(word.split_once(':').unwrap_or((word, "")))
57 }
58}
59
60#[derive(Clone, PartialEq, Eq)]
61pub struct Comments {
62 pub pre_lines: Vec<String>,
64 pub post_comment: Option<String>,
66}
67
68impl Comments {
69 pub fn new() -> Self {
70 Self { pre_lines: Vec::new(), post_comment: None }
71 }
72
73 pub fn display_pre_comments(&self) -> DisplayPreComments<'_> {
74 DisplayPreComments { comments: self }
75 }
76
77 pub fn display_post_comment(&self) -> DisplayPostComment<'_> {
78 DisplayPostComment { comments: self }
79 }
80
81 pub fn remove_leading_blank_lines(&mut self) {
82 let non_blank_index = self
83 .pre_lines
84 .iter()
85 .position(|line| !line.trim().is_empty())
86 .unwrap_or(self.pre_lines.len());
87 self.pre_lines.drain(0..non_blank_index);
88 }
89}
90
91pub struct DisplayPreComments<'a> {
92 comments: &'a Comments,
93}
94
95impl Display for DisplayPreComments<'_> {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 for pre_line in &self.comments.pre_lines {
98 writeln!(f, "{pre_line}")?;
99 }
100 Ok(())
101 }
102}
103
104pub struct DisplayPostComment<'a> {
105 comments: &'a Comments,
106}
107
108impl Display for DisplayPostComment<'_> {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 if let Some(post_comment) = &self.comments.post_comment {
111 write!(f, " {post_comment}")?;
112 }
113 Ok(())
114 }
115}
116
117pub struct CommentedLine {
118 pub text: String,
120 pub row: usize,
121 pub comments: Comments,
122}
123
124impl CommentedLine {
125 pub fn read<P>(path: P) -> Result<CommentedLineIterator, FileError>
126 where
127 P: AsRef<Path>,
128 {
129 let path = path.as_ref();
130 let file = open_file(path)?;
131 let reader = BufReader::new(file);
132 let lines = reader.lines();
133 Ok(CommentedLineIterator { lines, row: 0 })
134 }
135}
136
137pub struct CommentedLineIterator {
138 lines: Lines<BufReader<File>>,
139 row: usize,
140}
141
142impl Iterator for CommentedLineIterator {
143 type Item = Result<CommentedLine, std::io::Error>;
144
145 fn next(&mut self) -> Option<Self::Item> {
146 let mut pre_lines = Vec::new();
147 for line in self.lines.by_ref() {
148 self.row += 1;
149 let line = match line {
150 Ok(line) => line,
151 Err(e) => return Some(Err(e)),
152 };
153 let trimmed = line.trim();
154 if trimmed.is_empty() || trimmed.starts_with("//") {
155 pre_lines.push(line);
156 continue;
157 }
158 let (text, post_comment) = if let Some(comment_index) = line.find("//") {
159 let (text, post_comment) = line.split_at(comment_index);
160 (text, Some(post_comment.trim().to_string()))
161 } else {
162 (line.as_str(), None)
163 };
164 let text = text.to_string();
165 return Some(Ok(CommentedLine {
166 text,
167 row: self.row,
168 comments: Comments { pre_lines, post_comment },
169 }));
170 }
171 None
172 }
173}