use strict;
use warnings;
use Slay::Maker;
use File::Next;
use Path::Class;
use Template;
use File::Cache::Parsed;
use Cwd 'abs_path';
use Text::Restructured;
use Text::Restructured::Writer::LibXML;
use XML::LibXML;
use XML::LibXSLT;
use YAML::Syck;
use Getopt::Long;
my $SRCPATH='src';
my $DSTPATH='dst';
my $DSTBASEURL='/';
my @TMPLPATH=('common/');
{
my $res=GetOptions('src|s=s'=>\$SRCPATH,
'dst|d=s'=>\$DSTPATH,
'url|u=s'=>\$DSTBASEURL,
'include|I=s'=>\@TMPLPATH,
);
exit 1 unless $res;
$SRCPATH=~s{/+$}{};
$DSTPATH=~s{/+$}{};
$DSTBASEURL=~s{/*$}{/};
}
my $stash={};
my $template_provider=Template::Provider->new({
INCLUDE_PATH=> \@TMPLPATH,
ABSOLUTE=>1,
RELATIVE=>1,
});
my $template=Template->new({
LOAD_TEMPLATES=>[$template_provider],
});
my $rest=Text::Restructured->new({},'WebCoso');
my $xml_parser=XML::LibXML->new();
my $xslt_proc=XML::LibXSLT->new();
my $xpath=XML::LibXML::XPathContext->new();
$xpath->registerNs('x', 'http://www.w3.org/1999/xhtml');
my $fc=File::Cache::Parsed->new(follow=>1);
$fc->add_parser(qr{\.tt2?$} =>
sub {
$template->context->template($_[0]);
});
$fc->add_parser(qr{\.rest\.txt$} =>
sub {
my $dudom=$rest->Parse($_[1],$_[0]);
return Text::Restructured::Writer::LibXML
->new->ProcessDOM($dudom);
});
$fc->add_parser(qr{\.xml$} =>
sub { $xml_parser->parse_string($_[1],$_[0]) });
$fc->add_parser(qr{\.xslt?$} =>
sub { $xslt_proc->parse_stylesheet
($xml_parser->parse_string($_[1],$_[0])) });
$fc->add_writer(qr{\.xml$} =>
sub { $_[1]->toFile($_[0]) });
$fc->add_parser(qr{\.ya?ml$} =>
sub { Load($_[1]) });
$fc->add_writer(qr{\.ya?ml$} =>
sub { DumpFile($_[0],$_[1]) });
sub dstUriFor {
my ($name)=@_;
warn "dstUriFor($name)\n";
$name=~s{\.[^.]+\.[^.]+$}{.html};
$name=~s{^\Q$SRCPATH\E/}{$DSTBASEURL};
return $name;
}
sub getTitleFor {
my ($lang,$path,$name)=@_;
my $doc_name=$name;
$doc_name=~s{\.html$}{.du.xml};
$doc_name=~s{/$}{/document.$lang.du.xml};
if ($doc_name=~m{^\Q$DSTBASEURL\E}) {
$doc_name=~s{^\Q$DSTBASEURL\E}{$SRCPATH/};
}
else {
$doc_name="$SRCPATH/$doc_name";
}
warn "getTitleFor($lang,$path,$name)->$doc_name\n";
my $doc=$fc->get($doc_name);
unless ($doc) {
warn "No document for <$doc_name>, returning <$name>\n";
return $name;
}
my $title=$xpath->findnodes(
q{/document/title},
$doc);
return $title;
}
$xslt_proc->register_function('http://webcoso.thenautilus.net/','title-for',\&getTitleFor);
sub isLang {
my ($lang,$name)=@_;
warn "isLang($lang,$name)\n";
return 1 if $name=~m{/$};
return 1 if $name=~m{/document\.$lang(\.|$)};
return;
}
$stash->{dstUriFor}=\&dstUriFor;
$stash->{isLang}=\&isLang;
sub expandTT {
my ($maker,$target,$deps,$matches)=@_;
warn "expandTT($target,@$deps,@$matches)\n";
my $tmpl=$fc->get($deps->[-1]);
my $vars={ path=> $matches->[0],
language => $matches->[1],
%$stash,
};
if (@$deps>1) {
warn "tagging as $deps->[0]\n";
$vars->{tagged}=$fc->get($deps->[0]);
}
push @{$template_provider->include_path},
file($deps->[-1])->parent;
$fc->put($target,
$template->context->process($tmpl,
$vars));
pop @{$template_provider->include_path};
}
sub parseRST {
my ($maker,$target,$deps,$matches)=@_;
$fc->put($target,$fc->get($deps->[-1]));
}
sub du2html {
my ($maker,$target,$deps,$matches)=@_;
my $du=$fc->get($deps->[-1]);
my $xslt=file($deps->[-1])->parent->file('du2html.xsl');
$xslt=$fc->get($xslt);
my $out=$xslt->transform($du,
XML::LibXSLT::xpath_to_string(
path => $matches->[0],
language => $matches->[1],
filename => $deps->[-1],
));
$fc->put($target,$xslt->output_string($out));
}
sub getTags {
my ($maker,$target,$deps,$matches)=@_;
my %tagged;
for my $doc_name (@$deps) {
my $doc=$fc->get($doc_name);
my @tags=map {$_->textContent}
$xpath->findnodes(
q{/document/docinfo/field[field_name='tags']/field_body/*/list_item},
$doc);
chomp for @tags;
push @{$tagged{$_}},$doc_name for @tags;
}
$fc->put($target,\%tagged);
}
sub getChanges {
my ($maker,$target,$deps,$matches)=@_;
print "changes: $target <- @$deps, @$matches\n";
open my $fh,'>',$target;
}
sub ifExists {
my ($src)=@_;
return sub {
my ($maker,$target,$matches)=@_;
my $dep=Slay::MakerRule::var_expand_dep($src,$target,$matches);
return if -e $target and ! -e $dep;
return $dep;
}
}
sub fromTo {
my ($base,$opts)=@_;
my $iter=File::Next::files(
{
file_filter=>$opts->{files},
descend_filter=>$opts->{dirs},
},
$base);
my (@ret,$file);
if (defined $opts->{transform}) {
push @ret,$opts->{transform}->($file) while $file=$iter->();
}
else {
push @ret,$file while $file=$iter->();
}
return @ret;
}
{
my %order=(
'document.rest.tt'=>0,
'document.rest.txt'=>1,
'document.du.xml'=>2,
);
sub earliest {
my ($a,$b)=@_;
return $a unless $b;
return $order{$a} < $order{$b}
? $a
: $b;
}
}
sub keepEarliest {
my %dirs;
for my $f (@_) {
my $c=file($f);
$dirs{$c->parent}=earliest($c->basename,$dirs{$c->parent});
}
my @ret;
while (my ($d,$f)=each %dirs) {
push @ret,file($d,$f)->stringify;
}
return @ret;
}
my %docfiles=(
files=>sub{m{^document\.} and ($File::Next::dir !~ m{/\.svn/})},
dirs=>sub{!m{^(tags|_webcoso)$}});
my $maker=Slay::Maker->new({
rules => [
["$SRCPATH/tags/(**)/document.(*).rest.txt",':',"$SRCPATH/_webcoso/tags.yml",ifExists("$SRCPATH/tags/\$1/document.\$2.rest.tt"),'=',\&expandTT],
["$SRCPATH/tags/(**)/document.(*).du.xml",':',"$SRCPATH/_webcoso/tags.yml",ifExists("$SRCPATH/tags/\$1/document.\$2.rest.txt"),'=',\&parseRST],
["$SRCPATH/(**)/document.(*).rest.txt",':',ifExists("$SRCPATH/\$1/document.\$2.rest.tt"),'=',\&expandTT],
["$SRCPATH/(**)/document.(*).du.xml",':',ifExists("$SRCPATH/\$1/document.\$2.rest.txt"),'=',\&parseRST],
["$SRCPATH/()document.(*).rest.txt",':',ifExists("$SRCPATH/document.\$2.rest.tt"),'=',\&expandTT],
["$SRCPATH/()document.(*).du.xml",':',ifExists("$SRCPATH/document.\$2.rest.txt"),'=',\&parseRST],
["$SRCPATH/_webcoso/tags.yml",':',fromTo("$SRCPATH/",{%docfiles,transform=>sub{my $s=shift;$s=~s{\.[^.]+\.[^.]+$}{.du.xml};$s}}),'=',\&getTags],
["$DSTPATH/tags/(**)/document.(*).html",':',"$SRCPATH/_webcoso/tags.yml","$SRCPATH/tags/\$1/document.\$2.du.xml",'=',\&du2html],
["$DSTPATH/(**)/document.(*).html",':',"$SRCPATH/_webcoso/tags.yml","$SRCPATH/\$1/document.\$2.du.xml",'=',\&du2html],
["$DSTPATH/()document.(*).html",':',"$SRCPATH/_webcoso/tags.yml","$SRCPATH/document.\$2.du.xml",'=',\&du2html],
],
options => {
auto_create_dirs => 1,
},
});
my @targets=(fromTo("$SRCPATH/",
{
%docfiles,
transform=>sub{
my $src=shift;
$src=~s{\.[^.]+\.[^.]+$}{.html};
$src=~s{^\Q$SRCPATH\E/}{$DSTPATH/};
return $src;
},
}),
fromTo("$SRCPATH/tags/",
{
%docfiles,
transform=>sub{
my $src=shift;
$src=~s{\.[^.]+\.[^.]+$}{.html};
$src=~s{^\Q$SRCPATH\E/tags/}{$DSTPATH/tags/};
return $src;
},
}));
print "targets: @targets\n";
$maker->make(@targets);