All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tscfg.Main.scala Maven / Gradle / Ivy

package tscfg

import tscfg.generators.java.JavaGen
import tscfg.generators.scala.ScalaGen
import tscfg.generators.{GenOpts, Generator, TemplateGenerator, TemplateOpts}
import tscfg.ns.NamespaceMan

import java.io.{File, PrintWriter}
import java.util.Date
import scala.annotation.tailrec

/** The main program. Run with no arguments to see usage.
  */
object Main {
  val version: String = BuildInfo.version

  val defaultGenOpts: GenOpts = GenOpts(
    packageName = "tscfg.example",
    className = "ExampleCfg"
  )

  val defaultDestDir: String = {
    val tmp = new File("/tmp")
    if (tmp.isDirectory && tmp.canWrite) "/tmp"
    else System.getProperty("java.io.tmpdir")
  }

  var templateOpts: TemplateOpts = TemplateOpts()

  val usage: String =
    s"""
       |tscfg $version
       |Usage:  tscfg.Main --spec inputFile [options]
       |Options (default):
       |  --pn                                      (${defaultGenOpts.packageName})
       |  --cn                                        (${defaultGenOpts.className})
       |  --dd                                          ($defaultDestDir)
       |  --java                generate java code               (the default)
       |  --java:getters        generate getters (see #31)       (false)
       |  --java:records        generate records                 (false)
       |  --java:optionals      use optionals                    (false)
       |  --scala               generate scala code              (java)
       |  --scala:bt            use backticks (see #30)          (false)
       |  --durations           use java.time.Duration           (false)
       |  --all-required        assume all properties are required (see #47)
       |  --tpl       generate config template         (no default)
       |  --tpl.ind     template indentation string      ("${templateOpts.indent}")
       |  --tpl.cp      prefix for template comments     ("${templateOpts.commentPrefix}")
       |  --withoutTimestamp    generate header w/out timestamp  (false)
       |Output is written to $$destDir/$$className.ext
       |
       |More information at https://github.com/carueda/tscfg
    """.stripMargin

  case class CmdLineOpts(
      inputFilename: Option[String] = None,
      packageName: String = defaultGenOpts.packageName,
      className: String = defaultGenOpts.className,
      destDir: String = defaultDestDir,
      assumeAllRequired: Boolean = false,
      language: String = "java",
      useBackticks: Boolean = false,
      genGetters: Boolean = false,
      genRecords: Boolean = false,
      useOptionals: Boolean = false,
      useDurations: Boolean = false,
      tplFilename: Option[String] = None,
      generateWithoutTimestamp: Boolean = false
  )

  def main(args: Array[String]): Unit = {
    val argList = args.toList match {
      case "--log" :: rest =>
        tscfg.util.setLogMinLevel(
          // name = Some("tscfg.generators.scala.ScalaGen")
        )
        rest
      case list =>
        list
    }
    if (argList.isEmpty) {
      println(usage)
    }
    else {
      getOpts(argList) foreach generate
    }
  }

