001package com.github.sormuras.bach;
002
003import com.github.sormuras.bach.internal.VersionSupport;
004import java.io.PrintWriter;
005import java.io.Writer;
006import java.lang.module.ModuleDescriptor.Version;
007import java.nio.file.Path;
008import java.util.Locale;
009import java.util.Optional;
010
011/** Global settings with nested topic-specific configurations. */
012public record Configuration(
013    boolean verbose,
014    boolean lenient,
015    int timeout,
016    Pathing pathing,
017    Printing printing,
018    Tooling tooling,
019    Options.ProjectOptions projectOptions) {
020
021  public static final String //
022      EXTERNAL_MODULES_DIRECTORY = ".bach/external-modules",
023      EXTERNAL_TOOL_LAYERS_DIRECTORY = ".bach/external-tool-layers",
024      EXTERNAL_TOOL_PROGRAMS_DIRECTORY = ".bach/external-tool-programs",
025      EXTERNAL_TOOL_PROGRAM_ARGSFILE = "java.args",
026      LOGBOOK_ARCHIVE_FILE = "logbooks/logbook-{TIMESTAMP}.md",
027      LOGBOOK_MARKDOWN_FILE = "logbook.md",
028      TIMESTAMP_PATTERN = "yyyyMMdd-HHmmss",
029      WORKSPACE_DIRECTORY = ".bach/workspace";
030
031  public static Path computeJavaExecutablePath(String name) {
032    var windows = System.getProperty("os.name").toLowerCase(Locale.ROOT).startsWith("win");
033    return Path.of(System.getProperty("java.home"), "bin", name + (windows ? ".exe" : ""));
034  }
035
036  public static String computeJarFileName(String module, Version version) {
037    return module + "@" + VersionSupport.toNumberAndPreRelease(version) + ".jar";
038  }
039
040  public static Configuration of() {
041    var pathing = Pathing.ofCurrentWorkingDirectory();
042    var printing =
043        new Printing(new PrintWriter(System.out, true), new PrintWriter(System.err, true));
044    return Configuration.of(pathing, printing);
045  }
046
047  public static Configuration of(Pathing pathing, Printing printing) {
048    var tooling =
049        new Tooling(
050            ToolFinder.compose(
051                ToolFinder.ofSystem(),
052                ToolFinder.ofBach(),
053                ToolFinder.ofLayers(pathing.externalToolLayers()),
054                ToolFinder.ofPrograms(
055                    pathing.externalToolPrograms(),
056                    pathing.javaExecutable(),
057                    EXTERNAL_TOOL_PROGRAM_ARGSFILE)));
058    return new Configuration(
059        false,
060        false,
061        9,
062        pathing,
063        printing,
064        tooling,
065        new Options.ProjectOptions(Optional.empty(), Optional.empty()));
066  }
067
068  /** {@link Path}-related settings. */
069  public record Pathing(
070      Path root,
071      Path externalModules,
072      Path externalToolLayers,
073      Path externalToolPrograms,
074      Path workspace,
075      Path javaExecutable) {
076
077    public static Pathing of(Path root) {
078      return new Pathing(
079          root,
080          root.resolve(EXTERNAL_MODULES_DIRECTORY),
081          root.resolve(EXTERNAL_TOOL_LAYERS_DIRECTORY),
082          root.resolve(EXTERNAL_TOOL_PROGRAMS_DIRECTORY),
083          root.resolve(WORKSPACE_DIRECTORY),
084          computeJavaExecutablePath("java"));
085    }
086
087    public static Pathing ofCurrentWorkingDirectory() {
088      return Pathing.of(Path.of(""));
089    }
090
091    public Path root(String first, String... more) {
092      return root.resolve(Path.of(first, more));
093    }
094
095    public Path workspace(String first, String... more) {
096      return workspace.resolve(Path.of(first, more));
097    }
098  }
099
100  /** Print-related settings and common {@link PrintWriter} objects. */
101  public record Printing(PrintWriter out, PrintWriter err) {
102    public static Printing ofErrorsOnly() {
103      return new Printing(new PrintWriter(Writer.nullWriter()), new PrintWriter(System.err, true));
104    }
105  }
106
107  /** {@link ToolFinder}-related and {@link java.util.spi.ToolProvider}-related settings. */
108  public record Tooling(ToolFinder finder) {}
109
110  public Configuration with(Options options) {
111    return new Configuration(
112        options.forConfiguration().verbose().orElse(verbose),
113        options.forConfiguration().lenient().orElse(lenient),
114        options.forConfiguration().timeout().orElse(timeout),
115        pathing,
116        printing,
117        tooling,
118        options.forProject());
119  }
120}