App-PFT

 view release on metacpan or  search on metacpan

bin/pft-edit  view on Meta::CPAN


    pft edit -B -r -y2014 -d27 -m Aug

=head1 SEE ALSO

L<pft-init(1)>, L<pft-make(1)>

=cut

use strict;
use warnings;

use App::PFT;

use PFT::Tree;
use PFT::Date;

use Pod::Usage;

use Encode;
use Encode::Locale;

use Getopt::Long qw/GetOptionsFromArray/;
Getopt::Long::Configure qw/bundling/;

my %opts;
my %datespec;

GetOptions(
    'year|y=i'      => sub { $datespec{y}  = $_[1] },
    'month|m=s'     => sub { $datespec{m} = $_[1] },
    'day|d=i'       => sub { $datespec{d}  = $_[1] },
    'B!'            => \$opts{B},
    'M!'            => \$opts{M},
    'T!'            => \$opts{T},
    'P!'            => \$opts{P},
    'author|a=s'    => sub { $opts{author} = $_[1] },
    'tag|t=s@'      => sub { push @{$opts{tags}}, $_[1] },
    'resume|r!'     => sub { $opts{back} = 0 },
    'back=i'        => sub { $opts{back} = int($_[1]) },
    'editor=s'      => sub { $opts{editor} = $_[1] },
    'stdin!'        => sub { $opts{stdin} = 1 },
    'append!'       => sub { $opts{append} = 1 },
    'select=i'      => sub { $opts{select} = $_[1] },
    'raw!'          => sub { $opts{raw} = 1 },
    'help|h!'       => sub {
        pod2usage
            -exitval => 1,
            -verbose => 2,
            -input => App::PFT::help_of 'edit',
    },
) or exit 1;

$opts{B} = 1 if !$opts{M} &&
    grep defined, $opts{back}, map $datespec{$_}, qw(y m d);

do {
    my @sel = grep $opts{$_}, qw(B M T P);
    if (@sel != 1) {
        local $, = ' -';
        say STDERR 'Select exactly one mode: -B -M -T -P';
        exit 2
    }
};

my $tree = eval{ PFT::Tree->new } || do {
    say STDERR $@ =~ s/ at.*$//rs;
    exit 3
};

my $conf = eval{ $tree->conf } || do {
    say STDERR 'Configuration error: ', $@ =~ s/ at.*$//rs;
    exit 4
};

my($entry, $hdr);

# The following block defines $entry as the entry we are going to edit.
# In some of the cases also $hdr will be defined with the corresponding header.
eval {
    if (defined $opts{back}) {
        my @entries;
        if (keys %datespec) {
            my $date = PFT::Date->from_spec(%datespec);
            @entries = $tree->content->blog_at($date);
            die 'none for the specified date ', $date unless @entries;
        }
        else {
            @entries = $tree->content->blog_back($opts{back});
            die "cannot go back $opts{back} days" unless @entries;
        }

        if (@entries == 1) {
            $entry = $entries[0];
        }
        elsif (exists $opts{select}) {
            $entry = $entries[$opts{select}];
            die 'invalid index ', $opts{select} unless defined $entry;
        }
        else {
            say STDERR 'Multiple entries:';
            for (my $idx = 0; $idx < @entries; $idx ++) {
                my $hdr = $entries[$idx]->header;
                say STDERR sprintf("%d: %s",
                    $idx,
                    $hdr ? $hdr->title : $_->name
                )
            }
            die 'disambiguate by providing --select=<index>'
        }
        $hdr = eval{ $entry->header };  # Might be malformed.
    } elsif ($opts{T}) {
        die 'mandatory title' unless @ARGV;
        $hdr = PFT::Header->new(
            title => join(' ', @ARGV),
            author => $conf->{site}{author},
        );
        $entry = $tree->content->new_tag($hdr)
    } else {
        if ($opts{M}) {
            $hdr = PFT::Header->new(
                author => $conf->{site}{author},
                date => PFT::Date->from_spec(%datespec)->derive(d => undef),
            )
        } elsif ($opts{B}) {
            $hdr = PFT::Header->new(
                title => join(' ', @ARGV) || 'Today',
                author => $conf->{site}{author},
                tags => $opts{tags} || [],
                date => eval{ PFT::Date->from_spec(%datespec) } || do {
                    say STDERR 'Invalid date: ', $@ =~ s/ at.*$//rs;
                },
            )
        } elsif ($opts{P}) {
            die 'mandatory title' unless @ARGV;
            $hdr = PFT::Header->new(
                title => join(' ', @ARGV),
                author => $conf->{site}{author},
                tags => $opts{tags} || [],
            )
        } else { die "unhandled case? This is a bug" }

        $entry = $tree->content->new_entry($hdr)
    }
    1;  # can be 1 only if there was no error.
}
or $@ && do {
    say STDERR 'Editing entry: ', $@ =~ s/at .*$//rs;
    exit 6
};

sub edit_file {
    my $path = $entry->path;
    my $editor = $opts{editor}
              || $conf->{system}{editor}
              || $ENV{EDITOR}
              || do {
        say STDERR "Cannot infer editor. Try setting env EDITOR or to";
        say STDERR "define it in configuration file (system -> editor)";
        exit 5
    };
    if ($editor =~ s/(?<!%)%s/$path/g) {
        system($editor)
    }
    else {
        system($editor, $path)
    }
}

sub feed_file {
    my $mode = '>';
    my $skip_header = $opts{raw} || !defined $hdr;
    if ($opts{stdin}) {
        die "Supported --stdin or --append, not both" if $opts{append};
    }
    elsif ($opts{append}) {
        if (-f $entry->path) {
            $mode .= '>';
            $skip_header = 1;
        }
    }

    open my $out, "$mode:encoding(locale)", $entry->path
        or die 'Cannot open ', $entry->path, ": $!";
    $hdr->dump($out) unless $skip_header;
    print $out <STDIN>;
    close $out;
}

eval {
    if ($opts{stdin} or $opts{append}) {
        feed_file
    }
    else {
        edit_file
    }

    if ($entry->exists) {
        if ($entry->void) {
            say STDERR "Removing empty entry at ", $entry->path;
            $entry->unlink;
        }
        else {
            $entry->make_consistent;
        }
    }
} or $@ && do {
    say STDERR "After editing: ", $@ =~ s/at .*$//sr;
    exit 7
}



( run in 0.352 second using v1.01-cache-2.11-cpan-e93a5daba3e )