Rash - A Raku Shell

Exercise 2: Configuration

by Arne Sommer

Rash - Exercise 2: Configuration

[67.3.2] Published 13. April 2020.

You have read the Exercise text first?

We start with the file name, as a constant (without a sigil):

constant CONF_FILE = ( $*HOME.add: '.rashrc' ).Str;

See docs.raku.org/routine/constant for more information about constant.

Then we call «read-conf» instead of the «do-allow "more";»-line:

read-conf;

And finally we define «read-conf»:

sub read-conf
{
  return unless CONF_FILE.IO.r;
  
  for CONF_FILE.IO.lines
  {
    .say;
  }
}

Check that it works, without the «.rashrc» file.

Add the «.rashrc» file, and see that the allow-lines are displayed:

$ raku rash-conf
allow more
allow top

rash: Enter «exit» to exit
> 

Then we can swap the dummy say line with a copy of the when line:

sub read-conf
{
  return unless CONF_FILE.IO.r;
  
  for CONF_FILE.IO.lines
  {
    when /^allow\s+(.*)/ { do-allow $0.Str; }
  }
}

You do remember that the trailing .Str is extremely important here, as $0 is a match object and not a string.

The complete program (based on «rash-pwd»):

File: rash-conf
use Linenoise;

constant HIST_FILE = ( $*HOME.add: '.rash-hist' ).Str;
constant HIST_LEN  = 25;
constant CONF_FILE = ( $*HOME.add: '.rashrc' ).Str;

linenoiseHistoryLoad(HIST_FILE);
linenoiseHistorySetMaxLen(HIST_LEN);

my @commands = <allow cd exit help pwd run version>;
my %allow;

read-conf;

linenoiseSetCompletionCallback(-> $line, $c
{
  for @commands.grep(/^ $line /).sort -> $cmd
  {
    linenoiseAddCompletion($c, $cmd);
  }
});

say 'rash: Enter «exit» to exit';

while (my $line = linenoise '> ').defined
{
  linenoiseHistoryAdd($line);

  given $line
  {
    when /^allow\s+(.*)/ { do-allow $0; }
    when /^cd\s+(\S+)/   { do-chdir $0; }
    when "exit"          { last; }
    when "help"          { say "Legal commands: { @commands }" }
    when "pwd"           { say $*CWD.Str; }
    when /^run\s+(.*)/   { do-run $0; }
    when "version"       { say "Version 0.11"; }
    default
    {
      my ($cmd, $arg) = $line.split(/\s/, 2);
      %allow{$cmd}
        ?? do-run $line
        !! say "Unknown command: \"$_\" (use \"help\" for a list of commands)";
    }
  }
}

linenoiseHistorySave(HIST_FILE);

sub do-run ($line)
{
  my ($cmd, @args) = $line.words;

  my $res = @args
    ?? run $cmd, @args
    !! run $cmd;

  if ! $res.pid
  {
    say "$cmd: command not found";
  }
  elsif $res.exitcode 
  {
    say "$cmd: exit with code { $res.exitcode }";
  }
}

sub do-chdir ($dir)
{
  say "cd: $dir: No such file or directory" unless chdir $dir;
}

sub do-allow ($cmd)
{
  @commands.push($cmd);
  %allow{$cmd} = True;
}

sub read-conf
{
  return unless CONF_FILE.IO.r;
  
  for CONF_FILE.IO.lines
  {
    when /^allow\s+(.*)/ { do-allow $0.Str; }
  }
}