hyeong/util/
util.rs

1use crate::core::code::UnOptCode;
2use crate::core::parse;
3use crate::number::number::Num;
4use crate::util::error::Error;
5use crate::util::io;
6use crate::util::option::HyeongOption;
7use std::io::Write;
8use std::io::{BufRead, BufReader};
9use std::path::PathBuf;
10use std::process::{Command, Stdio};
11use termcolor::StandardStream;
12
13/// PathBuf to String helper function
14///
15/// # Examples
16///
17/// ```
18/// use hyeong::util::util;
19/// use std::path::PathBuf;
20///
21/// assert_eq!("foo/bar.hyeong", util::path_to_string(&PathBuf::from("foo/bar.hyeong")).unwrap());
22/// ```
23pub fn path_to_string(path: &PathBuf) -> Result<String, Error> {
24    path.clone().into_os_string().into_string().map_err(|_| {
25        Error::new(
26            "error on OsString to String conversion",
27            "maybe the path is not correct",
28        )
29    })
30}
31
32/// Read and parse file
33///
34/// # Examples
35///
36/// ```
37/// use termcolor::{StandardStream, ColorChoice};
38/// use hyeong::util::util;
39/// use std::path::PathBuf;
40/// use hyeong::util::option::HyeongOption;
41///
42/// let mut s = StandardStream::stdout(ColorChoice::Auto);
43/// let c = util::parse_file(&mut s, &PathBuf::from("examples/hello_world/hello_world.hyeong"), &HyeongOption::new()).unwrap();
44///
45/// assert_eq!(44, c.len());
46/// ```
47pub fn parse_file(
48    stdout: &mut StandardStream,
49    path: &PathBuf,
50    option: &HyeongOption,
51) -> Result<Vec<UnOptCode>, Error> {
52    let raw_code = io::read_file(path)?;
53    io::print_log(stdout, format!("parsing {}", path_to_string(path)?))?;
54    let un_opt_code = parse::parse(raw_code);
55    if option.verbose {
56        io::print_log(stdout, format!("⮑  total {} commands", un_opt_code.len()))?;
57    }
58    Ok(un_opt_code)
59}
60
61/// change `Num` to unicode char
62///
63/// # Examples
64///
65/// ```
66/// use hyeong::number::number::Num;
67/// use hyeong::util::util;
68///
69/// let a = Num::from_num(55357);
70/// let b = Num::from_num(0xAC00);
71///
72/// assert!(util::num_to_unicode(&a).is_err());
73/// assert!(matches!(util::num_to_unicode(&b), Ok('가')));
74/// ```
75pub fn num_to_unicode(num: &Num) -> Result<char, Error> {
76    let n = num.floor().to_int();
77    std::char::from_u32(n).ok_or(Error::new(
78        "utf-8 encoding error",
79        format!("number {} is not valid unicode", n),
80    ))
81}
82
83/// Execute command and stream stdout to `StandardStream`
84#[cfg(not(tarpaulin_include))]
85pub fn execute_command_stdout(w: &mut StandardStream, command: &str) -> Result<(), Error> {
86    let mut cmd = if cfg!(target_os = "windows") {
87        Command::new("cmd")
88            .arg("/C")
89            .arg(command)
90            .stdout(Stdio::piped())
91            .spawn()?
92    } else {
93        Command::new("bash")
94            .arg("-c")
95            .arg(command)
96            .stdout(Stdio::piped())
97            .spawn()?
98    };
99
100    let stdout = cmd.stdout.as_mut().unwrap();
101    let stdout_reader = BufReader::new(stdout);
102
103    for line in stdout_reader.lines() {
104        write!(w, "{}\n", line?)?;
105    }
106
107    let e = cmd.wait()?;
108
109    if e.success() {
110        Ok(())
111    } else {
112        match e.code() {
113            Some(code) => Err(Error::new(
114                format!("command {} failed with exit code {}", command, code),
115                "",
116            )),
117            None => Err(Error::new(
118                format!("command {} terminated by signal", command),
119                "",
120            )),
121        }
122    }
123}
124
125/// Execute command and stream stdout to `StandardStream`
126#[cfg(not(tarpaulin_include))]
127pub fn execute_command_stderr(w: &mut StandardStream, command: &str) -> Result<(), Error> {
128    let mut cmd = if cfg!(target_os = "windows") {
129        Command::new("cmd")
130            .arg("/C")
131            .arg(command)
132            .stderr(Stdio::piped())
133            .spawn()?
134    } else {
135        Command::new("bash")
136            .arg("-c")
137            .arg(command)
138            .stderr(Stdio::piped())
139            .spawn()?
140    };
141
142    let stdout = cmd.stderr.as_mut().unwrap();
143    let stdout_reader = BufReader::new(stdout);
144
145    for line in stdout_reader.lines() {
146        write!(w, "{}\n", line?)?;
147    }
148
149    let e = cmd.wait()?;
150
151    if e.success() {
152        Ok(())
153    } else {
154        match e.code() {
155            Some(code) => Err(Error::new(
156                format!("command {} failed with exit code {}", command, code),
157                "",
158            )),
159            None => Err(Error::new(
160                format!("command {} terminated by signal", command),
161                "",
162            )),
163        }
164    }
165}