duckbubble/
orwritekey.rs

1use std::fs;
2use std::fs::DirEntry;
3use std::io;
4use std::io::prelude::*;
5use std::io::Cursor;
6use std::path::Path;
7use std::str::FromStr;
8
9pub struct KeywordReader<R: AsRef<[u8]>>(Cursor<R>);
10
11pub struct PartReader<R: AsRef<[u8]>>(pub KeywordReader<R>);
12
13// There is no boundary check!!
14impl<R: AsRef<[u8]>> Iterator for PartReader<R> {
15    type Item = Option<(String, u64)>;
16
17    fn next(&mut self) -> Option<Self::Item> {
18        match self.0.read_keyword_a().1 {
19            Keyword::Part => Some(Some(self.0.process_part())),
20            Keyword::End => None,
21            _ => Some(None),
22        }
23    }
24}
25
26impl<R: AsRef<[u8]>> KeywordReader<R> {
27    pub fn new(stream: R) -> Self {
28        KeywordReader(Cursor::new(stream))
29    }
30    /// cursor move a char, return read char
31    pub fn read_char(&mut self) -> u8 {
32        let mut char_buf = [b' '; 1];
33        self.0.read_exact(&mut char_buf).expect("!reading a char!");
34        char_buf[0]
35    }
36    /// cursor move after \n, return read string
37    pub fn read_line(&mut self) -> String {
38        let mut buf = String::new();
39        self.0.read_line(&mut buf).expect("!reading a line!");
40        buf
41    }
42    /// seek head postion
43    pub fn seek_head(&self) -> u64 {
44        self.0.position()
45    }
46    pub fn seek_foward(&mut self, n: u64) {
47        self.0.set_position(self.seek_head() + n)
48    }
49    pub fn seek_back(&mut self, n: u64) {
50        self.0.set_position(self.seek_head() - n)
51    }
52    /// read till next *
53    fn find_keyword(&mut self) {
54        while self.read_char() != b'*' {
55            continue;
56        }
57    }
58    /// cursor at '*'
59    pub fn read_keyword_a(&mut self) -> (u64, Keyword) {
60        self.find_keyword();
61        (
62            self.seek_head() - 1,
63            self.read_line()
64                .parse::<Keyword>()
65                .expect("parse readed keyword"),
66        )
67    }
68    /// find the first desired keyword, set cursor at `*`
69    pub fn find_kwd_a(&mut self, kwd: Keyword) -> u64 {
70        let (head, k) = self.read_keyword_a();
71        if kwd as i32 == k as i32 {
72            head
73        } else {
74            self.find_kwd_a(kwd)
75        }
76    }
77    /// read until no '$' at line start
78    fn consume_comment_line(&mut self) {
79        loop {
80            if self.read_char() == b'$' {
81                //TODO: can use comment line to help locating and do hell more stuff
82                self.read_line();
83            } else {
84                self.seek_back(1);
85                break;
86            }
87        }
88    }
89    /// e.g. "MAT_ELASTIC" return "MAT"
90    fn consume_prefix(&mut self) -> String {
91        let ln = self.read_line();
92        let v: Vec<&str> = ln.trim().split(|c| c == '-' || c == '_').collect();
93        //FIXME: currently only the prefix of name is taken into consideration
94        v.first().expect("keyword should has title").to_string()
95    }
96    /// `MAT` & `SECTION` id cursor position in key file
97    /// tail recursively
98    pub fn process_part_attri(&mut self) -> u64 {
99        self.find_keyword();
100        let pref = self.consume_prefix();
101        let s = pref.as_str();
102        match s {
103            "MAT" | "SECTION" => {
104                self.consume_comment_line();
105                self.seek_head()
106            }
107            _ => self.process_part_attri(),
108        }
109    }
110    /// after located keyword, return position to be rewrite
111    pub fn process_part(&mut self) -> (String, u64) {
112        // below keyword, may have a comment line
113        self.consume_comment_line();
114        let name = self.read_line().trim().to_string();
115        //TODO: write toml read info into model
116        self.consume_comment_line();
117        // now we at the beginning of line keycells
118        dbg!(name, self.seek_head())
119    }
120}
121
122#[derive(Debug, Clone, Copy)]
123pub enum Keyword {
124    // *,&,name,$,cells
125    Part,
126    Shell,
127    Solid,
128    SetNode,
129    End,
130    Undefined,
131}
132
133#[derive(Debug)]
134pub enum KwdErr {
135    Undefined,
136}
137
138impl FromStr for Keyword {
139    type Err = KwdErr;
140
141    fn from_str(s: &str) -> Result<Self, Self::Err> {
142        match s.trim() {
143            "PART" => Ok(Self::Part),
144            "SECTION_SHELL" => Ok(Self::Shell),
145            "SECTION_SOLID" => Ok(Self::Solid),
146            "SET_NODE_LIST" => Ok(Self::SetNode),
147            "END" => Ok(Self::End),
148            _ => Ok(Self::Undefined),
149        }
150    }
151}
152
153// one possible implementation of walking a directory only visiting files
154pub fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> {
155    if dir.is_dir() {
156        for entry in fs::read_dir(dir)? {
157            let entry = entry?;
158            let path = entry.path();
159            if path.is_dir() {
160                visit_dirs(&path, cb)?;
161            } else {
162                cb(&entry);
163            }
164        }
165    }
166    Ok(())
167}
168
169pub fn print_dir(dir: &Path) -> io::Result<()> {
170    let print_path = |f: &DirEntry| println!("{}", f.path().display());
171    visit_dirs(dir, &print_path)?;
172    Ok(())
173}