  private def getOpts(args: List[String]): Option[CmdLineOpts] = {
    @tailrec
    def traverseList(
        list: List[String],
        opts: CmdLineOpts = CmdLineOpts()
    ): Option[CmdLineOpts] = {
      def chkVal(v: String): Boolean = {
        if (!v.startsWith("-")) true
        else {
          println(s"""error: argument looks like a switch: $v""")
          false
        }
      }

      list match {
        case "--spec" :: filename :: rest =>
          if (chkVal(filename))
            traverseList(rest, opts.copy(inputFilename = Some(filename)))
          else None

        case "--pn" :: packageName :: rest =>
          if (chkVal(packageName))
            traverseList(rest, opts.copy(packageName = packageName))
          else None

        case "--cn" :: className :: rest =>
          if (chkVal(className))
            traverseList(rest, opts.copy(className = className))
          else None

        case "--dd" :: destDir :: rest =>
          if (chkVal(destDir))
            traverseList(rest, opts.copy(destDir = destDir))
          else None

        case "--all-required" :: rest =>
          traverseList(rest, opts.copy(assumeAllRequired = true))

        case "--scala" :: rest =>
          traverseList(rest, opts.copy(language = "scala"))

        case "--scala:bt" :: rest =>
          traverseList(rest, opts.copy(useBackticks = true))

        case "--java" :: rest =>
          traverseList(rest, opts.copy(language = "java"))

        case "--java:getters" :: rest =>
          traverseList(rest, opts.copy(genGetters = true))

        case "--java:records" :: rest =>
          traverseList(rest, opts.copy(genRecords = true))

        case "--java:optionals" :: rest =>
          traverseList(rest, opts.copy(useOptionals = true))

        case "--durations" :: rest =>
          traverseList(rest, opts.copy(useDurations = true))

        case "--tpl" :: filename :: rest =>
          if (chkVal(filename))
            traverseList(rest, opts.copy(tplFilename = Some(filename)))
          else None

        case "--tpl.ind" :: indent :: rest =>
          templateOpts = templateOpts.copy(indent = indent)
          traverseList(rest, opts)

        case "--tpl.cp" :: prefixComment :: rest =>
          templateOpts = templateOpts.copy(commentPrefix = prefixComment)
          traverseList(rest, opts)

        case "--scala:fp" :: rest =>
          println("ignoring obsolete option: --scala:fp")
          traverseList(rest, opts)

        case "--withoutTimestamp" :: rest =>
          traverseList(rest, opts.copy(generateWithoutTimestamp = true))

        case opt :: _ =>
          println(s"""missing argument or unknown option: $opt""")
          None

        case Nil => Some(opts)
      }
    }

    traverseList(args) match {
      case Some(opts) =>
        if (opts.inputFilename.isEmpty) {
          println("--spec not given")
          None
        }
        else Some(opts)
      case None => None
    }
  }

  private def generate(opts: CmdLineOpts): Unit = {
    require(opts.inputFilename.isDefined)

    val ext = opts.language

    val inputFilename = opts.inputFilename.get
    val destFilename  = s"${opts.destDir}/${opts.className}.$ext"
    val destFile      = new File(destFilename)
    val out           = new PrintWriter(destFile)

    val genOpts = GenOpts(
      opts.packageName,
      opts.className,
      assumeAllRequired = opts.assumeAllRequired,
      useBackticks = opts.useBackticks,
      genGetters = opts.genGetters,
      genRecords = opts.genRecords,
      useOptionals = opts.useOptionals,
      useDurations = opts.useDurations
    )

    println(s"parsing: $inputFilename")
    val source    = io.Source.fromFile(new File(inputFilename))
    val sourceStr = source.mkString.trim

    source.close()

    val rootNamespace = new NamespaceMan

    val buildResult =
      ModelBuilder(
        rootNamespace,
        sourceStr,
        assumeAllRequired = opts.assumeAllRequired
      )
    val objectType = buildResult.objectType

    // println("\nobjectType:\n  |" + objectType.format().replaceAll("\n", "\n  |"))

    if (buildResult.warnings.nonEmpty) {
      println("WARNINGS:")
      buildResult.warnings.foreach(w =>
        println(s"   line ${w.line}: ${w.source}: ${w.message}")
      )
    }

    println(s"generating: $destFile")
    val generator: Generator = opts.language match {
      case "java"  => new JavaGen(genOpts, rootNamespace)
      case "scala" => new ScalaGen(genOpts, rootNamespace)
    }
    val results = generator.generate(objectType)

    val generateWithTimeStamp = !opts.generateWithoutTimestamp

    val tsStr = if (generateWithTimeStamp) s" on ${new Date()}" else ""
    out.println(
      s"""// generated by tscfg $version$tsStr
         |// source: $inputFilename
         |
         |${results.code}""".stripMargin
    )

    out.close()

    opts.tplFilename foreach { filename =>
      println(s"generating template $filename")
      val destFile  = new File(filename)
      val out       = new PrintWriter(destFile)
      val templater = new TemplateGenerator(templateOpts)
      val template  = templater.generate(objectType)
      out.println(template)
      out.close()
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy