App-vaporcalc

 view release on metacpan or  search on metacpan

bin/vaporcalc  view on Meta::CPAN

use Defaults::Modern;

use App::vaporcalc::CmdEngine;
use App::vaporcalc::FormatString;
use App::vaporcalc::Recipe;
use App::vaporcalc::RecipeResultSet;
use App::vaporcalc::Types -all;

use Getopt::Long;
my $Opts = +{
  help => sub {
    say $_ for (
      "vaporcalc - App::vaporcalc\n",
      "  --recipe=PATH    Load specified recipe",
      "  --nocolor        Disable ANSI colors",
      "",
      "  --help",
      "  --version",
    );
    exit 0
  },

  version => sub {
    my $vers = $App::vaporcalc::Recipe::VERSION || '[git]';
    say "vaporcalc (App::vaporcalc $vers)";
    exit 0
  },

  nocolor => 0,
  recipe  => '',
};

GetOptions( $Opts,
  'help',
  'version',
  'nocolor',
  'recipe=s',
);

fun getopts { state $argv = hash(%$Opts)->inflate }

use Term::ANSIColor ();
fun colorify (Str $color, Str $text) {
  getopts->nocolor ? $text : Term::ANSIColor::colored($text, $color)
}

fun format_error ($err) {
  # Colorify first line only:
  return '' unless $err;
  my @lines = split "\n", "$err";
  $lines[0] = colorify red => $lines[0];
  join "\n", @lines
}

fun flavor_strings (RecipeResultSet $rset) {
  my $recipe = $rset->recipe;
  my $result = $rset->result;

  my $flav_recipe_str = '';
  for my $flav ($recipe->flavor_array->all) {
    my $pcnt = colorify 'bold red' => $flav->percentage;
    my $tag  = $flav->tag;
    my $type = $flav->type;
    $flav_recipe_str .= "   $tag -> $pcnt %  ($type)\n";
  }

  my $flav_result_str = '';
  for my $flav ($result->flavors->kv->all) {
    my ($tag, $ml) = @$flav;
    $ml = colorify 'bold yellow' => $ml;
    $flav_result_str .= "   $tag => [$ml ml]\n";
  }

  hash(
    recipe_flavor_string => $flav_recipe_str,
    result_flavor_string => $flav_result_str
  )->inflate
}

fun format_resultset (RecipeResultSet $rset) {
  state $tmpl = <<'EOT';
 --> target amount: %target_quantity ml
  pg: %target_pg %  vg: %target_vg %
  nic base: %base_nic_per_ml mg/ml  nic type: %base_nic_type%
  nic target: %target_nic_per_ml mg/ml
  flavor targets:
%flavor_recipe
  notes: %notes
 =>
  vg: [%vg ml]  pg: [%pg ml]  nic base: [%nic ml]
   (max vg total: %max_vg_total ml)
  flavors:
%flavor_result
  total: %total ml
EOT

  my $recipe = $rset->recipe;
  my $result = $rset->result;

  my $flav_recipe_str = flavor_strings($rset)->recipe_flavor_string;
  my $flav_result_str = flavor_strings($rset)->result_flavor_string;

  format_str( $tmpl =>
    notes => sub {
      $recipe->notes->has_any ?
        "\n" . $recipe->notes
                ->map(sub { colorify('bold yellow', ' - '.$_) })
                ->join("\n")
        : 'none'
    },

    flavor_recipe => $flav_recipe_str,
    flavor_result => $flav_result_str,

    (map {; $_ => colorify('bold red', $recipe->$_) } qw/
      target_quantity target_nic_per_ml
      base_nic_per_ml base_nic_type
      target_pg target_vg
    /),

    (map {; $_ => colorify('bold yellow', $result->$_) } qw/
      vg pg nic total
    /),

    max_vg_total => sub {
      colorify('bold yellow', $result->pg + $result->vg)
    },
  )
}


use Term::UI;
use Term::ReadLine;
my $termpkg = 
  $ENV{PERL_RL} 
  || try { use_module('Term::ReadLine::Perl5') }
  || try { use_module('Term::ReadLine::Perl')  }
  || 'Term::ReadLine';
my $term  = $termpkg->new('vcalc'); 
my $outfh = $term->OUT || \*STDOUT;
$outfh->autoflush(1);

$outfh->say('Welcome to App::vaporcalc!');
$outfh->say("Try '@{[ colorify yellow => 'help' ]}' for usage details.");

my $cmdeng = App::vaporcalc::CmdEngine->new;
my $recipe = App::vaporcalc::Recipe->new(
  target_quantity   => 10,
  base_nic_per_ml   => 100,
  target_nic_per_ml => 12,
  target_pg         => 45,
  target_vg         => 55,
  flavor_percentage => 10,
);
my $initial_input = getopts->recipe ? 'recipe load '.getopts->recipe : undef;

my $orig_prompt = colorify green => 'vcalc> ';
my $prompt;
PROMPT: while (1) {
  $prompt //= $orig_prompt;

  my $input = $initial_input // $term->get_reply(
    prompt  => $prompt,
    default => 'calc'
  );
  undef $initial_input;

  next PROMPT unless $input;

  $term->addhistory($input);

  $input = 'show recipe' if $input eq 'calc';
  last PROMPT            if $input =~ /^(?:exit|quit)/i;

  my $parsed = try {
    $cmdeng->parse_cmd($input)
  } catch {
    $outfh->say( format_error(">> Parser err: $_") );
    undef
  } or next PROMPT;

  my $cmd_result = try {
    $cmdeng->prepare_cmd(
      subject => $parsed->subject,
      verb    => $parsed->verb,
      params  => $parsed->params,



( run in 1.753 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )