Help language development. Donate to The Perl Foundation

META6::To::Man cpan:TBROWDER last updated on 2017-10-05

lib/META6/To/Man.pm6
unit module META6::To::Man;

use META6;

enum DirStat <NoWrite NoDir CanWrite>;

# variables set from input args
my $section;
# mandatory args
my $meta6      = 0; # META6 object
# options
my $man        = 0; # file name
my $debug      = 0; # 0 | 1
my $install    = 0; # 0 | 1
my $install-to = 0; # dir name
my $date       = Date.today.Str; # default

my $verbose    = 1; # default
my $quiet      = 0; # if true, then $verbose is set to 0

sub meta6-to-man(@*ARGS) is export {
    handle-args @*ARGS;

    my $f = write-man-file $man;

    if $verbose {
	say "Normal end.  See output file:";
	say "  $f";
    }

} # meta6-to-man

sub write-man-file($man is rw) {

    # extract data from the META6 file

    # mandatory: guaranteed to have these
    my $version = $meta6.AT-KEY: 'version';
    my $name    = $meta6.AT-KEY: 'name';
    my $descrip = $meta6.AT-KEY: 'description';

    # optional per spec
    my $src-url = $meta6.AT-KEY: 'source-url';
    my $license = $meta6.AT-KEY: 'license';

    my $supp    = $meta6.support;
    my $bugs    = $supp.bugtracker;

    if $debug {
	say "DEBUG: \$descrip  = '$descrip'";
	say "       \$name     = '$name'";
	say "       \$src-url  = '$src-url'";
	say "       \$bugs     = '$bugs'";
	say "       \$license  = '$license'";
    }

    # check required fields
    # need a file name
    if !$man {
        $section = 1;
        $man = $name ~ ".$section";
    }

    # generate the man file as a string first
    my $s  = ".TH $name $section $date Perl6.org\n";

    $s    ~= ".SH NAME $name\n";
    $s    ~= "item \- $descrip\n";

    $s    ~= ".SH SYNOPSIS\n";
    $s    ~= "use $name;\n";

    $s    ~= ".SH DESCRIPTION\n";
    $s    ~= "This module is in the Perl 6 ecosystem.\n";
    if $src-url {
	$s ~= "Its source can be found at\n";
	$s    ~= ".UR $src-url\n";
	$s    ~= ".UE .\n";
    }
    else {
	$s ~= "However, its source location is unknown.\n";
    }

    $s    ~= ".SH BUGS\n";
    $s    ~= "Submit bug reports to\n";
    if $bugs {
	$s    ~= ".UR $bugs\n";
	$s    ~= ".UE .\n";
    }
    else {
	$s    ~= "Perl 6 IRC channel #perl6.\n";
    }

    if $license {
	$s    ~= ".SH LICENSE\n";
	$s    ~= "$license\n";
    }

    #$s    ~= ".SH SEE ALSO\n";

    my $f = $man;
    if $install {
	# check the standard dirs
	my $d = check-install-standard $section;
	if $d {
            $f = "$d/$f";
	}
	else {
	    say "FATAL:  Unable to install to dir '$d'--check it exists with write privileges." if $verbose;
	    exit 1;
            #$f = "./$f";
	}
    }
    elsif $install-to {
        $f = "$install-to/$f";
    }

    # write the file
    spurt $f, $s;
    return $f;

} # write-man-file

sub check-date-value($val) {
    # date should be in yyyy-mm-dd format
    my $d = Date.new: $val;
    CATCH {
        say "FATAL: Date entry '$val' is not in YYYY-MM-DD format." if $verbose;
        exit 1;
    }
    $date = $d.Str;

} # check-date-value

sub check-meta6-value($val){
    # val is a valid META6.json file
    if !$val.IO.f {
        say "FATAL: File '$val' doesn't exist." if $verbose;
        exit 1;
    }
    my $m = META6.new: :file($val);
    CATCH {
        say "FATAL: File '$val' is not a valid META6 file." if $verbose;
        exit 1;
    }

    check-meta6-validity $m;

    $meta6 = $m;

} # check-meta6-value

sub check-install-to-value($val) {
    # $val is a directory name the user must be able to write to
    my $res = check-dir-status $val;

    if $res ~~ NoWrite  {
	say "FATAL: Unable to write to directory $val." if $verbose;
	exit 1;
    }
    elsif $res ~~ NoDir {
        say "FATAL: Directory $val doesn't exist." if $verbose;
        exit 1;
    }

    # must be okay
    $install-to = $val;

} # check-install-to-value

sub check-man-value($val) {
    # $val is the desired name of the man file. ensure it
    # has a valid file extension
    if $val ~~ / '.' (<[1..8>]> ** 1) $/ {
        # name is okay
        $man = $val;
        $section = ~$0;
    }
    else {
        say "FATAL: Man name '$val' needs a number extension in the range '1..8'." if $verbose;
        exit 1;
    }

} # check-man-value

