123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- program mustache;
- {$mode objfpc}{$H+}
- uses
- {$IFDEF UNIX}
- cthreads,
- {$ENDIF}
- Classes, SysUtils, CustApp, strutils, fpjson, jsonparser, csvdataset, fpMustache, fpexmustache, fpdbmustache, iostream;
- type
- { TMustacheApplication }
- TMustacheApplication = class(TCustomApplication)
- private
- FTemplate : TMustacheString;
- FJSON : TJSONStringType;
- FCSV: TCSVDataset;
- FPartials,
- FDefines : TStrings;
- FAllowExpressions : Boolean;
- Foutput,
- FSection,
- FRootPath : String;
- procedure DoGetDefine(const aName: TMustacheString; var aHandled: Boolean;
- var aValue: TMustacheString);
- procedure ProcessOptions;
- Procedure Createoutput;
- procedure Usage(ErrorMsg: String);
- protected
- procedure DoRun; override;
- public
- constructor Create(TheOwner: TComponent); override;
- destructor Destroy; override;
- end;
- { TMustacheApplication }
- procedure TMustacheApplication.Usage(ErrorMsg : String);
- begin
- If ErrorMsg<>'' then
- Writeln('Error : ',ErrorMsg);
- Writeln('Usage : mustache [options]');
- Writeln('Where options is one or more of:');
- writeln('-c --csv=FILE Use a CSV file as data source. First line must contain column names.');
- writeln('-d --define=name=value Define fixed value.');
- writeln('-e --expressions Allow expressions.');
- writeln('-h --help This message.');
- writeln('-j --json=JSON Use JSON as data source. @FILENAME will read JSON from file (UTF8).');
- writeln('-o --output=FILE output file to write output to. If empty, stdout is assumed.');
- writeln('-p --partial=name=PARTIAL Register partial. @FILENAME reads partial from file.');
- writeln('-r --root=PATH Register variables at root path PATH for expression engine');
- writeln('-s --section=SECTIOn Section name for CSV data');
- writeln('-t --template=TEMPLATE Use TEMPLATE as data source. @FILENAME will read template from file (UTF8). Required.');
- Halt(Ord(ErrorMsg<>''));
- end;
- procedure TMustacheApplication.ProcessOptions;
- Function StringOrFile(S : String) : UTF8String;
- begin
- if Copy(S,1,1)<>'@' then
- Result:=S
- else
- With TFileStream.Create(Copy(S,2,Length(S)-1),fmOpenRead or fmShareDenyNone) do
- try
- SetLength(Result,Size);
- ReadBuffer(Result[1],Size);
- finally
- Free;
- end;
- end;
- Var
- S : String;
- begin
- if Not HasOption('t','template') then
- Raise Exception.Create('Need a template');
- if HasOption('c','csv') and HasOption('j','json') then
- Raise Exception.Create('Cannot specify both JSON or CSV');
- FTemplate:=StringOrFile(GetOptionValue('t','template'));
- if HasOption('j','json') then
- FJSON:=StringOrFile(GetOptionValue('j','json'))
- else if HasOption('c','csv') then
- begin
- FCSV:=TCSVDataset.Create(Self);
- FCSV.FileName:=GetOptionValue('c','csv');
- FCSV.CSVOptions.FirstLineAsFieldNames:=True;
- FCSV.Open;
- end;
- for S in GetOptionValues('d','define') do
- FDefines.Add(S);
- for S in GetOptionValues('p','partial') do
- FPartials.Add(ExtractWord(1,S,['='])+'='+StringOrFile(ExtractWord(2,S,['='])));
- FAllowExpressions:=HasOption('e','expressions');
- FRootPath:=GetOptionValue('r','root');
- FSection:=GetOptionValue('s','section');
- if FSection='' then
- FSection:='data';
- Foutput:=GetOptionValue('o','output');
- end;
- procedure TMustacheApplication.DoGetDefine(const aName: TMustacheString;
- var aHandled: Boolean; var aValue: TMustacheString);
- Var
- Idx : Integer;
- begin
- Writeln('Getting define ',aName);
- Idx:=FDefines.IndexOfName(aName);
- aHandled:=Idx<>-1;
- if aHandled then
- aValue:=FDefines.ValueFromIndex[Idx]
- else
- aValue:='';
- end;
- procedure TMustacheApplication.DoRun;
- var
- ErrorMsg: String;
- begin
- Terminate;
- // quick check parameters
- ErrorMsg:=CheckOptions('het:j:c:d:o:r:', ['help','template','json','csv','define','output','expressions','root']);
- if (ErrorMsg<>'') or HasOption('h','help') then
- Usage(ErrorMsg);
- ProcessOptions;
- CreateOutput;
- end;
- procedure TMustacheApplication.CreateOutput;
- Var
- M : TMustache;
- C : TMustacheContext;
- O : TStream;
- S : TMustacheString;
- begin
- O:=Nil;
- M:=Nil;
- C:=Nil;
- try
- if FAllowExpressions then
- M:=TMustache.Create(Self)
- else
- begin
- M:=TMustacheExpr.Create(Self);
- if (FRootPath<>'') and (FJSON<>'') then
- TMustacheExpr(M).RegisterVariables(FJSON,FRootPath,True);
- end;
- M.Partials:=FPartials;
- if Assigned(FCSV) then
- begin
- C:=TMustacheDBContext.Create(@DoGetDefine);
- TMustacheDBContext(C).AddDataset(FCSV,FSection);
- end
- else if (FJSON<>'') then
- C:=TMustacheJSONContext.Create(GetJSON(FJSON),@DoGetDefine)
- else
- C:=TMustacheContext.Create(@DoGetDefine);
- if Foutput<>'' then
- O:=TFileStream.Create(Foutput,fmCreate)
- else
- O:=TIOStream.Create(iosOutput);
- M.Template:=FTemplate;
- S:=M.Render(C);
- O.WriteBuffer(S[1],Length(S));
- finally
- O.Free;
- C.Free;
- M.Free;
- end;
- end;
- constructor TMustacheApplication.Create(TheOwner: TComponent);
- begin
- inherited Create(TheOwner);
- FPartials:=TStringList.Create;
- FDefines:=TStringList.Create;
- StopOnException:=True;
- end;
- destructor TMustacheApplication.Destroy;
- begin
- FreeAndNil(FPartials);
- FreeAndNil(FDefines);
- FreeAndNil(FCSV);
- inherited Destroy;
- end;
- var
- Application: TMustacheApplication;
- begin
- Application:=TMustacheApplication.Create(nil);
- Application.Title:='Mustache Templater';
- Application.Run;
- Application.Free;
- end.
|