1use std::collections::BTreeMap;
10use std::sync::Arc;
11use std::sync::RwLock;
12use getopts::HasArg;
13use getopts::Occur;
14use getopts::Options;
15use crate::env::*;
16use crate::error::*;
17use crate::interp::*;
18use crate::utils::*;
19use crate::value::*;
20
21fn create_options<F>(value: &Value, err_msg: &str, mut f: F) -> Result<Options>
22 where F: FnMut(String)
23{
24 match value {
25 Value::Ref(object) => {
26 let object_g = rw_lock_read(object)?;
27 match &*object_g {
28 MutObject::Array(opt_values) => {
29 let mut opts = Options::new();
30 for opt_value in opt_values {
31 match opt_value {
32 Value::Ref(opt_object) => {
33 let opt_object_g = rw_lock_read(opt_object)?;
34 match &*opt_object_g {
35 MutObject::Array(opt_arg_values) => {
36 if opt_arg_values.len() < 3 || opt_arg_values.len() > 6 {
37 return Err(Error::Interp(String::from("invalid number of elements for option")))
38 }
39 let short_name = match opt_arg_values.get(0) {
40 Some(opt_arg_value) => {
41 let mut s = String::new();
42 match format!("{}", opt_arg_value).chars().next() {
43 Some(c) => s.push(c),
44 None => (),
45 }
46 s
47 },
48 None => return Err(Error::Interp(String::from("no short name for option"))),
49 };
50 let long_name = match opt_arg_values.get(1) {
51 Some(opt_arg_value) => format!("{}", opt_arg_value),
52 None => return Err(Error::Interp(String::from("no long name for option"))),
53 };
54 let desc = match opt_arg_values.get(2) {
55 Some(opt_arg_value) => format!("{}", opt_arg_value),
56 None => return Err(Error::Interp(String::from("no description for option"))),
57 };
58 let hint = match opt_arg_values.get(3) {
59 Some(opt_arg_value) => format!("{}", opt_arg_value),
60 None => String::new(),
61 };
62 let hasarg = match opt_arg_values.get(4) {
63 Some(opt_arg_value) => {
64 let s = format!("{}", opt_arg_value);
65 if s == String::from("yes") {
66 HasArg::Yes
67 } else if s == String::from("no") {
68 HasArg::No
69 } else if s == String::from("maybe") {
70 HasArg::Maybe
71 } else {
72 return Err(Error::Interp(String::from("invalid has argument for option")));
73 }
74 },
75 None => {
76 if !hint.is_empty() {
77 HasArg::Yes
78 } else {
79 HasArg::No
80 }
81 },
82 };
83 let occur = match opt_arg_values.get(5) {
84 Some(opt_arg_value) => {
85 let s = format!("{}", opt_arg_value);
86 if s == String::from("req") {
87 Occur::Req
88 } else if s == String::from("optional") {
89 Occur::Optional
90 } else if s == String::from("multi") {
91 Occur::Multi
92 } else {
93 return Err(Error::Interp(String::from("invalid has argument for option")));
94 }
95 },
96 None => Occur::Optional,
97 };
98 let name = if !long_name.is_empty() {
99 long_name.clone()
100 } else {
101 short_name.clone()
102 };
103 if name.is_empty() {
104 return Err(Error::Interp(String::from("no name for option")))
105 }
106 f(name);
107 opts.opt(short_name.as_str(), long_name.as_str(), desc.as_str(), hint.as_str(), hasarg, occur);
108 },
109 _ => return Err(Error::Interp(String::from("invalid option"))),
110 }
111 },
112 _ => return Err(Error::Interp(String::from("invalid option"))),
113 }
114 }
115 Ok(opts)
116 },
117 _ => Err(Error::Interp(String::from(err_msg))),
118 }
119 },
120 _ => Err(Error::Interp(String::from(err_msg))),
121 }
122}
123
124pub fn create_args(value: &Value, err_msg: &str) -> Result<Vec<String>>
125{
126 match value {
127 Value::Ref(object) => {
128 let object_g = rw_lock_read(object)?;
129 match &*object_g {
130 MutObject::Array(arg_values) => Ok(arg_values.iter().map(|v| format!("{}", v)).collect()),
131 _ => Err(Error::Interp(String::from(err_msg))),
132 }
133 },
134 _ => Err(Error::Interp(String::from(err_msg))),
135 }
136}
137
138pub fn getopts(_interp: &mut Interp, env: &mut Env, arg_values: &[Value]) -> Result<Value>
140{
141 if arg_values.len() < 1 || arg_values.len() > 2 {
142 return Err(Error::Interp(String::from("invalid number of arguments")));
143 }
144 let mut names: Vec<String> = Vec::new();
145 let (opts, args) = match (arg_values.get(0), arg_values.get(1)) {
146 (Some(opt_value), None) => {
147 let tmp_opts = create_options(&opt_value, "unsupported types for getopts", |s| names.push(s))?;
148 let shared_env_g = rw_lock_read(env.shared_env())?;
149 (tmp_opts, shared_env_g.args().to_vec())
150 },
151 (Some(opt_value), Some(arg_value)) => {
152 let tmp_opts = create_options(&opt_value, "unsupported types for getopts", |s| names.push(s))?;
153 let tmp_args = create_args(&arg_value, "unsupported types for getopts")?;
154 (tmp_opts, tmp_args)
155 },
156 (_, _) => return Err(Error::Interp(String::from("no argument"))),
157 };
158 let matches = match opts.parse(args.as_slice()) {
159 Ok(tmp_matches) => tmp_matches,
160 Err(err) => return Ok(Value::Object(Arc::new(Object::Error(String::from("getopts"), format!("{}", err))))),
161 };
162 let mut opt_fields: BTreeMap<String, Value> = BTreeMap::new();
163 for name in &names {
164 let ident = name.replace("-", "_");
165 if matches.opt_present(name.as_str()) {
166 opt_fields.insert(ident, Value::Ref(Arc::new(RwLock::new(MutObject::Array(matches.opt_strs(name.as_str()).iter().map(|s| Value::Object(Arc::new(Object::String(s.clone())))).collect())))));
167 } else {
168 opt_fields.insert(ident, Value::None);
169 }
170 }
171 let mut fields: BTreeMap<String, Value> = BTreeMap::new();
172 fields.insert(String::from("opts"), Value::Ref(Arc::new(RwLock::new(MutObject::Struct(opt_fields)))));
173 fields.insert(String::from("free"), Value::Ref(Arc::new(RwLock::new(MutObject::Array(matches.free.iter().map(|s| Value::Object(Arc::new(Object::String(s.clone())))).collect())))));
174 Ok(Value::Ref(Arc::new(RwLock::new(MutObject::Struct(fields)))))
175}
176
177pub fn getoptsusage(_interp: &mut Interp, _env: &mut Env, arg_values: &[Value]) -> Result<Value>
179{
180 if arg_values.len() != 2 {
181 return Err(Error::Interp(String::from("invalid number of arguments")));
182 }
183 let (opts, brief) = match (arg_values.get(0), arg_values.get(1)) {
184 (Some(opt_value), Some(brief_value)) => {
185 let tmp_opts = create_options(&opt_value, "unsupported type for getoptsusage", |_| ())?;
186 (tmp_opts, format!("{}", brief_value))
187 },
188 (_, _) => return Err(Error::Interp(String::from("no argument"))),
189 };
190 Ok(Value::Object(Arc::new(Object::String(opts.usage(brief.as_str())))))
191}
192
193#[cfg(test)]
194mod tests;