1use capitalize::Capitalize;
2use std::{collections::BTreeMap, iter};
3
4use crate::{
5 tree::{self, Node, Tree},
6 vessel::{Article, Vessel, vessel_text},
7};
8
9use super::parse::{Action, Verb, VesselRequest};
10
11pub fn list_children(children: BTreeMap<String, Vessel>) -> String {
13 children
14 .iter()
15 .fold("You see:".to_string(), |acc, (key, vessel)| {
16 format!("{acc}\n\t> {}", vessel_text(key, &vessel.article))
17 })
18}
19pub fn note_text(vessel: &Vessel) -> String {
22 if let Some(note) = &vessel.note {
23 format!("{note}\n")
24 } else {
25 String::new()
26 }
27}
28pub fn status_info(
34 tree: &Tree<String, Vessel>,
35 user_key: &String,
36) -> Result<String, tree::Error<String>> {
37 let user = tree.get(user_key)?;
38 let parent = tree.get(&user.parent)?;
39 let mut out = String::new();
40
41 out += &format!(
42 "You are {} in {}.\n",
43 vessel_text(user_key, &user.value.article),
44 vessel_text(&user.parent, &parent.value.article)
45 );
46 out += ¬e_text(&parent.value);
47
48 for sibling in tree
49 .get_all(tree.siblings(user_key, false)?)?
50 .iter()
51 .map(|(key, node)| vessel_text(key, &node.value.article))
52 {
53 out += &format!("You see {}.\n", sibling);
54 }
55 for child in tree
56 .get_all(tree.children(user_key))?
57 .iter()
58 .map(|(key, node)| vessel_text(key, &node.value.article))
59 {
60 out += &format!("You are carrying {}.\n", child);
61 }
62
63 Ok(out)
64}
65
66pub enum ActionResult {
69 ShowStatus,
71 Programming(String, Option<Article>),
73 StillProgramming,
75 StillProgrammingButExit,
77 DoneProgramming,
79 Exit,
81 UserError(String),
84 DebugText(String),
87 ResponseText(String),
90 HelpText(String),
93 Saved,
95 Loaded,
97 SaveError(std::io::Error),
100 LoadError(std::io::Error),
103}
104
105pub fn run_program(
116 user_key: &mut String,
117 world: &mut Tree<String, Vessel>,
118 program: Vec<Action>,
119) -> Result<ActionResult, Box<dyn std::error::Error>> {
120 let mut local_editor_stack: Vec<String> = Vec::new();
121 for action in program {
122 if matches!(action, Action::Save { filename: _ })
123 || matches!(action, Action::Load { filename: _ })
124 {
125 return Ok(ActionResult::UserError(
126 "Programs are not allowed to use the debug, save, or load actions.".to_string(),
127 ));
128 }
129 match run_action(
130 user_key,
131 world,
132 Some(action.clone()),
133 &mut local_editor_stack,
134 ) {
135 Ok(ActionResult::StillProgrammingButExit)
136 | Ok(ActionResult::DoneProgramming)
137 | Ok(ActionResult::ResponseText(_))
138 | Ok(ActionResult::HelpText(_))
139 | Ok(ActionResult::StillProgramming)
140 | Ok(ActionResult::ShowStatus)
141 | Ok(ActionResult::Programming(_, _)) => {}
142 Ok(ActionResult::Exit) => {
143 if local_editor_stack.pop().is_none() {
144 return Ok(ActionResult::Exit);
145 }
146 }
147 Ok(ActionResult::UserError(e)) => {
148 return Ok(ActionResult::UserError(e));
149 }
150 Ok(ActionResult::DebugText(_))
151 | Ok(ActionResult::Saved)
152 | Ok(ActionResult::Loaded)
153 | Ok(ActionResult::SaveError(_))
154 | Ok(ActionResult::LoadError(_)) => {
155 return Ok(ActionResult::UserError(
156 "Programs are not allowed to use the debug, save, or load actions.".to_string(),
157 ));
158 }
159 Err(e) => return Err(e),
160 }
161 }
162
163 Ok(ActionResult::ShowStatus)
164}
165
166pub fn run_action(
180 user_key: &mut String,
181 world: &mut Tree<String, Vessel>,
182 action: Option<Action>,
183 editor_stack: &mut Vec<String>,
184) -> Result<ActionResult, Box<dyn std::error::Error>> {
185 let parent_key = world.get_parent(user_key)?.clone();
186
187 match editor_stack.first().map(|editing| world.get_mut(editing)) {
188 Some(Ok(editing_node)) => match action {
189 Some(Action::Exit) => {
190 editor_stack.pop();
191 if editor_stack.is_empty() {
192 Ok(ActionResult::DoneProgramming)
193 } else {
194 editing_node.value.program = match &editing_node.value.program {
195 Some(program) => Some(
196 program
197 .clone()
198 .into_iter()
199 .chain(iter::once(Action::Exit))
200 .collect(),
201 ),
202 None => Some(vec![Action::Exit]),
203 };
204
205 Ok(ActionResult::StillProgrammingButExit)
206 }
207 }
208 Some(Action::Program(vessel_request)) => {
209 let action_copy = Action::Program(vessel_request.clone());
210 let name = vessel_request
211 .map(|vr| vr.name)
212 .unwrap_or(parent_key.clone());
213 editor_stack.push(name);
214 editing_node.value.program = match &editing_node.value.program {
215 Some(program) => Some(
216 program
217 .clone()
218 .into_iter()
219 .chain(iter::once(action_copy))
220 .collect(),
221 ),
222 None => Some(vec![action_copy]),
223 };
224
225 Ok(ActionResult::StillProgramming)
226 }
227 Some(action) => {
228 editing_node.value.program = match &editing_node.value.program {
229 Some(program) => Some(
230 program
231 .clone()
232 .into_iter()
233 .chain(iter::once(action))
234 .collect(),
235 ),
236 None => Some(vec![action]),
237 };
238
239 Ok(ActionResult::StillProgramming)
240 }
241 None => Ok(ActionResult::StillProgramming),
242 },
243 Some(Err(e)) => Err(Box::new(e)),
244 None => match action {
245 Some(action) => match action {
246 Action::Create(VesselRequest { article, name }) => {
247 if let Err(e) = world.add(
248 name.clone(),
249 Node::new(Vessel::with_article(article), parent_key),
250 ) {
251 match e {
252 tree::Error::NonUniqueKey(key) => {
253 Ok(ActionResult::UserError(format!("{key} already exists")))
254 }
255 _ => Err(Box::new(e)),
256 }
257 } else {
258 Ok(ActionResult::ShowStatus)
259 }
260 }
261 Action::Become(VesselRequest { article, name }) => {
262 if world
263 .get_parent(&name)
264 .is_ok_and(|parent| parent == &parent_key)
265 {
266 *user_key = name;
267
268 Ok(ActionResult::ShowStatus)
269 } else {
270 Ok(ActionResult::UserError(format!(
271 "You don't see {}.",
272 vessel_text(&name, &article)
273 )))
274 }
275 }
276 Action::Enter(VesselRequest { article, name }) => {
277 if let Err(e) = world.set_parent(user_key, name.clone()) {
278 match e {
279 tree::Error::NonexistentNode(_) => Ok(ActionResult::UserError(
280 format!("You don't see {}.", vessel_text(&name, &article)),
281 )),
282 _ => Err(Box::new(e)),
283 }
284 } else {
285 Ok(ActionResult::ShowStatus)
286 }
287 }
288 Action::Leave => {
289 world.set_parent(user_key, world.get_parent(&parent_key)?.clone())?;
290
291 Ok(ActionResult::ShowStatus)
292 }
293 Action::Take(VesselRequest { article, name }) => {
294 if world
295 .get_parent(&name)
296 .is_ok_and(|selected_parent| selected_parent == &parent_key)
297 {
298 world.set_parent(&name, user_key.clone())?;
299
300 Ok(ActionResult::ShowStatus)
301 } else {
302 Ok(ActionResult::UserError(format!(
303 "You don't see {}.",
304 vessel_text(&name, &article)
305 )))
306 }
307 }
308 Action::Drop(VesselRequest { article, name }) => {
309 if world
310 .get_parent(&name)
311 .is_ok_and(|selected_parent| selected_parent == user_key)
312 {
313 world.set_parent(&name, parent_key.clone())?;
314
315 Ok(ActionResult::ShowStatus)
316 } else {
317 Ok(ActionResult::UserError(format!(
318 "You aren't carrying {}.",
319 vessel_text(&name, &article)
320 )))
321 }
322 }
323 Action::Move {
324 object_article,
325 object_name,
326 preposition: _,
327 target_article,
328 target_name,
329 } => {
330 if world
331 .get_parent(&object_name)
332 .is_ok_and(|object_parent| object_parent == &parent_key)
333 {
334 if world
335 .get_parent(&target_name)
336 .is_ok_and(|target_parent| target_parent == &parent_key)
337 {
338 world.set_parent(&object_name, target_name)?;
339 Ok(ActionResult::ShowStatus)
340 } else {
341 Ok(ActionResult::UserError(format!(
342 "You don't see {}",
343 vessel_text(&target_name, &target_article)
344 )))
345 }
346 } else {
347 Ok(ActionResult::UserError(format!(
348 "You don't see {}",
349 vessel_text(&object_name, &object_article)
350 )))
351 }
352 }
353 Action::Look {
354 preposition: _,
355 basic,
356 } => match basic {
357 Some(VesselRequest { article, name }) => {
358 match world
359 .get_from_parent(&name, &parent_key)
360 .ok()
361 .flatten()
362 .or_else(|| world.get_from_parent(&name, user_key).ok().flatten())
363 {
364 Some(node) => Ok(ActionResult::ResponseText(format!(
365 "You look at {}.\n{}{}",
366 vessel_text(&name, &article),
367 note_text(&node.value),
368 list_children(world.get_all_values(world.children(&name))?)
369 ))),
370 None => Ok(ActionResult::UserError(format!(
371 "You don't see {}.",
372 vessel_text(&name, &article)
373 ))),
374 }
375 }
376 None => Ok(ActionResult::ResponseText(format!(
377 "{}{}",
378 note_text(&world.get(&parent_key)?.value),
379 list_children(world.get_all_values(world.siblings(user_key, false)?)?)
380 ))),
381 },
382 Action::Transform {
383 preposition: _,
384 basic: VesselRequest { article: _, name },
385 } => {
386 if let Err(e) = world.rename(user_key, name.clone()) {
387 match e {
388 tree::Error::NonUniqueKey(key) => {
389 Ok(ActionResult::UserError(format!("{key} already exists.")))
390 }
391 e => Err(Box::new(e)),
392 }
393 } else {
394 *user_key = name;
395 Ok(ActionResult::ShowStatus)
396 }
397 }
398 Action::Note { text: note } => {
399 world.get_mut(&parent_key)?.value.note = note;
400
401 Ok(ActionResult::ShowStatus)
402 }
403 Action::Warp {
404 preposition: _,
405 basic: VesselRequest { article, name },
406 } => {
407 if let Err(e) = world.set_parent(user_key, name) {
408 match e {
409 tree::Error::NonexistentNode(node) => Ok(ActionResult::UserError(
410 format!("You can't find {}.", vessel_text(&node, &article)),
411 )),
412 e => Err(Box::new(e)),
413 }
414 } else {
415 Ok(ActionResult::ShowStatus)
416 }
417 }
418 Action::Program(vessel_request) => match vessel_request {
419 Some(VesselRequest { article, name }) => {
420 if world
421 .get_parent(&name)
422 .is_ok_and(|parent| parent == &parent_key)
423 {
424 editor_stack.push(name.clone());
425 Ok(ActionResult::Programming(name, article))
426 } else {
427 Ok(ActionResult::UserError(format!(
428 "You can't find {}.",
429 vessel_text(&name, &article)
430 )))
431 }
432 }
433 None => {
434 editor_stack.push(parent_key.clone());
435 let article = world.get(&parent_key)?.value.article;
436 Ok(ActionResult::Programming(parent_key, article))
437 }
438 },
439 Action::Use(VesselRequest { article, name }) => {
440 if let Some(program) = world
441 .get_from_parent(&name, &parent_key)
442 .ok()
443 .flatten()
444 .map(|node| node.value.program.clone())
445 {
446 match program {
447 Some(program) => run_program(user_key, world, program),
448 None => Ok(ActionResult::UserError(format!(
449 "{} has no program.",
450 vessel_text(&name, &article).capitalize_first_only()
451 ))),
452 }
453 } else {
454 Ok(ActionResult::UserError(format!(
455 "You can't find {}.",
456 vessel_text(&name, &article)
457 )))
458 }
459 }
460 Action::Cast {
461 spell,
462 preposition: _,
463 target,
464 } => {
465 if let Some(program) = world
466 .get(&spell.name)
467 .ok()
468 .map(|node| node.value.program.clone())
469 {
470 match program {
471 Some(program) => {
472 if let Ok(Some(_)) =
473 world.get_from_parent(&target.name, &parent_key)
474 {
475 let mut name = target.name;
476 run_program(&mut name, world, program)
477 } else {
478 Ok(ActionResult::UserError(format!(
479 "You can't find {}.",
480 vessel_text(&target.name, &target.article)
481 )))
482 }
483 }
484 None => Ok(ActionResult::UserError(format!(
485 "{} has no program.",
486 vessel_text(&spell.name, &spell.article).capitalize_first_only()
487 ))),
488 }
489 } else {
490 Ok(ActionResult::UserError(format!(
491 "You can't find {}.",
492 vessel_text(&spell.name, &spell.article)
493 )))
494 }
495 }
496 Action::Save { filename } => {
497 let filename = filename.unwrap_or("parade_save".to_string()) + ".json";
498 let save_data = serde_json::to_string(&world)?;
499 match std::fs::write(filename, save_data) {
500 Ok(_) => Ok(ActionResult::Saved),
501 Err(e) => Ok(ActionResult::SaveError(e)),
502 }
503 }
504 Action::Load { filename } => {
505 let filename = filename.unwrap_or("parade_save".to_string()) + ".json";
506 let save_data_text = match std::fs::read_to_string(filename) {
507 Ok(data) => data,
508 Err(e) => return Ok(ActionResult::LoadError(e)),
509 };
510 let save_data: Tree<String, Vessel> = serde_json::from_str(&save_data_text)?;
511 *world = save_data;
512 Ok(ActionResult::Loaded)
513 }
514 Action::Exit => Ok(ActionResult::Exit),
515 Action::Debug => Ok(ActionResult::DebugText(format!("{:?}", world))),
516 Action::Learn {
517 preposition: _,
518 topic: Some(topic),
519 } => Ok(ActionResult::HelpText(topic.help_text())),
520 Action::Learn {
521 preposition: _,
522 topic: None,
523 } => Ok(ActionResult::HelpText(Verb::help_list_text())),
524 },
525 None => Ok(ActionResult::ShowStatus),
526 },
527 }
528}