diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-07-25 18:46:43 +0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2018-07-25 18:48:25 +0800 |
commit | e52c6f57ef42b54a355d0976cb43f6762280e855 (patch) | |
tree | c3cc0d8a971c7f8b112886c725bb759c3471151d /cask/src/cask/main/Main.scala | |
parent | 5c8a2030c048f96a7ded0c2f701e1612b53a2046 (diff) | |
download | cask-e52c6f57ef42b54a355d0976cb43f6762280e855.tar.gz cask-e52c6f57ef42b54a355d0976cb43f6762280e855.tar.bz2 cask-e52c6f57ef42b54a355d0976cb43f6762280e855.zip |
Basic invocation error renderer copied from Ammonite
Diffstat (limited to 'cask/src/cask/main/Main.scala')
-rw-r--r-- | cask/src/cask/main/Main.scala | 125 |
1 files changed, 117 insertions, 8 deletions
diff --git a/cask/src/cask/main/Main.scala b/cask/src/cask/main/Main.scala index 045c2ed..680dd04 100644 --- a/cask/src/cask/main/Main.scala +++ b/cask/src/cask/main/Main.scala @@ -7,7 +7,7 @@ import io.undertow.Undertow import io.undertow.server.{HttpHandler, HttpServerExchange} import io.undertow.server.handlers.BlockingHandler import io.undertow.util.HttpString - +import fastparse.utils.Utils.literalize class MainRoutes extends BaseMain with Routes{ def allRoutes = Seq(this) @@ -47,6 +47,111 @@ abstract class BaseMain{ response.data.write(exchange.getOutputStream) } + def getLeftColWidth(items: Seq[Router.ArgSig[_, _, _,_]]) = { + items.map(_.name.length + 2) match{ + case Nil => 0 + case x => x.max + } + } + + def renderArg[T](base: T, + arg: Router.ArgSig[_, T, _, _], + leftOffset: Int, + wrappedWidth: Int): (String, String) = { + val suffix = arg.default match{ + case Some(f) => " (default " + f(base) + ")" + case None => "" + } + val docSuffix = arg.doc match{ + case Some(d) => ": " + d + case None => "" + } + val wrapped = Util.softWrap( + arg.typeString + suffix + docSuffix, + leftOffset, + wrappedWidth - leftOffset + ) + (arg.name, wrapped) + } + + def formatMainMethodSignature[T](base: T, + main: Router.EntryPoint[_, T, _], + leftIndent: Int, + leftColWidth: Int) = { + // +2 for space on right of left col + val args = main.argSignatures.map(renderArg(base, _, leftColWidth + leftIndent + 2 + 2, 80)) + + val leftIndentStr = " " * leftIndent + val argStrings = + for((lhs, rhs) <- args) + yield { + val lhsPadded = lhs.padTo(leftColWidth, ' ') + val rhsPadded = rhs.lines.mkString("\n") + s"$leftIndentStr $lhsPadded $rhsPadded" + } + val mainDocSuffix = main.doc match{ + case Some(d) => "\n" + leftIndentStr + Util.softWrap(d, leftIndent, 80) + case None => "" + } + + s"""$leftIndentStr${main.name}$mainDocSuffix + |${argStrings.map(_ + "\n").mkString}""".stripMargin + } + + def formatInvokeError[T](base: T, route: Router.EntryPoint[_, T, _], x: Router.Result.Error): String = { + def expectedMsg = formatMainMethodSignature(base: T, route, 0, 0) + + x match{ + case Router.Result.Error.Exception(x) => ??? + case Router.Result.Error.MismatchedArguments(missing, unknown) => + val missingStr = + if (missing.isEmpty) "" + else { + val chunks = + for (x <- missing) + yield x.name + ": " + x.typeString + + val argumentsStr = Util.pluralize("argument", chunks.length) + s"Missing $argumentsStr: (${chunks.mkString(", ")})\n" + } + + + val unknownStr = + if (unknown.isEmpty) "" + else { + val argumentsStr = Util.pluralize("argument", unknown.length) + s"Unknown $argumentsStr: " + unknown.map(literalize(_)).mkString(" ") + "\n" + } + + + s"""$missingStr$unknownStr + |Arguments provided did not match expected signature: + | + |$expectedMsg + |""".stripMargin + + case Router.Result.Error.InvalidArguments(x) => + val argumentsStr = Util.pluralize("argument", x.length) + val thingies = x.map{ + case Router.Result.ParamError.Invalid(p, v, ex) => + val literalV = literalize(v) + + s"${p.name}: ${p.typeString} = $literalV failed to parse with $ex" + case Router.Result.ParamError.DefaultFailed(p, ex) => + s"${p.name}'s default value failed to evaluate with $ex" + } + + s"""The following $argumentsStr failed to parse: + | + |${thingies.mkString("\n")} + | + |expected signature: + | + |$expectedMsg + |""".stripMargin + + } + } lazy val defaultHandler = new HttpHandler() { def handleRequest(exchange: HttpServerExchange): Unit = { routeTrie.lookup(Util.splitPath(exchange.getRequestPath).toList, Map()) match{ @@ -61,13 +166,17 @@ abstract class BaseMain{ result match{ case Router.Result.Success(response) => writeResponse(exchange, response) - case Router.Result.Error.Exception(e) => - println(e) - e.printStackTrace() - writeResponse(exchange, handleError(500)) - case err: Router.Result.Error => - println(err) - writeResponse(exchange, handleError(400)) + case e: Router.Result.Error => + + writeResponse(exchange, + Response( + formatInvokeError( + routes, + metadata.entryPoint.asInstanceOf[EntryPoint[_, cask.main.Routes, _]], + e + ), + statusCode = 500) + ) } |