sub handle-args(@*ARGS) {
    # check for debug first
    my @args;
    for @*ARGS {
        if /:i debug / {
	    $debug = 1;
            next;
        }
        @args.append: $_;
    }

    for @args {
	say "DEBUG: arg '$_'" if $debug;
	my $val;
	my $need-value = 0;
	if /:i ^ \s* '--' (<-[=]>+) \s* $ / {
	    # good arg format
	    $_ = ~$0;
	    say "  DEBUG: good arg format" if $debug;
	}
	elsif /:i ^ \s* '--' (<-[=]>+) '=' (<-[=]>+) \s* $ / {
	    # good arg format
	    say "  DEBUG: good arg format" if $debug;
	    $_   = ~$0;
	    $val = ~$1;
	}
	else {
	    say "FATAL: Unknown arg '$_'." if $verbose;
	    exit 1;
	}

	if $debug {
	    say "  DEBUG: good arg '$_'";
	    say "  DEBUG: good val '$val'" if $val.defined;
	}

	#===== options with a value
        when /:i ^ man  $ / {
	    say "  DEBUG: inside when block, option = '$_'" if $debug;
	    # skip if no value
	    if !$val.defined { $need-value++; proceed }
            check-man-value $val;
	}
        when /:i ^ 'install-to'  $ / {
	    # option with value
	    say "  DEBUG: inside when block, option = '$_'" if $debug;
	    # skip if no value
	    if !$val.defined { $need-value++; proceed }
            check-install-to-value $val;
	}
        when /:i ^ meta6  $ / {
	    say "  DEBUG: inside when block, option = '$_'" if $debug;
	    # skip if no value
	    if !$val.defined { $need-value++; proceed }
            check-meta6-value $val;
	}
        when /:i ^ date  $ / {
	    say "  DEBUG: inside when block, option = '$_'" if $debug;
	    # skip if no value
	    if !$val.defined { $need-value++; proceed }
            check-date-value $val;
	}

	#===== options with NO value
        when /:i ^ install $ / {
	    # option with no value
	    say "  DEBUG: inside when block, option = '$_'" if $debug;
	    # skip if it has a value
	    proceed if $val.defined;
            $install = 1;
        }
        when /:i ^ quiet $ / {
	    # option with no value
	    say "  DEBUG: inside when block, option = '$_'" if $debug;
	    # skip if it has a value
	    proceed if $val.defined;
            $quiet = 1;
            $verbose = !$quiet;
        }
        default {
            my $msg;
	    if $val.defined {
		 $msg = "FATAL: Unknown arg with value '{$_}={$val}'.";
	    }
	    elsif $need-value {
		$msg = "FATAL: Known arg '{$_}' also needs a value (e.g., 'arg=value').";
	    }
	    else {
		$msg = "FATAL: Unknown arg '{$_}' with no value.";
	    }
            say "$msg" if $verbose;
	    exit 1;
        }
    }

    # one more check
    if !$meta6 {
	say "FATAL: Missing option '--meta6=M'." if $verbose;
        exit 1;
    }

} # handle-args

sub check-meta6-validity(META6 $m, :$file?) is export {
    # check for validity
    my $err = 0;
    my $msg = "ERROR:  META6 is missing mandatory key:";

    # mandatory attributes per spec
    for 'version', 'name', 'description' -> $k {
        unless $m.AT-KEY($k) {
            ++$err;
            say "$msg $k";
        }
    }
    =begin comment
    # doesn't work as expected; I filed META6 issue #9
    for 'version', 'name', 'description' -> $k {
        unless $m.EXISTS-KEY($k) {
            ++$err;
            say "$msg $k";
        }
    }
    =end comment

    if $err {
	if $file {
            say "FATAL:  Invalid META6 file: $file" if $verbose;
	}
	else {
            say "FATAL:  Invalid META6 file." if $verbose;
	}
        exit 1;
    }

} # check-meta6-validity

sub check-dir-status($dir --> DirStat) {
    if $dir.IO.d {
        # dir exists, can the user write to it?
        my $f = "$dir/.meta6-to-man";
        spurt $f, 'some text';
        CATCH {
	    say "WARNING: Unable to write to directory $dir." if $verbose;
	    return NoWrite;
        }
        # write is okay, remove the evidence
	unlink $f;
        return CanWrite;
    }

    # if we got here the dir doesn't exist
    say "WARNING: Directory $dir doesn't exist." if $verbose;
    return NoDir;
} # check-dir-status

sub check-install-standard($section --> Str) {
    # check the Linux FHS standard locations
    # and return the one to use, if any
    my @fhs = [
        "/usr/share/man/man{$section}",
        "/usr/local/share/man/man{$section}",
        "/usr/local/man/man{$section}",
    ];

    for @fhs -> $d {
	my $res = check-dir-status $d;
	return $d if $res ~~ CanWrite;
    }

    return '';

} # check-install-standard