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

mdoc.internal.cli.Settings.scala Maven / Gradle / Ivy

The newest version!
package mdoc.internal.cli

import java.io.InputStream
import java.nio.charset.Charset
import java.nio.charset.IllegalCharsetNameException
import java.nio.charset.StandardCharsets
import java.nio.charset.UnsupportedCharsetException
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.InvalidPathException
import java.nio.file.PathMatcher

import metaconfig.Conf
import metaconfig.ConfDecoder
import metaconfig.ConfEncoder
import metaconfig.ConfError
import metaconfig.Configured
import metaconfig.annotation._
import metaconfig.generic
import metaconfig.generic.Surface
import org.typelevel.paiges.Doc

import scala.annotation.StaticAnnotation
import scala.meta.internal.io.PathIO
import scala.meta.io.AbsolutePath
import scala.meta.io.RelativePath
import mdoc.StringModifier
import mdoc.Variable
import mdoc.Reporter
import mdoc.internal.markdown.{ReplVariablePrinter, GitHubIdGenerator, MarkdownCompiler}

class Section(val name: String) extends StaticAnnotation

case class Settings(
    @Section("Common options")
    @Description(
      "The input directory containing markdown and other documentation sources. " +
        "Markdown files will be processed by mdoc while other files will be copied " +
        "verbatim to the output directory."
    )
    @ExtraName("i")
    in: AbsolutePath,
    @Description("The output directory to generate the mdoc site.")
    @ExtraName("o")
    out: AbsolutePath,
    @Description("Start a file watcher and incrementally re-generate the site on file save.")
    @ExtraName("w")
    watch: Boolean = false,
    @Description(
      "Instead of generating a new site, report an error if generating the site would produce a diff " +
        "against an existing site. Useful for asserting in CI that a site is up-to-date."
    )
    @ExtraName("test")
    check: Boolean = false,
    @Description("Include additional diagnostics for debuggin potential problems.")
    verbose: Boolean = false,
    @Description(
      "Classpath to use when compiling Scala code examples. " +
        "Defaults to the current thread's classpath."
    )
    classpath: String = "",
    @Description(
      "Key/value pairs of variables to replace through @VAR@. " +
        "For example, the flag '--site.VERSION 1.0.0' will replace appearances of '@VERSION@' in " +
        "markdown files with the string 1.0.0"
    )
    site: Map[String, String] = Map.empty,
    @Description("Remove all files in the outout directory before generating a new site.")
    cleanTarget: Boolean = false,
    @Section("Less common options")
    @Description("Print out a help message and exit")
    help: Boolean = false,
    @Description("Print out usage instructions and exit")
    usage: Boolean = false,
    @Description("Print out the version number and exit")
    version: Boolean = false,
    @Description(
      "Glob to filter which files to process. Defaults to all files. " +
        "Example: --include **/example.md will process only files with the name example.md."
    )
    @ExtraName("includePath")
    include: List[PathMatcher] = Nil,
    @Description(
      "Glob to filter which files from exclude from processing. Defaults to no files. " +
        "Example: --include users/**.md --exclude **/example.md will process all files in the users/ directory " +
        "excluding files named example.md."
    )
    @ExtraName("excludePath")
    exclude: List[PathMatcher] = Nil,
    @Description(
      "Use relative filenames when reporting error messages. " +
        "Useful for producing consistent docs on a local machine and CI. "
    )
    reportRelativePaths: Boolean = false,
    @Description("The encoding to use when reading and writing files.")
    charset: Charset = StandardCharsets.UTF_8,
    @Description("The working directory to use for making relative paths absolute.")
    cwd: AbsolutePath,
    @Hidden()
    stringModifiers: List[StringModifier] = Nil,
    @Hidden()
    @Description("The input stream to listen for enter key during file watching.")
    inputStream: InputStream = System.in,
    @Hidden()
    @Description("The generator for header IDs, defaults to GitHub ID generator")
    headerIdGenerator: String => String = GitHubIdGenerator,
    @Hidden()
    @Description("The pretty printer for variables")
    variablePrinter: Variable => String = ReplVariablePrinter
) {
  def isFileWatching: Boolean = watch && !check

  def toInputFile(infile: AbsolutePath): Option[InputFile] = {
    val relpath = infile.toRelative(in)
    if (isIncluded(relpath)) {
      val outfile = out.resolve(relpath)
      Some(InputFile(relpath, infile, outfile))
    } else {
      None
    }
  }
  def isExplicitlyExcluded(path: RelativePath): Boolean = {
    exclude.exists(_.matches(path.toNIO))
  }
  def isIncluded(path: RelativePath): Boolean = {
    (include.isEmpty || include.exists(_.matches(path.toNIO))) &&
    !isExplicitlyExcluded(path)
  }
  def validate(logger: Reporter): Configured[Context] = {
    if (Files.exists(in.toNIO)) {
      val compiler = MarkdownCompiler.fromClasspath(classpath)
      Configured.ok(Context(this, logger, compiler))
    } else {
      ConfError.fileDoesNotExist(in.toNIO).notOk
    }
  }
  def resolveIn(relpath: RelativePath): AbsolutePath = {
    in.resolve(relpath)
  }

  def resolveOut(relpath: RelativePath): AbsolutePath = {
    out.resolve(relpath)
  }
}

object Settings extends MetaconfigScalametaImplicits {
  def default(cwd: AbsolutePath): Settings = new Settings(
    in = cwd.resolve("docs"),
    out = cwd.resolve("out"),
    cwd = cwd
  )
  def fromCliArgs(args: List[String], base: Settings): Configured[Settings] = {
    Conf
      .parseCliArgs[Settings](args)
      .andThen(_.as[Settings](decoder(base)))
  }
  def version(displayVersion: String) =
    s"Mdoc v$displayVersion"
  def usage: String =
    """|Usage:   mdoc [




© 2015 - 2024 Weber Informatics LLC | Privacy Policy