app/ansible/RoleParser.scala (55 lines of code) (raw):

package ansible import java.nio.file.{Files, Path} import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import models.{Markdown, RoleId, RoleSummary, Yaml} import scala.jdk.CollectionConverters._ import scala.util.Try object RoleParser { private val yamlFactory = new YAMLFactory() def createRoleSummary(roleDir: Path): Option[RoleSummary] = { val roleId = RoleId(roleDir.getFileName.toString) val dependsOn = parseDependencies(roleDir.resolve("meta/main.yml")) val tasksMain = readYamlFile(roleDir.resolve("tasks/main.yml")) val readme = readReadme(roleDir.resolve("README.md")) for { yaml <- tasksMain } yield { RoleSummary(roleId, dependsOn, yaml, readme) } } def readReadme(path: Path): Option[Markdown] = { if (Files.isRegularFile(path)) { Try(Files.readAllLines(path)).toOption .map(lines => Markdown(lines.asScala.mkString("\n"))) } else None } def parseDependencies(metaMainYaml: Path): Set[RoleId] = { readYamlFile(metaMainYaml) .map(parseDependenciesFromYaml) .getOrElse(Set.empty) } /** Extracts the role IDs from YAML that looks like this: * * {{{ * --- * dependencies: * - foo * - { role: bar, baz: wow } * }}} */ def parseDependenciesFromYaml(yaml: Yaml): Set[RoleId] = { val mapper = new ObjectMapper(yamlFactory) val jmap = mapper.readValue(yaml.content, classOf[java.util.Map[String, Any]]) val deps = jmap.asScala .get("dependencies") .map(_.asInstanceOf[java.util.List[Any]].asScala) .getOrElse(Nil) deps .collect { case roleId: String => Some(RoleId(roleId)) case customisedRole: java.util.Map[String, String] @unchecked => customisedRole.asScala.get("role").map(RoleId(_)) } .flatten .toSet } private def readYamlFile(path: Path): Option[Yaml] = { if (Files.isRegularFile(path)) { Try(Files.readAllLines(path)).toOption .map(lines => Yaml(lines.asScala.mkString("\n"))) } else None } }