igo/
util.rs

1use std::convert::From;
2use std::error::Error;
3use std::fs::{self, File};
4use std::io::{self, BufRead, BufReader};
5use std::mem;
6use std::path::Path;
7
8use byteorder::{NativeEndian as NE, ReadBytesExt, WriteBytesExt};
9use encoding_rs::*;
10#[cfg(feature = "zip")]
11use zip::ZipArchive;
12#[cfg(feature = "zip")]
13use std::io::{Cursor, Read};
14
15use crate::dictionary::build::*;
16use crate::Utf16Char;
17
18pub trait InputUtil: io::Read {
19    fn get_int(&mut self) -> io::Result<i32> {
20        self.read_i32::<NE>()
21    }
22
23    fn get_int_array(&mut self, count: usize) -> io::Result<Box<[i32]>> {
24        let mut v = vec![0i32; count];
25        for i in 0..count {
26            v[i] = self.read_i32::<NE>()?;
27        }
28        Ok(v.into_boxed_slice())
29    }
30
31    fn get_short_array(&mut self, count: usize) -> io::Result<Box<[i16]>> {
32        let mut v = vec![0i16; count];
33        for i in 0..count {
34            v[i] = self.read_i16::<NE>()?;
35        }
36        Ok(v.into_boxed_slice())
37    }
38
39    fn get_char_array(&mut self, count: usize) -> io::Result<Box<[Utf16Char]>> {
40        let mut v = vec![0u16; count];
41        for i in 0..count {
42            v[i] = self.read_u16::<NE>()?;
43        }
44        Ok(v.into_boxed_slice())
45    }
46
47    fn get_string(&mut self, count: usize) -> io::Result<Box<[Utf16Char]>> {
48        let mut v = vec![0u16; count];
49        for i in 0..count {
50            v[i] = self.read_u16::<NE>()?;
51        }
52        Ok(v.into_boxed_slice())
53    }
54}
55
56impl<R: io::Read + ?Sized> InputUtil for R {}
57
58
59pub fn read_all_as_chars(dir: &mut dyn DirLike, path: &str) -> io::Result<Box<[Utf16Char]>> {
60    let file_len = dir.file_size(path)?;
61    let file = dir.open(path)?;
62    let mut reader = BufReader::new(file);
63    reader.get_string((file_len as usize) / mem::size_of::<u16>())
64}
65
66pub fn read_all_as_int_array(dir: &mut dyn DirLike, path: &str) -> io::Result<Box<[i32]>> {
67    let file_len = dir.file_size(path)?;
68    let file = dir.open(path)?;
69    let mut reader = BufReader::new(file);
70    reader.get_int_array((file_len as usize) / mem::size_of::<i32>())
71}
72
73
74pub trait OutputUtil: io::Write {
75    fn put_string(&mut self, str: &[Utf16Char]) -> io::Result<()> {
76        for i in 0..str.len() {
77            self.write_u16::<NE>(str[i])?;
78        }
79        Ok(())
80    }
81}
82
83impl<W: io::Write + ?Sized> OutputUtil for W {}
84
85
86pub struct ReadLine<'a> {
87    reader: BufReader<File>,
88    line_number: i32,
89    path: &'a Path,
90    decoder: Option<&'static Encoding>,
91    encoded_buf: Vec<u8>,
92}
93
94impl<'a> ReadLine<'a> {
95    pub fn new(file_path: &'a Path, encoding_name: &str) -> AppResult<ReadLine<'a>> {
96        let file = File::open(file_path)?;
97        let encoding = Encoding::for_label(encoding_name.as_bytes())
98            .ok_or_else(|| format!("Unknown encoding; {}", encoding_name))?;
99
100//        debug!("encoding.name: {}", encoding.name());
101        Ok(ReadLine {
102            reader: BufReader::new(file),
103            line_number: 0,
104            path: file_path,
105            decoder: if encoding != UTF_8 { Some(encoding) } else { None },
106            encoded_buf: Vec::new(),
107        })
108    }
109
110    pub fn next(&mut self, read_buf: &mut String) -> AppResult<usize> {
111        match self.decoder {
112            Some(encoding) =>
113                self.next_with_decoder(encoding.new_decoder_without_bom_handling(), read_buf),
114            None => self.next_without_decoder(read_buf)
115        }
116    }
117
118    fn next_without_decoder(&mut self, read_buf: &mut String) -> AppResult<usize> {
119        read_buf.clear();
120        let r = self.reader.read_line(read_buf);
121        if r.as_ref().map(|len| *len > 0).unwrap_or(false) {
122            self.line_number += 1;
123        }
124        r.map_err(AppError::from)
125    }
126
127    fn next_with_decoder(&mut self, mut decoder: Decoder, decode_buf: &mut String) -> AppResult<usize> {
128        self.encoded_buf.clear();
129        decode_buf.clear();
130        let len = self.reader.read_until(b'\n', self.encoded_buf.as_mut()).map_err(AppError::from)?;
131        if len < 1 {
132            Ok(0) // EOF
133        } else {
134            decode_buf.reserve(decoder.max_utf8_buffer_length_without_replacement(len).expect("overflow"));
135            let (result, _) =
136                decoder.decode_to_string_without_replacement(&self.encoded_buf, decode_buf, true);
137            match result {
138                DecoderResult::InputEmpty => {
139                    self.line_number += 1;
140                    Ok(decode_buf.len())
141                }
142                DecoderResult::Malformed(_, _) => Err(AppError::from("Decoder Malformed")),
143                DecoderResult::OutputFull => Err(AppError::from("Decoder OutputFull"))
144            }
145        }
146    }
147
148    pub fn parse_error<S: Into<String>>(&self, msg: S) -> AppError {
149        AppError::Parse {
150            message: msg.into(),
151            path: self.path.to_path_buf(),
152            line_number: self.line_number,
153        }
154    }
155
156    pub fn convert_error<E: Error>(&self, e: E) -> AppError {
157        self.parse_error(e.description())
158    }
159}
160
161/// Virtual directory trait
162///
163/// Local file system or archive file (Zip, Tar ...)
164pub trait DirLike {
165    /// 指定したパスのファイルサイズを取得
166    fn file_size(&mut self, path: &str) -> io::Result<u64>;
167    /// 指定したパスのファイルを開く
168    fn open(&mut self, path: &str) -> io::Result<Box<dyn io::Read>>;
169}
170
171/// DirLike implement for Zip file
172#[cfg(feature = "zip")]
173pub struct ZipDir<R: io::Read + io::Seek>(ZipArchive<R>);
174
175#[cfg(feature = "zip")]
176impl<R: io::Read + io::Seek> DirLike for ZipDir<R> {
177    fn file_size(&mut self, path: &str) -> io::Result<u64> {
178        Ok(self.0.by_name(path)?.size())
179    }
180
181    fn open(&mut self, path: &str) -> io::Result<Box<dyn io::Read>> {
182        let mut file = self.0.by_name(path)?;
183        let mut buf = Vec::with_capacity(file.size() as usize);
184        file.read_to_end(&mut buf)?;
185        Ok(Box::new(Cursor::new(buf)))
186    }
187}
188
189#[cfg(feature = "zip")]
190impl<R: io::Read + io::Seek> ZipDir<R> {
191    /// zipバイナリを読み込んで初期化する
192    /// # Arguments
193    /// * `buf` - std::io::Cursor<&[u8]> や std::fs::File 等
194    pub fn new(buf: R) -> io::Result<ZipDir<R>> {
195        Ok(ZipDir(ZipArchive::new(buf)?))
196    }
197}
198
199/// DirLike implement for Local file system
200impl DirLike for &Path {
201    fn file_size(&mut self, path: &str) -> io::Result<u64> {
202        let buf = self.join(path);
203        Ok(fs::metadata(buf)?.len())
204    }
205
206    fn open(&mut self, path: &str) -> io::Result<Box<dyn io::Read>> {
207        let buf = self.join(path);
208        File::open(buf).map(|f| Box::new(f) as Box<dyn io::Read>)
209    }
210}
211
212#[allow(dead_code)]
213pub mod debug {
214    use std::fs::File;
215    use std::io::Write;
216
217    pub fn dump_string_list(list: &[String], path: &str) {
218        let mut f = File::create(path).unwrap();
219        for s in list {
220            f.write_all(s.as_bytes()).unwrap();
221            f.write_all(b"\n").unwrap();
222        }
223    }
224}