1use sim_kernel::{Args, Symbol, Value};
4
5#[derive(Clone, Debug, Eq, Hash, PartialEq)]
10pub enum ArgumentOrigin {
11 KernelPosition(usize),
13 Guest(Symbol),
15}
16
17#[derive(Clone)]
22pub enum ArgumentInput {
23 Positional(Value),
25 Named {
27 name: Symbol,
29 value: Value,
31 },
32 Receiver(Value),
34 Remainder(Value),
36 Unconsumed(Value),
38}
39
40#[derive(Clone)]
42pub struct BoundArgument {
43 input: ArgumentInput,
44 origin: ArgumentOrigin,
45}
46
47impl BoundArgument {
48 pub fn new(input: ArgumentInput, origin: ArgumentOrigin) -> Self {
50 Self { input, origin }
51 }
52
53 pub const fn input(&self) -> &ArgumentInput {
55 &self.input
56 }
57
58 pub const fn origin(&self) -> &ArgumentOrigin {
60 &self.origin
61 }
62}
63
64#[derive(Clone, Default)]
66pub struct CallInput {
67 arguments: Vec<BoundArgument>,
68}
69
70impl CallInput {
71 pub const fn new() -> Self {
73 Self {
74 arguments: Vec::new(),
75 }
76 }
77
78 pub fn push(&mut self, input: ArgumentInput, origin: ArgumentOrigin) {
80 self.arguments.push(BoundArgument::new(input, origin));
81 }
82
83 pub fn with(mut self, input: ArgumentInput, origin: ArgumentOrigin) -> Self {
85 self.push(input, origin);
86 self
87 }
88
89 pub fn arguments(&self) -> &[BoundArgument] {
91 &self.arguments
92 }
93}
94
95impl From<Args> for CallInput {
96 fn from(args: Args) -> Self {
97 Self {
98 arguments: args
99 .into_vec()
100 .into_iter()
101 .enumerate()
102 .map(|(position, value)| {
103 BoundArgument::new(
104 ArgumentInput::Positional(value),
105 ArgumentOrigin::KernelPosition(position),
106 )
107 })
108 .collect(),
109 }
110 }
111}
112
113#[derive(Clone, Default)]
115pub struct BoundCall {
116 arguments: Vec<BoundArgument>,
117}
118
119impl BoundCall {
120 pub fn arguments(&self) -> &[BoundArgument] {
122 &self.arguments
123 }
124
125 pub fn positional(&self) -> impl Iterator<Item = &BoundArgument> {
127 self.select(|input| matches!(input, ArgumentInput::Positional(_)))
128 }
129
130 pub fn named(&self) -> impl Iterator<Item = &BoundArgument> {
132 self.select(|input| matches!(input, ArgumentInput::Named { .. }))
133 }
134
135 pub fn receivers(&self) -> impl Iterator<Item = &BoundArgument> {
137 self.select(|input| matches!(input, ArgumentInput::Receiver(_)))
138 }
139
140 pub fn remainder(&self) -> impl Iterator<Item = &BoundArgument> {
142 self.select(|input| matches!(input, ArgumentInput::Remainder(_)))
143 }
144
145 pub fn unconsumed(&self) -> impl Iterator<Item = &BoundArgument> {
147 self.select(|input| matches!(input, ArgumentInput::Unconsumed(_)))
148 }
149
150 fn select(
151 &self,
152 predicate: impl Fn(&ArgumentInput) -> bool,
153 ) -> impl Iterator<Item = &BoundArgument> {
154 self.arguments
155 .iter()
156 .filter(move |argument| predicate(&argument.input))
157 }
158}
159
160pub fn bind(input: CallInput) -> BoundCall {
162 BoundCall {
163 arguments: input.arguments,
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use sim_kernel::testing::bare_cx;
171
172 fn origin(name: &str) -> ArgumentOrigin {
173 ArgumentOrigin::Guest(Symbol::new(name))
174 }
175
176 #[test]
177 fn duplicate_names_reach_policy_as_distinct_ordered_occurrences() {
178 let cx = bare_cx();
179 let first = cx.factory().symbol(Symbol::new("first")).unwrap();
180 let second = cx.factory().symbol(Symbol::new("second")).unwrap();
181 let name = Symbol::new("option");
182 let input = CallInput::new()
183 .with(
184 ArgumentInput::Named {
185 name: name.clone(),
186 value: first,
187 },
188 origin("call:4"),
189 )
190 .with(
191 ArgumentInput::Named {
192 name,
193 value: second,
194 },
195 origin("call:9"),
196 );
197
198 let bound = bind(input);
199 let origins = bound.named().map(BoundArgument::origin).collect::<Vec<_>>();
200 assert_eq!(origins, vec![&origin("call:4"), &origin("call:9")]);
201 }
202
203 #[test]
204 fn every_input_class_remains_visible_and_stably_ordered() {
205 let cx = bare_cx();
206 let make = || {
207 let value = |name| cx.factory().symbol(Symbol::new(name)).unwrap();
208 let positional = value("positional");
209 let receiver = value("receiver");
210 let remainder = value("remainder");
211 let unconsumed = value("unconsumed");
212 CallInput::from(Args::new(vec![positional]))
213 .with(ArgumentInput::Receiver(receiver), origin("receiver"))
214 .with(ArgumentInput::Remainder(remainder), origin("spread"))
215 .with(ArgumentInput::Unconsumed(unconsumed), origin("unknown"))
216 };
217
218 let project = |bound: BoundCall| {
219 bound
220 .arguments()
221 .iter()
222 .map(|argument| argument.origin().clone())
223 .collect::<Vec<_>>()
224 };
225 assert_eq!(project(bind(make())), project(bind(make())));
226 let bound = bind(make());
227 assert_eq!(bound.positional().count(), 1);
228 assert_eq!(bound.receivers().count(), 1);
229 assert_eq!(bound.remainder().count(), 1);
230 assert_eq!(bound.unconsumed().count(), 1);
231 }
232}