From 6f1b026dd57026966b9e48de0e31d1db6986042f Mon Sep 17 00:00:00 2001
From: Johan Maasing <johan@zoom.nu>
Date: Sun, 13 Apr 2025 14:11:03 +0200
Subject: [PATCH] Output files are named as the templates with the ending
 stripped.

---
 README.md                                     | 37 +++++++------------
 .../{Codecs.ftl => Codecs.scala.ftl}          |  0
 .../{Endpoints.ftl => Endpoints.scala.ftl}    |  0
 .../{Protocol.ftl => Protocol.scala.ftl}      |  0
 .../{endpoints-list.ftl => endpoints.txt.ftl} |  0
 .../nu/zoom/dsl/freemarker/Generator.java     | 27 ++++++++------
 6 files changed, 30 insertions(+), 34 deletions(-)
 rename endpoints-templates/{Codecs.ftl => Codecs.scala.ftl} (100%)
 rename endpoints-templates/{Endpoints.ftl => Endpoints.scala.ftl} (100%)
 rename endpoints-templates/{Protocol.ftl => Protocol.scala.ftl} (100%)
 rename endpoints-templates/{endpoints-list.ftl => endpoints.txt.ftl} (100%)

diff --git a/README.md b/README.md
index 2eb6a32..8cf8fcb 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,6 @@ This is a converter tool that reads a DSL and generates output files.
 Endgen is Open Source Software using the [Apache Software License v2.0](http://www.apache.org/licenses/LICENSE-2.0)
 
 ## Motivation
-
 The motivation behind this tool is that I wanted to generate boilerplate code for handling HTTP Endpoints (hence the 
 endgen name).
 
@@ -23,25 +22,24 @@ parser and a code generator using [freemarker](https://freemarker.apache.org).
                                  | Endgen |
                   -------------------------------------------------
 -----------       ||--------|      |-----------|     |------------||
-| endpoint |      || Parser |      | In-Memory |     | Freemarker ||     ---------------
-| file     | -->  ||        | -->  | AST       | --> | engine     || --> | Output file |
-\__________\      ||--------|      |-----------|     |------------||     \_____________\
-                  ------------------------------------------------- -
+| endpoint |      || Parser |      | In-Memory |     | Freemarker ||     ------------------
+| file     | -->  ||        | -->  | AST       | --> | engine     || --> | Output file    |
+\__________\      ||--------|      |-----------|     |------------||     | mytemplate.xxx |
+                  --------------------------------------------------     \________________\
                                                            ^
                                                            |
-                                                    ----------------
-                                                    | Template.ftl |
-                                                    \_______________\
+                                                    ----------------------
+                                                    | mytemplate.xxx.ftl |
+                                                    \____________________\
 ``` 
 ## How to Run
-
 You need a Java 24 runtime and java in the path. A very convenient way to install a java runtime is [SdkMan](https://sdkman.io).
 
 Unpack the archive, run the provided shellscript file.
 
 ### Usage
 ```
-Usage: EndpointsCLI [-hvV] [-o=<outputDir>] [-t=<templateDir>] <file>
+Usage: run.sh [-hvV] [-o=<outputDir>] [-t=<templateDir>] <file>
 Generate source code from an endpoints specification file.
       <file>                 The source endpoints DSL file.
   -h, --help                 Show this help message and exit.
@@ -55,28 +53,28 @@ Generate source code from an endpoints specification file.
 ```
 
 ## DSL example
-
 In the simplest form the DSL looks like this
 ```
 /some/endpoint <- SomeType(foo:String)
 ```
 
-This gets parsed into an [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) which is this case holds a list of 
+This gets parsed into an [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) which in this case holds a list of 
 Path segments and a data strucutre representing the input/body type.
 
 ## Code generation example
-
 When the parser is done reading the DSL it will look in a directory for [freemarker](https://freemarker.apache.org)
 templates. For each template it finds it sends in the AST. The resulting file (per template) is written to an
 output directory.
 
+The templates must have the file ending `.ftl` - this ending is stripped when generating the output file. So a template 
+called `types.java.ftl` will generate a file called `types.java`.
+
 The idea being that you can take these files and probably adapt them before checking them into your project. Endgen
 does not aim to be a roundtrip tool (i.e. reading the generated source, or being smart in updating them etc). It is also
 a very limited DSL, you can for example not express what type of HTTP Verb to use or declare response codes. There are
 no plans to extend the DSL to do that either.
 
 ## DSL 
-
 This is the ANTLR grammar for the root of the DSL
 
 ```antlrv4
@@ -89,7 +87,7 @@ Here is an example:
 ```
 {
     package: se.rutdev.senash,
-    ending: .scala
+    mykey: myvalue
 }
 
 /some/endpoint <- SomeType(foo:String)
@@ -98,7 +96,7 @@ Embedded(foo:Bar)
 /some/other/endpoint <- (bar:Seq[Embedded])
 ```
 
-This consists of a config block with 2 items, the 'package' and the 'ending' deinfition. These are available to be used
+This consists of a config block with 2 items, the 'package' and the 'mykey' definition. These are available to be used
 in the freemarker template as a Map of String-keys to String-values.
 
 `/some/endpoint <- SomeType(foo:String)` is an endpoint declaration. It declares one endpoint that have a request body 
@@ -113,7 +111,6 @@ just named field-name and the other string is named field-type.
 to a specific endpoint.
 
 ### Automatically named data types
-
 `/some/other/endpoint <- (bar:Seq[Embedded])` is another endpoint declaration. However this time the request body is
 not named in the DSL. But all datatypes must have a name so it will simply name it after the last path segment and
 tack on the string 'Request' at the end. So the AST till contain a datatype named `endpointRequest` with a field named
@@ -126,7 +123,6 @@ decide to generate in the templates.
 The only 'semantic' validation the parser performs is to check that not two types have the same name.
 
 ### Reponse data types
-
 It is possible to have an optional response data type declared like so:
 
 `/some/other/endpoint <- (bar:Seq[Embedded]) -> ResponseType(foo: Bar)`
@@ -135,12 +131,10 @@ The right pointing arrow `->` denotes a response type, it can be an anonymous da
 name it from the last path segment and add 'Response' to the end of the data type name.
 
 ### DSL config
-
 The only key in the config block the generator looks at is called `ending`, this will be used as the file ending for
 the resulting file of applying the freemarker template.
 
 ## Generating
-
 If the parser is successful it will hold the following data in the AST
 
 ```java
@@ -162,14 +156,12 @@ This will be passed to the freemarker engine as the 'root' data object, meaning
 That is, you can directly reference `typeDefinitions`, `endpoints` or `config`.
 
 ### Config
-
 The config object is simply a String-map with the keys and values unfiltered from the input file. Here is an example
 that writes the value for a config key called 'package'.
 
 `package ${config.package}`
 
 ### Data types
-
 These are all the data types the parser have collected, either from explicit declarations, request payloads and response 
 bodies. 
 
@@ -191,7 +183,6 @@ object Protocol:
 ```
 
 ### Endpoints
-
 The parser will collect the following data for endpoint declarations
 
 ```java
diff --git a/endpoints-templates/Codecs.ftl b/endpoints-templates/Codecs.scala.ftl
similarity index 100%
rename from endpoints-templates/Codecs.ftl
rename to endpoints-templates/Codecs.scala.ftl
diff --git a/endpoints-templates/Endpoints.ftl b/endpoints-templates/Endpoints.scala.ftl
similarity index 100%
rename from endpoints-templates/Endpoints.ftl
rename to endpoints-templates/Endpoints.scala.ftl
diff --git a/endpoints-templates/Protocol.ftl b/endpoints-templates/Protocol.scala.ftl
similarity index 100%
rename from endpoints-templates/Protocol.ftl
rename to endpoints-templates/Protocol.scala.ftl
diff --git a/endpoints-templates/endpoints-list.ftl b/endpoints-templates/endpoints.txt.ftl
similarity index 100%
rename from endpoints-templates/endpoints-list.ftl
rename to endpoints-templates/endpoints.txt.ftl
diff --git a/parser/src/main/java/nu/zoom/dsl/freemarker/Generator.java b/parser/src/main/java/nu/zoom/dsl/freemarker/Generator.java
index bb1a29a..dcfa86a 100644
--- a/parser/src/main/java/nu/zoom/dsl/freemarker/Generator.java
+++ b/parser/src/main/java/nu/zoom/dsl/freemarker/Generator.java
@@ -29,10 +29,12 @@ import java.util.Objects;
 import java.util.stream.Stream;
 
 public class Generator {
-    private final Path templatesDir ;
+    private final Path templatesDir;
     private final DocumentNode data;
-    private final Path outputDir ;
+    private final Path outputDir;
     private final Configuration cfg;
+    private final String TEMPLATE_EXTENSION = ".ftl";
+    private final int TEMPLATE_EXTENSION_LENGTH = TEMPLATE_EXTENSION.length();
 
     public Generator(Path templatesDir, DocumentNode data, Path outputDir) throws IOException {
         this.templatesDir = Objects.requireNonNull(templatesDir);
@@ -49,23 +51,26 @@ public class Generator {
 
     public List<Path> generate() throws IOException, TemplateException {
         try (Stream<Path> files = Files.list(templatesDir)) {
-            List<Path> templates = files.filter(p -> p.toString().endsWith(".ftl")).toList() ;
+            List<String> templates = files
+                    .map(Path::getFileName)
+                    .map(Path::toString)
+                    .filter(p -> p.length() > TEMPLATE_EXTENSION_LENGTH && p.endsWith(TEMPLATE_EXTENSION)
+                    )
+                    .toList();
             ArrayList<Path> out = new ArrayList<>();
-            for (Path template : templates) {
-                String configEnding = this.data.config().get("ending") ;
-                String ending = configEnding != null ? configEnding.trim() : "";
-                Path outpath = outputDir.resolve(outputFilenameFromTemplate(template.getFileName(), ending));
-                Template ftl = this.cfg.getTemplate(template.getFileName().toString()) ;
+            for (String template : templates) {
+                Path outpath = outputDir.resolve(outputFilenameFromTemplate(template));
+                Template ftl = this.cfg.getTemplate(template);
                 try (var outw = Files.newBufferedWriter(outpath, StandardCharsets.UTF_8)) {
                     ftl.process(this.data, outw);
                     out.add(outpath);
                 }
             }
-            return out ;
+            return out;
         }
     }
 
-    private String outputFilenameFromTemplate(Path template, String ending) {
-        return template.getFileName().toString().replace(".ftl", ending);
+    private String outputFilenameFromTemplate(String template) {
+        return template.substring(0, template.length() - TEMPLATE_EXTENSION_LENGTH);
     }
 }