123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package SugarTrail::Template::Master;
  2. # Master template holder
  3. # new()
  4. # parse()
  5. # _parse_headers()
  6. # _parse_body()
  7. use strict;
  8. use warnings;
  9. use Carp;
  10. use SugarTrail::Template::Slave;
  11. use SugarTrail::Template::Condition;
  12. sub new {
  13. my $class = shift;
  14. my $args = { @_ };
  15. my $self = {};
  16. $self->{args} = $args;
  17. return bless $self, $class;
  18. }
  19. sub parse {
  20. my $self = shift;
  21. my ($head, $body) = split "\n\n", $self->{text}, 2;
  22. $self->{head} = $head;
  23. $self->{body} = $body;
  24. return (
  25. $self->_parse_headers(),
  26. $self->_parse_body()
  27. );
  28. }
  29. sub _parse_headers {
  30. my $self = shift;
  31. foreach (split "\n", $self->{head}) {
  32. my ($n, $v) = split ": ", $_, 2;
  33. $self->{meta}->{$n} = $v;
  34. }
  35. return scalar keys %{ $self->{meta} };
  36. }
  37. sub _parse_body {
  38. my $self = shift;
  39. $self->{steps} = [];
  40. foreach (split "\n", $self->{body}) {
  41. chomp; s/ *$//;
  42. my @conds;
  43. my ($line, $cond_block) = $_ =~ m/^(.*?)(\{(.*)\})?$/;
  44. $line =~ s/ *$//;
  45. if ($cond_block) {
  46. $cond_block =~ s/\}$//;
  47. $cond_block =~ s/^\{//;
  48. @conds = split m/\s*;\s*/, $cond_block;
  49. }
  50. push @{ $self->{steps} }, { line => $line, conds => \@conds };
  51. }
  52. return scalar @{ $self->{steps} };
  53. }
  54. sub generate_slave {
  55. my $self = shift;
  56. my $args = shift;
  57. #create an "empty" slave
  58. my $slave = SugarTrail::Template::Slave->new($args);
  59. my @slave_steps;
  60. STEP: foreach my $master_step (@{ $self->{steps} }) {
  61. my $accept = 1; # accept by default
  62. my $slave_step = {
  63. line => $master_step->{line},
  64. warnings => []
  65. };
  66. # test against each condition from the pre-parsed condblock
  67. # if any number of condition warnings is found, accept
  68. # otherwise accept step unless a condition failed
  69. COND: foreach my $condstr (@{ $master_step->{conds} }) {
  70. my $c = SugarTrail::Template::Condition->new($condstr);
  71. my ($r, $w) = $c->match($args);
  72. # if warnings, store them, prepare to accept and ignore result
  73. if (scalar @$w) {
  74. $accept = 1;
  75. push @{$slave_step->{warnings}}, @$w;
  76. next COND;
  77. } else {
  78. # point of no warnings -- refusal should be valid here
  79. $accept = 0 unless $r;
  80. }
  81. }
  82. push @slave_steps, $slave_step
  83. if ($accept || scalar @{$slave_step->{warnings}});
  84. }
  85. $slave->{meta}->{'Stm'} = $self->{stm};
  86. $slave->{meta}->{'Stm Revision'} = $self->get_current_revision();
  87. $slave->{meta}->{'Params'} = mkmymime($args);
  88. $slave->{steps} = \@slave_steps;
  89. return $slave;
  90. }
  91. # skel: returns time (might be useful in absence of VCS)
  92. sub get_current_revision {
  93. return time;
  94. }
  95. sub mkmymime {
  96. my $d = shift;
  97. my $d1 = "; "; my $d2 = "=";
  98. my @t;
  99. foreach (sort keys %$d) {
  100. push @t, $_ . $d2 . $d->{$_};
  101. }
  102. return join $d1, @t;
  103. }
  104. 1;