1use std::collections::HashMap;
18
19use crate::value::{
20 RuntimeError, Value, list_from_vec, list_head, list_len, list_slice, list_tail_view,
21 list_to_vec,
22};
23
24pub fn register(global: &mut HashMap<String, Value>) {
25 let mut members = HashMap::new();
26 for method in &[
27 "len", "map", "filter", "fold", "get", "push", "head", "tail", "find", "any", "zip",
28 "flatMap", "contains",
29 ] {
30 members.insert(
31 method.to_string(),
32 Value::Builtin(format!("List.{}", method)),
33 );
34 }
35 global.insert(
36 "List".to_string(),
37 Value::Namespace {
38 name: "List".to_string(),
39 members,
40 },
41 );
42}
43
44pub fn effects(_name: &str) -> &'static [&'static str] {
45 &[]
46}
47
48pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
51 match name {
52 "List.len" => Some(len(&args)),
53 "List.get" => Some(get(&args)),
54 "List.push" => Some(push(&args)),
55 "List.head" => Some(head(&args)),
56 "List.tail" => Some(tail(&args)),
57 "List.contains" => Some(contains(&args)),
58 "List.zip" => Some(zip(&args)),
59 _ => None,
60 }
61}
62
63fn len(args: &[Value]) -> Result<Value, RuntimeError> {
66 if args.len() != 1 {
67 return Err(RuntimeError::Error(format!(
68 "List.len() takes 1 argument, got {}",
69 args.len()
70 )));
71 }
72 list_len(&args[0])
73 .map(|n| Value::Int(n as i64))
74 .ok_or_else(|| RuntimeError::Error("List.len() argument must be a List".to_string()))
75}
76
77fn get(args: &[Value]) -> Result<Value, RuntimeError> {
78 if args.len() != 2 {
79 return Err(RuntimeError::Error(format!(
80 "List.get() takes 2 arguments (list, index), got {}",
81 args.len()
82 )));
83 }
84 let list = list_slice(&args[0]).ok_or_else(|| {
85 RuntimeError::Error("List.get() first argument must be a List".to_string())
86 })?;
87 let index = match &args[1] {
88 Value::Int(i) => *i,
89 _ => {
90 return Err(RuntimeError::Error(
91 "List.get() index must be an Int".to_string(),
92 ));
93 }
94 };
95 if index < 0 || index as usize >= list.len() {
96 Ok(Value::None)
97 } else {
98 Ok(Value::Some(Box::new(list[index as usize].clone())))
99 }
100}
101
102fn push(args: &[Value]) -> Result<Value, RuntimeError> {
103 if args.len() != 2 {
104 return Err(RuntimeError::Error(format!(
105 "List.push() takes 2 arguments (list, val), got {}",
106 args.len()
107 )));
108 }
109 let mut list = list_to_vec(&args[0]).ok_or_else(|| {
110 RuntimeError::Error("List.push() first argument must be a List".to_string())
111 })?;
112 list.push(args[1].clone());
113 Ok(list_from_vec(list))
114}
115
116fn head(args: &[Value]) -> Result<Value, RuntimeError> {
117 if args.len() != 1 {
118 return Err(RuntimeError::Error(format!(
119 "List.head() takes 1 argument, got {}",
120 args.len()
121 )));
122 }
123 match list_head(&args[0]) {
124 Some(v) => Ok(Value::Some(Box::new(v))),
125 None if list_len(&args[0]).is_some() => Ok(Value::None),
126 None => Err(RuntimeError::Error(
127 "List.head() argument must be a List".to_string(),
128 )),
129 }
130}
131
132fn tail(args: &[Value]) -> Result<Value, RuntimeError> {
133 if args.len() != 1 {
134 return Err(RuntimeError::Error(format!(
135 "List.tail() takes 1 argument, got {}",
136 args.len()
137 )));
138 }
139 match list_tail_view(&args[0]) {
140 Some(v) => Ok(Value::Some(Box::new(v))),
141 None if list_len(&args[0]).is_some() => Ok(Value::None),
142 None => Err(RuntimeError::Error(
143 "List.tail() argument must be a List".to_string(),
144 )),
145 }
146}
147
148fn contains(args: &[Value]) -> Result<Value, RuntimeError> {
149 if args.len() != 2 {
150 return Err(RuntimeError::Error(format!(
151 "List.contains() takes 2 arguments (list, value), got {}",
152 args.len()
153 )));
154 }
155 let list = list_slice(&args[0]).ok_or_else(|| {
156 RuntimeError::Error("List.contains() first argument must be a List".to_string())
157 })?;
158 let target = &args[1];
159 Ok(Value::Bool(list.iter().any(|item| item == target)))
160}
161
162fn zip(args: &[Value]) -> Result<Value, RuntimeError> {
163 if args.len() != 2 {
164 return Err(RuntimeError::Error(format!(
165 "List.zip() takes 2 arguments (list, list), got {}",
166 args.len()
167 )));
168 }
169 let a = list_slice(&args[0]).ok_or_else(|| {
170 RuntimeError::Error("List.zip() first argument must be a List".to_string())
171 })?;
172 let b = list_slice(&args[1]).ok_or_else(|| {
173 RuntimeError::Error("List.zip() second argument must be a List".to_string())
174 })?;
175 let pairs: Vec<Value> = a
176 .iter()
177 .zip(b.iter())
178 .map(|(x, y)| Value::Tuple(vec![x.clone(), y.clone()]))
179 .collect();
180 Ok(list_from_vec(pairs))
181}