Skip to main content

ds_decomp/config/
mod.rs

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
31// Shorthand for snafu errors
32impl 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    /// Lines of comments or blank lines that precede the main text line.
63    pub pre_lines: Vec<String>,
64    /// Comment at the end of the main text line.
65    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    /// The main text without comments.
119    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}