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
104
105
106
107
108
109
110
111
112
113
use super::*;

/// A generic tracker for a property during a cli session.
///
/// # Examples
///
/// ```
/// # use dsa::commands::Tracker;
/// # use dsa::output::Output;
/// let mut health = Tracker::new_action("health",10,10);
/// let matches = health.usage().get_matches_from_safe(&["health","-s","5"]).unwrap();
/// # let hero = Default::default();
/// let output = health.call(&hero,&matches).unwrap();
/// assert_eq!(output.len(), 1);
/// if let Output::Gauge { name, current, max } = &output[0] {
///     assert_eq!("health", name);
///     assert_eq!(5, *current);
///     assert_eq!(10, *max);
/// }
/// # else {
/// #     panic!("unexpected output");
/// # }
/// ```
pub struct Tracker<'a>
{
	name: &'a str,
	max: isize,
	current: isize,
}

impl<'a> Tracker<'a>
{
	pub fn new_action(name: &'static str,current: isize,max: isize) -> Box<dyn Action>
	{
		Box::new(Tracker
		{
			name,
			max,
			current,
		})
	}
}

impl Action for Tracker<'_>
{
	fn usage<'a,'b>(&'a self) -> App<'b,'b>
	{
		SubCommand::with_name(self.name)
			.about("track the current value")
			.arg
				( Arg::with_name("get")
				.long("get")
				.short("g")
				.help("get current value (default)")
				)
			.arg
				( Arg::with_name("set")
				.long("set")
				.short("s")
				.help("set current value")
				.takes_value(true)
				)
			.arg
				( Arg::with_name("add")
				.long("add")
				.help("add to current value")
				.takes_value(true)
				)
			.arg
				( Arg::with_name("sub")
				.long("sub")
				.help("subtract of current value")
				.takes_value(true)
				)
			.arg
				( Arg::with_name("max")
				.long("max")
				.short("m")
				.help("change max value instead of current")
				)
			.group
				( ArgGroup::with_name("action")
				. args(&["get","set","sub","add"])
				)
	}

	fn call(&mut self, _: &Hero, matches: &ArgMatches) -> Result<Vec<Output>>
	{
		if matches.is_present("action") && !matches.is_present("get")
		{
			let target = if matches.is_present("max") { &mut self.max } else { &mut self.current };
			*target = match [matches.value_of("set"),matches.value_of("add"),matches.value_of("sub")]
			{
				[Some(value),None,None] => value.parse::<isize>()?,
				[None,Some(value),None] => *target+value.parse::<isize>()?,
				[None,None,Some(value)] => *target-value.parse::<isize>()?,
				_ => unreachable!(),
			}
		}

		// keep it within bounds 0 <= current <= max
		self.max = self.max.max(0);
		self.current = self.current.max(0).min(self.max);

		Ok(vec![Output::Gauge
		{
			name: self.name.to_string(),
			current: self.current,
			max: self.max,
		}])
	}
}