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