xref: /DragonReach/src/parse/parse_util/mod.rs (revision e945c217b313a00a228c454741360ee9a74397d2)
1 use std::{fs, io::BufRead, os::unix::fs::PermissionsExt, path::Path};
2 
3 use crate::{
4     contants::{AF_INET, AF_INET6, IPV4_MIN_MTU, IPV6_MIN_MTU, PRIO_MAX, PRIO_MIN},
5     error::parse_error::{ParseError, ParseErrorType},
6     task::cmdtask::CmdTask,
7     unit::{service::ServiceUnit, target::TargetUnit, timer::TimerUnit, Unit, UnitType, Url},
8     FileDescriptor,
9 };
10 
11 use super::{UnitParser, BASE_IEC, BASE_SI, SEC_UNIT_TABLE};
12 
13 #[allow(dead_code)]
14 #[derive(PartialEq)]
15 pub enum SizeBase {
16     IEC,
17     Si,
18 }
19 
20 pub struct UnitParseUtil;
21 
22 #[allow(dead_code)]
23 impl UnitParseUtil {
24     /// @brief 解析布尔值
25     ///
26     /// 将传入的字符串解析为布尔值
27     /// "yes","y","1","true","t","on"均可表示true
28     /// "no","n","0","false","f","off"均可表示false
29     ///
30     /// @param s 需解析的字符串
31     ///
32     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
33     pub fn parse_boolean(s: &str) -> Result<bool, ParseError> {
34         let t_table: Vec<&str> = vec!["yes", "y", "1", "true", "t", "on"];
35         let f_table: Vec<&str> = vec!["no", "n", "0", "false", "f", "off"];
36 
37         if t_table.contains(&s) {
38             return Ok(true);
39         } else if f_table.contains(&s) {
40             return Ok(false);
41         }
42 
43         return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
44     }
45 
46     /// @brief 解析pid
47     ///
48     /// 将传入的字符串解析为pid
49     ///
50     /// @param s 需解析的字符串
51     ///
52     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
53     pub fn parse_pid(s: &str) -> Result<i32, ParseError> {
54         let s = s.trim();
55         //先使用u64变换
56         let pid_ul = match s.parse::<u64>() {
57             Ok(val) => val,
58             Err(_) => {
59                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
60             }
61         };
62         let pid: i32 = pid_ul as i32;
63 
64         if (pid as u64) != pid_ul {
65             //如果在从pid_t转换为u64之后与之前不等,则说明发生了截断,返回错误
66             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
67         }
68 
69         if pid < 0 {
70             //pid小于0不合法
71             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
72         }
73 
74         return Ok(pid);
75     }
76 
77     /// @brief 解析pid
78     ///
79     /// 将传入的字符串解析为mode_t
80     ///
81     /// @param s 需解析的字符串
82     ///
83     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
84     pub fn parse_mode(s: &str) -> Result<u32, ParseError> {
85         let s = s.trim();
86         let m = match u32::from_str_radix(s, 8) {
87             Ok(val) => val,
88             Err(_) => {
89                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
90             }
91         };
92 
93         //如果模式大于权限的最大值则为非法权限,返回错误
94         if m > 0o7777 {
95             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
96         }
97 
98         return Ok(m);
99     }
100 
101     /// @brief 解析网络接口索引
102     ///
103     /// 将传入的字符串解析为网络接口索引具体值
104     ///
105     /// @param s 需解析的字符串
106     ///
107     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
108     pub fn parse_ifindex(s: &str) -> Result<i32, ParseError> {
109         let s = s.trim();
110         let ret: i32 = match s.parse::<i32>() {
111             Ok(val) => val,
112             Err(_) => {
113                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
114             }
115         };
116 
117         if ret <= 0 {
118             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
119         }
120 
121         return Ok(ret);
122     }
123 
124     /// @brief 解析最大传输单元(MTU)
125     ///
126     /// 将传入的字符串解析为具体值
127     ///
128     /// @param s 需解析的字符串
129     ///
130     /// @param family 网络地址族
131     ///
132     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
133     pub fn parse_mtu(s: &str, family: i32) -> Result<u32, ParseError> {
134         let s = s.trim();
135         let mtu = match s.parse::<u64>() {
136             Ok(val) => val,
137             Err(_) => {
138                 //针对非法字符出错时
139                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
140             }
141         };
142 
143         //针对数据溢出时的报错
144         if mtu > u32::MAX as u64 {
145             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
146         }
147 
148         let mtu: u32 = mtu as u32;
149         //判断mtu是否合法
150         if family == AF_INET6 && mtu < IPV6_MIN_MTU {
151             return Err(ParseError::new(ParseErrorType::ERANGE, String::new(), 0));
152         } else if family == AF_INET && mtu < IPV4_MIN_MTU {
153             return Err(ParseError::new(ParseErrorType::ERANGE, String::new(), 0));
154         } else if family != AF_INET6 || family != AF_INET {
155             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
156         }
157 
158         return Ok(mtu);
159     }
160 
161     /// @brief 解析Size
162     ///
163     /// 将传入的字符串解析为具体的字节数
164     /// 可支持IEC二进制后缀,也可支持SI十进制后缀
165     ///
166     /// @param s 需解析的字符串
167     ///
168     /// @param base 设置为IEC二进制后缀或者SI十进制后缀
169     ///
170     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
171     pub fn parse_size(s: &str, base: SizeBase) -> Result<u64, ParseError> {
172         let s = s.trim();
173         //将s分解为数字和后缀部分
174         let (number_str, suffix) = match s.find(|c: char| !c.is_digit(10) && c != '.') {
175             Some(mid) => s.split_at(mid),
176             None => (s, ""),
177         };
178 
179         //获得数字部分的整数和小数部分
180         let (integer, fraction) = match number_str.find(".") {
181             Some(mid) => {
182                 let (integer, fraction) = number_str.split_at(mid);
183                 let integer = integer.parse::<u64>().unwrap();
184                 let fraction = match fraction[1..].parse::<u64>() {
185                     Ok(val) => val,
186                     Err(_) => {
187                         return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
188                     }
189                 };
190                 (integer, fraction)
191             }
192             None => (number_str.parse::<u64>().unwrap(), 0),
193         };
194 
195         //从表中查找到后缀所对应的字节倍数
196         let mut factor: u64 = 0;
197         if base == SizeBase::IEC {
198             factor = match BASE_IEC.get(suffix) {
199                 Some(val) => *val,
200                 None => {
201                     return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
202                 }
203             }
204         } else if base == SizeBase::Si {
205             factor = match BASE_SI.get(suffix) {
206                 Some(val) => *val,
207                 None => {
208                     return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
209                 }
210             }
211         }
212 
213         Ok(integer * factor + (fraction * factor) / (10u64.pow(fraction.to_string().len() as u32)))
214     }
215 
216     /// @brief 解析扇区大小
217     ///
218     /// 将传入的字符串解析为具体的扇区大小
219     /// 若扇区大小小于512或者大于4096,将会返回错误,若扇区大小不为2的幂,返回错误。
220     ///
221     /// @param s 需解析的字符串
222     ///
223     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
224     pub fn parse_sector_size(s: &str) -> Result<u64, ParseError> {
225         let s = s.trim();
226         let size: u64 = match s.parse::<u64>() {
227             Ok(val) => val,
228             Err(_) => {
229                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
230             }
231         };
232 
233         if size < 512 || size > 4096 {
234             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
235         }
236 
237         //判断是否为2的幂,如果不是则报错
238         if (size & (size - 1)) != 0 {
239             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
240         }
241 
242         return Ok(size);
243     }
244 
245     /// @brief 解析范围
246     ///
247     /// 将传入的字符串解析为具体的范围
248     ///
249     /// @param s 需解析的字符串
250     ///
251     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
252     pub fn parse_range(s: &str) -> Result<(u32, u32), ParseError> {
253         let mid = match s.find('-') {
254             Some(val) => val,
255             None => {
256                 //如果字符串中没有'-'符号,则表示一个值,所以范围两端都为该值
257                 let s = s.trim();
258                 let ret = match s.parse::<u32>() {
259                     Ok(val) => val,
260                     Err(_) => {
261                         return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
262                     }
263                 };
264                 return Ok((ret, ret));
265             }
266         };
267 
268         //若字符串中存在'-',则分别解析为u32,解析失败则报错
269         let (l, r) = s.split_at(mid);
270 
271         let l = l.trim();
272         let l = match l.parse::<u32>() {
273             Ok(val) => val,
274             Err(_) => {
275                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
276             }
277         };
278         let r = r.trim();
279         let r = match r.parse::<u32>() {
280             Ok(val) => val,
281             Err(_) => {
282                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
283             }
284         };
285 
286         return Ok((l, r));
287     }
288 
289     /// @brief 解析文件描述符
290     ///
291     /// 将传入的字符串解析为文件描述符fd
292     ///
293     /// @param s 需解析的字符串
294     ///
295     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
296     pub fn parse_fd(s: &str) -> Result<FileDescriptor, ParseError> {
297         let s = s.trim();
298         let fd = match s.parse::<i32>() {
299             Ok(val) => val,
300             Err(_) => {
301                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
302             }
303         };
304 
305         if fd < 0 {
306             return Err(ParseError::new(ParseErrorType::EBADF, String::new(), 0));
307         }
308 
309         return Ok(FileDescriptor(fd as usize));
310     }
311 
312     /// @brief 解析nice
313     ///
314     /// 将传入的字符串解析为nice
315     ///
316     /// @param s 需解析的字符串
317     ///
318     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
319     pub fn parse_nice(s: &str) -> Result<i8, ParseError> {
320         let s = s.trim();
321         let nice = match s.parse::<i8>() {
322             Ok(val) => val,
323             Err(_) => {
324                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
325             }
326         };
327 
328         if nice > PRIO_MAX || nice < PRIO_MIN {
329             return Err(ParseError::new(ParseErrorType::ERANGE, String::new(), 0));
330         }
331 
332         return Ok(nice);
333     }
334 
335     /// @brief 解析端口号
336     ///
337     /// 将传入的字符串解析为端口号
338     ///
339     /// @param s 需解析的字符串
340     ///
341     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
342     pub fn parse_ip_port(s: &str) -> Result<u16, ParseError> {
343         let s = s.trim();
344         let port = match s.parse::<u16>() {
345             Ok(val) => val,
346             Err(_) => {
347                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
348             }
349         };
350 
351         if port == 0 {
352             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
353         }
354 
355         return Ok(port);
356     }
357 
358     /// @brief 解析端口范围
359     ///
360     /// 将传入的字符串解析为端口范围
361     ///
362     /// @param s 需解析的字符串
363     ///
364     /// @return 解析成功则返回Ok((u16,u16)),否则返回Err
365     pub fn parse_ip_port_range(s: &str) -> Result<(u16, u16), ParseError> {
366         let (l, h) = Self::parse_range(s)?;
367 
368         let l = l as u16;
369         let h = h as u16;
370         if l <= 0 || l >= 65535 || h <= 0 || h >= 65535 {
371             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
372         }
373 
374         return Ok((l, h));
375     }
376 
377     /// @brief 解析OOM(Out-of-Memory)分数调整值
378     ///
379     /// 将传入的字符串解析为OOM(Out-of-Memory)分数调整值
380     ///
381     /// @param s 需解析的字符串
382     ///
383     /// @return 解析成功则返回Ok(u32),否则返回Err
384     pub fn parse_ip_prefix_length(s: &str) -> Result<u32, ParseError> {
385         let len = match s.parse::<u32>() {
386             Ok(val) => val,
387             Err(_) => {
388                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
389             }
390         };
391 
392         if len > 128 {
393             return Err(ParseError::new(ParseErrorType::ERANGE, String::new(), 0));
394         }
395 
396         return Ok(len);
397     }
398 
399     /// @brief 目前为简单的分割字符串,并未提供严谨的Url解析
400     ///
401     /// 将传入的字符串解析为Url结构体的Vec,若Url非法则返回错误
402     ///
403     /// @param s 需解析的字符串
404     ///
405     /// @return 解析成功则返回Ok(Url),否则返回Err
406     pub fn parse_url(s: &str) -> Result<Vec<Url>, ParseError> {
407         let _url = Url::default();
408         let url_strs = s.split_whitespace().collect::<Vec<&str>>();
409         let mut urls = Vec::new();
410         for s in url_strs {
411             urls.push(Url {
412                 url_string: String::from(s),
413             })
414         }
415         return Ok(urls);
416     }
417 
418     /// @brief 将对应的str解析为对应Unit
419     ///
420     /// 将传入的字符串解析为Unit,解析失败返回错误
421     ///
422     /// @param path 需解析的文件
423     ///
424     /// @return 解析成功则返回Ok(Arc<dyn Unit>),否则返回Err
425     pub fn parse_unit<T: Unit>(path: &str) -> Result<usize, ParseError> {
426         return T::from_path(path);
427     }
428 
429     /// @brief 将对应的str解析为Arc<dyn Unit>
430     ///
431     /// 将传入的字符串解析为Arc<dyn Unit>,解析失败返回错误
432     ///
433     /// @param path 需解析的文件
434     ///
435     /// @return 解析成功则返回Ok(Arc<dyn Unit>),否则返回Err
436     pub fn parse_unit_no_type(path: &str) -> Result<usize, ParseError> {
437         let idx = match path.rfind('.') {
438             Some(val) => val,
439             None => {
440                 return Err(ParseError::new(ParseErrorType::EFILE, path.to_string(), 0));
441             }
442         };
443 
444         if idx == path.len() - 1 {
445             //处理非法文件xxxx. 类型
446             return Err(ParseError::new(ParseErrorType::EFILE, path.to_string(), 0));
447         }
448 
449         let suffix = &path[idx + 1..];
450 
451         //通过文件后缀分发给不同类型的Unit解析器解析
452         let unit = match suffix {
453             //TODO: 目前为递归,后续应考虑从DragonReach管理的Unit表中寻找是否有该Unit,并且通过记录消除递归
454             "service" => UnitParser::parse::<ServiceUnit>(path, UnitType::Service)?,
455             "target" => UnitParser::parse::<TargetUnit>(path, UnitType::Target)?,
456             "timer" => UnitParser::parse::<TimerUnit>(path, UnitType::Timer)?,
457             _ => {
458                 return Err(ParseError::new(ParseErrorType::EFILE, path.to_string(), 0));
459             }
460         };
461 
462         return Ok(unit);
463     }
464 
465     pub fn parse_env(s: &str) -> Result<(String, String), ParseError> {
466         let s = s.trim().split('=').collect::<Vec<&str>>();
467         if s.len() != 2 {
468             return Err(ParseError::new(ParseErrorType::EINVAL, "".to_string(), 0));
469         }
470 
471         return Ok((s[0].to_string(), s[1].to_string()));
472     }
473 
474     /// @brief 将对应的str解析为对应CmdTask
475     ///
476     /// 将传入的字符串解析为CmdTask组,解析失败返回错误
477     ///
478     /// @param path 需解析的文件
479     ///
480     /// @return 解析成功则返回Ok(Vec<CmdTask>>),否则返回Err
481     pub fn parse_cmd_task(s: &str) -> Result<Vec<CmdTask>, ParseError> {
482         //分拆成单词Vec
483         let cmds = s.split_whitespace().collect::<Vec<&str>>();
484         let mut tasks = Vec::new();
485         let mut i = 0;
486         while i < cmds.len() {
487             let mut cmd_task = CmdTask::default();
488             //匹配到这里时,这个单词肯定是路径,若路径以-开头则设置ignore
489             cmd_task.ignore = cmds[i].starts_with('-');
490 
491             //获取到一个CmdTask的路径部分
492             let path: String;
493             if cmd_task.ignore {
494                 path = String::from(&cmds[i][1..]);
495             } else {
496                 path = String::from(cmds[i]);
497             }
498 
499             //得到的非绝对路径则不符合语法要求,报错
500             if !UnitParseUtil::is_valid_exec_path(path.as_str()) {
501                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
502             }
503 
504             cmd_task.path = path;
505 
506             //i += 1,继续匹配下一个单词
507             i += 1;
508             let mut cmd_vec = Vec::new();
509             while i < cmds.len() && !UnitParseUtil::is_valid_exec_path(cmds[i]) {
510                 //命令可能会有多个单词,将多个命令整理成一个
511                 let cmd = cmds[i];
512                 cmd_vec.push(String::from(cmd));
513                 i += 1;
514             }
515             cmd_task.cmd = cmd_vec;
516             tasks.push(cmd_task);
517             //经过while到这里之后,cmds[i]对应的单词一点是路径,i不需要加一
518         }
519         return Ok(tasks);
520     }
521 
522     /// @brief 判断是否为绝对路径,以及指向是否为可执行文件或者sh脚本
523     ///
524     /// 目前该方法仅判断是否为绝对路径
525     ///
526     /// @param path 路径
527     ///
528     /// @return 解析成功则返回true,否则返回false
529     pub fn is_valid_exec_path(path: &str) -> bool {
530         let path = Path::new(path);
531         return path.is_absolute();
532 
533         //TODO: 后续应判断该文件是否为合法文件
534         //let path = Path::new(path);
535         //return Self::is_executable_file(path) || Self::is_shell_script(path);
536     }
537 
538     pub fn is_valid_file(path: &str) -> bool {
539         if !path.starts_with("/") {
540             return false;
541         }
542 
543         let path = Path::new(path);
544         if let Ok(matadata) = fs::metadata(path) {
545             return matadata.is_file();
546         }
547 
548         return false;
549     }
550 
551     fn is_executable_file(path: &Path) -> bool {
552         if let Ok(metadata) = fs::metadata(path) {
553             // 检查文件类型是否是普通文件并且具有可执行权限
554             if metadata.is_file() {
555                 let permissions = metadata.permissions().mode();
556                 return permissions & 0o111 != 0;
557             }
558         }
559         false
560     }
561 
562     fn is_shell_script(path: &Path) -> bool {
563         if let Some(extension) = path.extension() {
564             if extension == "sh" {
565                 return true;
566             }
567         }
568         false
569     }
570 
571     /// @brief 将对应的str解析为us(微秒)
572     ///
573     /// 将传入的字符串解析为秒数,解析失败返回错误
574     ///
575     /// @param path 需解析的文件
576     ///
577     /// @return 解析成功则返回Ok(u64),否则返回Err
578     pub fn parse_sec(s: &str) -> Result<u64, ParseError> {
579         let ss = s.split_whitespace().collect::<Vec<&str>>();
580         let mut ret = 0;
581         for s in ss {
582             //下列参数分别记录整数部分,小数部分以及单位
583             let integer: u64;
584             let mut frac: u64 = 0;
585             let unit: &str;
586 
587             match s.find('.') {
588                 Some(idx) => {
589                     //解析整数部分
590                     integer = match s[..idx].parse::<u64>() {
591                         Ok(val) => val,
592                         Err(_) => {
593                             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0))
594                         }
595                     };
596                     //获得小数+单位的字符串
597                     let frac_and_unit = &s[(idx + 1)..];
598                     match frac_and_unit.find(|c: char| !c.is_digit(10)) {
599                         Some(val) => {
600                             //匹配小数部分
601                             frac = match frac_and_unit[..val].parse::<u64>() {
602                                 Ok(val) => val,
603                                 Err(_) => {
604                                     return Err(ParseError::new(
605                                         ParseErrorType::EINVAL,
606                                         String::new(),
607                                         0,
608                                     ))
609                                 }
610                             };
611                             //单位部分
612                             unit = &frac_and_unit[val..];
613                         }
614                         None => {
615                             //没有单位的情况,直接匹配小数
616                             frac = match frac_and_unit.parse::<u64>() {
617                                 Ok(val) => val,
618                                 Err(_) => {
619                                     return Err(ParseError::new(
620                                         ParseErrorType::EINVAL,
621                                         String::new(),
622                                         0,
623                                     ))
624                                 }
625                             };
626                             unit = "";
627                         }
628                     };
629                 }
630                 None => {
631                     //没有小数点则直接匹配整数部分和单位部分
632                     match s.find(|c: char| !c.is_digit(10)) {
633                         Some(idx) => {
634                             integer = match s[..idx].parse::<u64>() {
635                                 Ok(val) => val,
636                                 Err(_) => {
637                                     return Err(ParseError::new(
638                                         ParseErrorType::EINVAL,
639                                         String::new(),
640                                         0,
641                                     ))
642                                 }
643                             };
644                             unit = &s[idx..];
645                         }
646                         None => {
647                             integer = match s.parse::<u64>() {
648                                 Ok(val) => val,
649                                 Err(_) => {
650                                     return Err(ParseError::new(
651                                         ParseErrorType::EINVAL,
652                                         String::new(),
653                                         0,
654                                     ))
655                                 }
656                             };
657                             unit = "";
658                         }
659                     };
660                 }
661             };
662 
663             //从时间单位转换表中获取到单位转换为ns的倍数
664             let factor = match SEC_UNIT_TABLE.get(unit) {
665                 Some(val) => val,
666                 None => {
667                     return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
668                 }
669             };
670 
671             ret += integer * factor + (frac * factor) / (10u64.pow(frac.to_string().len() as u32));
672         }
673 
674         //计算ns
675         return Ok(ret);
676     }
677     /// @brief 判断对应路径是否为目录
678     ///
679     /// @param path 路径
680     ///
681     /// @return true/false
682     pub fn is_dir(path: &str) -> bool {
683         if let Ok(metadata) = fs::metadata(path) {
684             if metadata.is_dir() {
685                 return true;
686             }
687             return false;
688         }
689         return false;
690     }
691 
692     /// ## 通过文件名解析该Unit的类型
693     pub fn parse_type(path: &str) -> UnitType {
694         let ret: &str;
695         if let Some(index) = path.rfind('.') {
696             ret = &path[index + 1..];
697         } else {
698             ret = path;
699         }
700         match ret {
701             "service" => return UnitType::Service,
702             "target" => return UnitType::Target,
703             "timer" => return UnitType::Timer,
704             //TODO: 添加文件类型
705             _ => return UnitType::Unknown,
706         }
707     }
708 
709     /// ## 将读取环境变量文件解析为环境变量集合
710     pub fn parse_environment_file(env_file: &str) -> Result<Vec<(String, String)>, ParseError> {
711         let mut envs = Vec::new();
712         if env_file.len() > 0 {
713             let env_reader = UnitParser::get_reader(env_file, UnitType::Unknown)?;
714             for line in env_reader.lines() {
715                 if let Ok(line) = line {
716                     let x = UnitParseUtil::parse_env(line.as_str())?;
717                     envs.push(x);
718                 }
719             }
720         }
721         return Ok(envs);
722     }
723 }
724