import java.io.{File, IOException} import java.net.MalformedURLException import java.nio.file.Files import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer import scala.io.Source import scala.util.{Failure, Success, Try} import scalaj.http.Http object Main { private val maven_host = "search.maven.org" private val cbt_home = System.getenv("CBT_HOME") implicit class StringExtensionMethods(str: String) { def /(s: String): String = str + File.separator + s } val uiPort = 9080 def main(args: Array[String]) = launchUi(new File(args(0)), args(1)) def launchUi(projectDirectory: File, scalaMajorVersion: String): Unit = { val staticBase = new File(cbt_home / "tools" / "gui" / "resources" / "web").toURI.toURL.toExternalForm val server = new JettyServer(uiPort, staticBase) { override def route(method: String, path: String, param: String => String, setContentType: String => Unit) = (method, path) match { case ("GET", "/cwd") => Success(s"""["$projectDirectory"]""") case ("POST", "/project/new") => val name = param("name") val defaultPackage = param("pack") val dependencies = param("dependencies") val flags = param("flags") handleIoException { new ProjectBuilder(name, defaultPackage, dependencies, flags, projectDirectory, scalaMajorVersion).build() Success("[]") } case ("POST", "/project/copy") => val name = param("name") val source = new File(cbt_home / "examples" / name) val target = new File(projectDirectory.getAbsolutePath / name) handleIoException { copyTree(source, target) Success("[]") } case ("GET", "/dependency") => val query = param("query") handleIoException(handleMavenBadResponse(searchDependency(query))) case ("GET", "/dependency/version") => val group = param("group") val artifact = param("artifact") handleIoException(handleMavenBadResponse(searchDependencyVersion(group, artifact))) case ("GET", "/examples") => handleIoException { val names = new File(cbt_home / "examples").listFiles().filter(_.isDirectory).sortBy(_.getName) .map('"' + _.getName + '"').mkString(",") Success(s"[$names]") } case ("GET", "/example/files") => val name = param("name") handleIoException { val dir = new File(cbt_home / "examples" / name) if (dir.exists()) Success(serializeTree(dir)) else Failure(new IllegalArgumentException(s"Incorrect example name: $name")) } case ("GET", "/example/file") => setContentType("text/plain") val path = param("path") handleIoException { val file = new File(path) Success { val content = Source.fromFile(file).mkString if (file.getName.endsWith(".md")) parseMd(content) else content } } case _ => Failure(new MalformedURLException(s"Incorrect path: $path")) } } server.start() java.awt.Desktop.getDesktop.browse(new java.net.URI(s"http://localhost:$uiPort/")) println("Press Enter to stop UI server.") while (Source.stdin.getLines().next().nonEmpty) {} server.stop() } private def searchDependency(query: String) = { Http(s"http://$maven_host/solrsearch/select") .param("q", query) .param("rows", "30") .param("wt", "json") .asString.body } private def searchDependencyVersion(group: String, artifact: String) = { val query = s"""q=g:"$group"+AND+a:"$artifact"""" Http(s"http://$maven_host/solrsearch/select?" + query) .param("rows", "30") .param("wt", "json") .param("core", "gav") .asString.body } private def handleIoException(f: => Try[String]) = try f catch { case e: IOException => e.printStackTrace() Failure(e) } private def handleMavenBadResponse(result: String) = { if (result.startsWith("{")) Success(result) else Failure(new Exception(s"Bad response from $maven_host: $result")) } private def serializeTree(file: File): String = { val data = if (file.isDirectory) s""","children":[${file.listFiles().sortBy(_.getName).map(serializeTree).mkString(",")}]""" else "" s"""{"name":"${file.getName}","path":"${file.getAbsolutePath}"$data}""" } private def parseMd(s: String) = { val parser = Parser.builder().build() val document = parser.parse(s) val renderer = HtmlRenderer.builder().build() renderer.render(document) } private def copyTree(sourceRoot: File, targetRoot: File) = listFilesRecursive(sourceRoot).sorted.foreach { source => val path = source.getAbsolutePath.replace(sourceRoot.getAbsolutePath, targetRoot.getAbsolutePath) val file = new File(path) if (source.isDirectory) file.mkdirs() else Files.copy(source.toPath, file.toPath) } private def listFilesRecursive(f: File): Seq[File] = f +: (if (f.isDirectory) f.listFiles.flatMap(listFilesRecursive).toVector else Vector[File]()) }