1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use crate::context::Context;
use crate::repl::{Error, Subcommand};
use factorio_bot_core::miette::{IntoDiagnostic, Result};
use factorio_bot_core::paris::error;
use reedline_repl_rs::clap::{
  builder::PossibleValuesParser, Arg, ArgMatches, Command, PossibleValue,
};
use reedline_repl_rs::Repl;
use std::str::FromStr;
use strum::{EnumIter, EnumMessage, EnumString, IntoEnumIterator, IntoStaticStr};

async fn run(matches: ArgMatches, context: &mut Context) -> Result<Option<String>, Error> {
  let command =
    DumpType::from_str(matches.value_of("type").expect("Has default value")).into_diagnostic()?;
  let save_path = match matches.value_of("save").expect("Has default value") {
    "" => None,
    save_path => Some(save_path),
  };

  let instance_state = context.instance_state.read().await;
  if let Some(instance_state) = instance_state.as_ref() {
    match command {
      DumpType::World => {
        if let Some(world) = instance_state.world.as_ref() {
          world.dump(save_path)?;
        } else {
          error!("no factorio world found??");
        }
      }
      DumpType::EntityPrototypes => {
        if let Some(world) = instance_state.world.as_ref() {
          world.dump_entitiy_prototypes(save_path)?;
        } else {
          error!("no factorio world found??");
        }
      }
      DumpType::ItemPrototypes => {
        if let Some(world) = instance_state.world.as_ref() {
          world.dump_item_prototypes(save_path)?;
        } else {
          error!("no factorio world found??");
        }
      }
      DumpType::Recipes => {
        if let Some(world) = instance_state.world.as_ref() {
          world.dump_recipes(save_path)?;
        } else {
          error!("no factorio world found??");
        }
      }
    }
  } else {
    error!("no factorio instance running");
  }
  Ok(None)
}

#[derive(EnumString, EnumMessage, EnumIter, IntoStaticStr)]
#[strum(serialize_all = "kebab-case")]
enum DumpType {
  #[strum(message = "dump complete internal factorio world representation (very big)")]
  World,
  #[strum(message = "dump entity prototypes")]
  EntityPrototypes,
  #[strum(message = "dump item prototypes")]
  ItemPrototypes,
  #[strum(message = "dump recipes")]
  Recipes,
}

impl Subcommand for ThisCommand {
  fn name(&self) -> &str {
    "dump"
  }
  fn build_command(&self, repl: Repl<Context, Error>) -> Repl<Context, Error> {
    repl.with_command_async(
      Command::new(self.name())
        .about("dump information")
        .arg(
          Arg::new("type")
            .required(true)
            .value_parser(PossibleValuesParser::new(DumpType::iter().map(|action| {
              let message = action.get_message().unwrap();
              PossibleValue::new(action.into()).help(message)
            })))
            .help("type of information to dump"),
        )
        .arg(
          Arg::new("save")
            .default_value("")
            .long("save")
            .required(false)
            .help("path to save at"),
        ),
      |args, context| Box::pin(run(args, context)),
    )
  }
}

struct ThisCommand {}
pub fn build() -> Box<dyn Subcommand> {
  Box::new(ThisCommand {})
}