diff --git a/src/main/java/eu/m724/blog/BlogBuilder.java b/src/main/java/eu/m724/blog/BlogBuilder.java new file mode 100644 index 0000000..32dba3b --- /dev/null +++ b/src/main/java/eu/m724/blog/BlogBuilder.java @@ -0,0 +1,162 @@ +package eu.m724.blog; + +import eu.m724.blog.data.Feed; +import eu.m724.blog.data.Post; +import eu.m724.blog.data.Site; +import org.apache.commons.io.file.PathUtils; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.lib.RepositoryBuilder; + +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.stream.Collectors; + +public class BlogBuilder { + private final Git git; + private final Path workingDirectory; + + private Site site; + private TemplateRenderer template; + + private Path templateDirectory; + private Path outputDirectory; + private boolean renderDrafts = false; + + public BlogBuilder(Git git) { + this.git = git; + + this.workingDirectory = git.getRepository().getDirectory().toPath().getParent(); + this.templateDirectory = workingDirectory.resolve("template"); + this.outputDirectory = workingDirectory.resolve("generated_out"); + } + + public static BlogBuilder fromPath(Path workingDirectory) throws IOException { + var repository = new RepositoryBuilder() + .setGitDir(workingDirectory.resolve(".git").toFile()) + .build(); + var git = new Git(repository); + + // + + return new BlogBuilder(git); + } + + public BlogBuilder templateDirectory(Path templateDirectory) { + this.templateDirectory = templateDirectory; + return this; + } + + public BlogBuilder outputDirectory(Path outputDirectory) { + this.outputDirectory = outputDirectory; + return this; + } + + public BlogBuilder renderDrafts(boolean renderDrafts) { + this.renderDrafts = renderDrafts; + return this; + } + + public void mkdirs(boolean force) throws IOException { + if (outputDirectory.toFile().exists()) { + if (force) { + PathUtils.deleteDirectory(outputDirectory); + } else { + throw new FileAlreadyExistsException(outputDirectory.toString(), null, "Output directory already exists. --force?"); + } + } + + Files.createDirectory(outputDirectory); + } + + public void build() throws IOException { + if (site == null) + this.site = Site.fromConfig(workingDirectory.resolve("site-config.json")); + + if (template == null) + this.template = new TemplateRenderer(templateDirectory); + + copyIfExists(workingDirectory.resolve("assets"), outputDirectory.resolve("assets")); + copyIfExists(templateDirectory.resolve("static"), outputDirectory.resolve("static")); + + Files.createDirectory(outputDirectory.resolve("post")); + var postDirectory = workingDirectory.resolve("posts"); + var posts = new ArrayList(); + + try (var stream = Files.walk(postDirectory)) { + for (var path : stream.collect(Collectors.toSet())) { + if (!Files.isRegularFile(path)) + continue; // directory is created below + + if (!path.toString().endsWith(".html")) { + System.out.println("Post " + path.getFileName() + ": unsupported file type"); + continue; + } + + path = postDirectory.relativize(path); + var post = Post.fromFile(git, path); + + if (post.draft() && !renderDrafts) { + System.out.println("Post " + path.getFileName() + ": draft, ignoring"); + continue; + } + + var render = template.renderPost(site, post); + var outFile = outputDirectory.resolve("post").resolve(path); + + try { + Files.createDirectory(outFile.getParent()); + } catch (FileAlreadyExistsException ignored) { } + + Files.writeString(outFile, render); + posts.add(post); + } + } + + posts.sort(Comparator.comparing(Post::createdAt).reversed()); + Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(site, posts)); + + Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts)); + } + + + /* Internal functions */ + + private boolean copyTree(Path srcDir, Path destDir) throws IOException { + if (!Files.isDirectory(srcDir)) { + return false; + } + + try (var walk = Files.walk(srcDir)) { + for (var src : walk.collect(Collectors.toSet())) { + var rel = srcDir.relativize(src); + var dest = destDir.resolve(rel); + + if (Files.isRegularFile(src)) { + var parent = dest.getParent(); + + if (!Files.isDirectory(parent)) { + Files.createDirectories(parent); + } + + Files.copy(src, dest); + } + } + } + + return true; + } + + private void copyIfExists(Path srcDir, Path destDir) throws IOException { + System.out.print(srcDir); + + if (copyTree(srcDir, destDir)) { + System.out.println(" copied"); + } else { + System.out.println(" doesn't exist, not copying"); + } + } +} diff --git a/src/main/java/eu/m724/blog/Main.java b/src/main/java/eu/m724/blog/Main.java index fcea476..a5b1ed1 100644 --- a/src/main/java/eu/m724/blog/Main.java +++ b/src/main/java/eu/m724/blog/Main.java @@ -1,25 +1,59 @@ package eu.m724.blog; -import eu.m724.blog.data.Feed; -import eu.m724.blog.data.Post; -import eu.m724.blog.data.Site; import org.apache.commons.cli.*; -import org.apache.commons.io.file.PathUtils; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.lib.RepositoryBuilder; import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.stream.Collectors; public class Main { public static void main(String[] args) throws IOException { System.out.println("Hello world!"); + var commandLine = getCommandLine(args); + + if (commandLine == null) + System.exit(2); + + args = commandLine.getArgs(); + + var workingDirectory = Path.of(args[0]); + var templateDirectory = Path.of(commandLine.getOptionValue("output-dir", workingDirectory.resolve("template").toString())); + var outputDirectory = Path.of(commandLine.getOptionValue("output-dir", workingDirectory.resolve("generated_out").toString())); + var force = commandLine.hasOption("force"); + + var startServer = commandLine.hasOption("server"); + var openBrowser = !commandLine.hasOption("no-browser"); + var renderDrafts = commandLine.hasOption("draft") || startServer; + + + /* Build process */ + + var start = System.nanoTime(); + + var builder = BlogBuilder.fromPath(workingDirectory) + .templateDirectory(templateDirectory) + .outputDirectory(outputDirectory) + .renderDrafts(renderDrafts); + + builder.mkdirs(force); + builder.build(); + + var end = System.nanoTime(); + System.out.printf("Exported to %s (%.2f ms)\n", outputDirectory, (end - start) / 1000000.0); + + /* Server process */ + + if (startServer) { + var server = new Server(outputDirectory); + server.start(); + + if (openBrowser) { + server.openBrowser(); + } + } + } + + private static CommandLine getCommandLine(String[] args) { var options = new Options() .addOption("h", "help", false, "Show help") .addOption("f", "force", false, "Overwrite current build") @@ -30,7 +64,6 @@ public class Main { .addOption("d", "draft", false, "Render drafts. Default: only with server"); CommandLine commandLine; - String wdStr; try { commandLine = new DefaultParser().parse(options, args); @@ -40,145 +73,13 @@ public class Main { if (commandLine.getArgs().length == 0) throw new ParseException("Missing required argument: WORKDIR"); - wdStr = commandLine.getArgs()[0]; } catch (ParseException e) { System.out.println(e.getMessage()); new HelpFormatter().printHelp("blog-software-java [OPTION]... [WORKDIR]", options); - System.exit(1); - return; + return null; } - - var workingDirectory = Path.of(wdStr); - var templateDirectory = Path.of(commandLine.getOptionValue("output-dir", workingDirectory.resolve("template").toString())); - var outputDirectory = Path.of(commandLine.getOptionValue("output-dir", workingDirectory.resolve("generated_out").toString())); - var force = commandLine.hasOption("force"); - - var server = commandLine.hasOption("server"); - var openBrowser = !commandLine.hasOption("no-browser"); - var renderDrafts = commandLine.hasOption("draft") || server; - - // - - var start = System.nanoTime(); - - var repository = new RepositoryBuilder() - .setGitDir(workingDirectory.resolve(".git").toFile()) - .build(); - var git = new Git(repository); - - // - - if (outputDirectory.toFile().exists()) { - if (force) { - PathUtils.deleteDirectory(outputDirectory); - } else { - throw new FileAlreadyExistsException(outputDirectory.toString(), null, "Output directory already exists. --force?"); - } - } - - var site = Site.fromConfig(git); - var template = new TemplateRenderer(templateDirectory); - - Files.createDirectory(outputDirectory); - - copyIfExists(workingDirectory.resolve("assets"), outputDirectory.resolve("assets")); - copyIfExists(templateDirectory.resolve("static"), outputDirectory.resolve("static")); - - Files.createDirectory(outputDirectory.resolve("post")); - var postDirectory = workingDirectory.resolve("posts"); - var posts = new ArrayList(); - - try (var stream = Files.walk(postDirectory)) { - for (var path : stream.collect(Collectors.toSet())) { - if (!Files.isRegularFile(path)) - continue; // directory is created below - - if (!path.toString().endsWith(".html")) { - System.out.println("Post " + path.getFileName() + ": unsupported file type"); - continue; - } - - path = postDirectory.relativize(path); - var post = Post.fromFile(git, path); - - if (post.draft() && !renderDrafts) { - System.out.println("Post " + path.getFileName() + ": draft, ignoring"); - continue; - } - - var render = template.renderPost(site, post); - var outFile = outputDirectory.resolve("post").resolve(path); - - try { - Files.createDirectory(outFile.getParent()); - } catch (FileAlreadyExistsException ignored) { } - - Files.writeString(outFile, render); - posts.add(post); - } - } - - posts.sort(Comparator.comparing(Post::createdAt).reversed()); - Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(site, posts)); - - Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts)); - - - var end = System.nanoTime(); - System.out.printf("Exported to %s (%.2f ms)\n", outputDirectory, (end - start) / 1000000.0); - - if (server) { - var listenAddress = new Server(outputDirectory).start(); - if (openBrowser) { - try { - var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() }); - var code = process.waitFor(); - if (code != 0) { - throw new Exception("Exit code " + code); - } - System.out.println("Opened browser"); - } catch (Exception e) { - System.out.println("Failed to open browser: " + e); - } - } - } - } - - private static boolean copyTree(Path srcDir, Path destDir) throws IOException { - if (!Files.isDirectory(srcDir)) { - return false; - } - - try (var walk = Files.walk(srcDir)) { - for (var src : walk.collect(Collectors.toSet())) { - var rel = srcDir.relativize(src); - var dest = destDir.resolve(rel); - - if (Files.isRegularFile(src)) { - var parent = dest.getParent(); - - if (!Files.isDirectory(parent)) { - Files.createDirectories(parent); - } - - Files.copy(src, dest); - } - } - } - - return true; - } - - - private static void copyIfExists(Path srcDir, Path destDir) throws IOException { - System.out.print(srcDir); - - if (copyTree(srcDir, destDir)) { - System.out.println(" copied"); - } else { - System.out.println(" doesn't exist, not copying"); - } + return commandLine; } } \ No newline at end of file diff --git a/src/main/java/eu/m724/blog/Server.java b/src/main/java/eu/m724/blog/Server.java index 11eeeec..1a0c7f0 100644 --- a/src/main/java/eu/m724/blog/Server.java +++ b/src/main/java/eu/m724/blog/Server.java @@ -11,26 +11,39 @@ import java.nio.file.Files; import java.nio.file.Path; public class Server implements HttpHandler { - private final InetSocketAddress listenAddress; private final Path webroot; + private InetSocketAddress listenAddress; - public Server(InetSocketAddress listenAddress, Path webroot) { - this.listenAddress = listenAddress; + public Server(Path webroot, InetSocketAddress listenAddress) { this.webroot = webroot; + this.listenAddress = listenAddress; } public Server(Path webroot) { - this(new InetSocketAddress("localhost", 0), webroot); + this(webroot, new InetSocketAddress("localhost", 0)); } - public InetSocketAddress start() throws IOException { + public void start() throws IOException { var server = HttpServer.create(listenAddress, 0); server.createContext("/", this); server.start(); System.out.println("Server started on http:/" + server.getAddress()); - return server.getAddress(); + this.listenAddress = server.getAddress(); + } + + public void openBrowser() { + try { + var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() }); + var code = process.waitFor(); + if (code != 0) { + throw new Exception("Exit code " + code); + } + System.out.println("Opened browser"); + } catch (Exception e) { + System.out.println("Failed to open browser: " + e); + } } @Override diff --git a/src/main/java/eu/m724/blog/data/Site.java b/src/main/java/eu/m724/blog/data/Site.java index 64292ce..f928667 100644 --- a/src/main/java/eu/m724/blog/data/Site.java +++ b/src/main/java/eu/m724/blog/data/Site.java @@ -1,10 +1,10 @@ package eu.m724.blog.data; -import org.eclipse.jgit.api.Git; import org.json.JSONObject; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; import java.util.Map; @@ -14,8 +14,8 @@ public record Site( Map custom ) { - public static Site fromConfig(Git git) throws IOException { - var content = Files.readString(git.getRepository().getDirectory().toPath().getParent().resolve("site-config.json")); + public static Site fromConfig(Path path) throws IOException { + var content = Files.readString(path); var json = new JSONObject(content); String name = null;