-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add API for modify Node
- Loading branch information
Showing
7 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
yaml/shared/src/main/scala/org/virtuslab/yaml/NodeVisitor.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package org.virtuslab.yaml | ||
|
||
import org.virtuslab.yaml.Node | ||
import org.virtuslab.yaml.Node.* | ||
import org.virtuslab.yaml.Range | ||
import org.virtuslab.yaml.TraverseError | ||
import org.virtuslab.yaml.YamlError | ||
import org.virtuslab.yaml.syntax.NodeSelector | ||
import org.virtuslab.yaml.syntax.NodeSelector.* | ||
|
||
class NodeVisitor(node: Node, selectors: List[NodeSelector]): | ||
def apply(index: Int): NodeVisitor = | ||
NodeVisitor(node, selectors :+ NodeSelector.IntSelector(index)) | ||
def apply(field: String): NodeVisitor = | ||
NodeVisitor(node, selectors :+ NodeSelector.StringSelector(field)) | ||
|
||
private def updateScalarNode( | ||
value: String, | ||
scalar: ScalarNode | ||
): Either[TraverseError, ScalarNode] = | ||
selectors match { | ||
case Nil => | ||
Right( | ||
scalar.copy( | ||
value = value | ||
) | ||
) | ||
case _ => | ||
Left( | ||
TraverseError( | ||
s"Expected end of scalar path, instead found path ${selectors.map(_.show).mkString(".")}" | ||
) | ||
) | ||
} | ||
|
||
private def updateSequenceNode( | ||
value: String, | ||
sequence: SequenceNode | ||
): Either[TraverseError, SequenceNode] = | ||
selectors match { | ||
case IntSelector(index) :: rest => | ||
val nodes = sequence.nodes | ||
val updateNode = NodeVisitor(nodes(index), rest).setValue(value) | ||
updateNode.map(node => sequence.copy(nodes = nodes.updated(index, node))) | ||
|
||
case StringSelector(field) :: rest => | ||
Left( | ||
TraverseError( | ||
s"Expeceted index of sequence, insted found string path: ${field}" | ||
) | ||
) | ||
case _ => Left(TraverseError(s"Expeceted index of sequence, insted found end of path")) | ||
} | ||
|
||
private def updateMappingNode(value: String, mapping: MappingNode) = | ||
selectors match | ||
case StringSelector(field) :: rest => | ||
val mappings = mapping.mappings | ||
val entryToUpdateOpt = mappings.find { | ||
case (ScalarNode(keyName, _), _) => keyName == field | ||
case _ => false | ||
} | ||
|
||
entryToUpdateOpt match | ||
case Some(entryToUpdate) => | ||
val updatedValueE = entryToUpdate match | ||
case (ScalarNode(keyName, _), valueNode) => | ||
val updatedNode = NodeVisitor(valueNode, rest).setValue(value) | ||
updatedNode | ||
case _ => Left(TraverseError(s"Not found $field in mapping")) | ||
|
||
updatedValueE.map { updatedValue => | ||
mapping.copy( | ||
mappings.updated(entryToUpdate._1, updatedValue) | ||
) | ||
} | ||
case None => Left(TraverseError(s"Not found $field in mapping")) | ||
case IntSelector(index) :: rest => | ||
Left( | ||
TraverseError( | ||
s"Expeceted plain test, insted found index: $index" | ||
) | ||
) | ||
case _ => Left(TraverseError(s"Expeceted plain text, insted found end of path")) | ||
|
||
def setValue(value: String): Either[TraverseError, Node] = node match | ||
case scalar: ScalarNode => updateScalarNode(value, scalar) | ||
case sequence: SequenceNode => updateSequenceNode(value, sequence) | ||
case mapping: MappingNode => updateMappingNode(value, mapping) | ||
|
||
object NodeVisitor: | ||
|
||
def apply(node: Node, selectors: List[NodeSelector]): NodeVisitor = | ||
new NodeVisitor(node, selectors) | ||
|
||
extension (either: Either[TraverseError, NodeVisitor]) | ||
def apply(field: String): Either[TraverseError, NodeVisitor] = either.map(_.apply(field)) | ||
|
||
extension (either: Either[TraverseError, NodeVisitor]) | ||
def apply(index: Int): Either[TraverseError, NodeVisitor] = either.map(_.apply(index)) | ||
|
||
extension (either: Either[TraverseError, NodeVisitor]) | ||
def setValue(value: String): Either[TraverseError, Node] = either.flatMap(_.setValue(value)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
yaml/shared/src/main/scala/org/virtuslab/yaml/syntax/NodeSelector.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package org.virtuslab.yaml.syntax | ||
|
||
import org.virtuslab.yaml.Node | ||
|
||
sealed trait NodeSelector: | ||
def show: String | ||
|
||
object NodeSelector: | ||
|
||
case class IntSelector(index: Int) extends NodeSelector: | ||
override def show: String = index.toString | ||
case class StringSelector(field: String) extends NodeSelector: | ||
override def show: String = field |
11 changes: 11 additions & 0 deletions
11
yaml/shared/src/test/scala/org/virtuslab/yaml/TestOps.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.virtuslab.yaml | ||
|
||
object TestOps { | ||
|
||
extension [E <: YamlError, T](either: Either[E, T]) | ||
def orThrow: T = | ||
either match | ||
case Left(e) => throw new RuntimeException(e.msg) | ||
case Right(t) => t | ||
|
||
} |
53 changes: 53 additions & 0 deletions
53
yaml/shared/src/test/scala/org/virtuslab/yaml/travers/NodeVisitorSuite.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package org.virtuslab.yaml.travers | ||
|
||
import org.virtuslab.yaml.* | ||
import org.virtuslab.yaml.TestOps.* | ||
import org.virtuslab.yaml.Node | ||
import org.virtuslab.yaml.NodeVisitor._ | ||
|
||
class NodeVisitorSuite extends munit.FunSuite { | ||
|
||
test("should update ports for web") { | ||
|
||
val yaml = | ||
s"""version: "3.9" | ||
|services: | ||
| web: | ||
| build: . | ||
| volumes: | ||
| - .:/code | ||
| - logvolume01:/var/log | ||
| ports: | ||
| - "5000:5000" | ||
| redis: | ||
| image: "redis:alpine" | ||
|""".stripMargin | ||
|
||
val node: Node = yaml.asNode.orThrow | ||
val modifiedNode: Node = | ||
node | ||
.modify("services")("web")("ports")(0) | ||
.setValue("6000:6000") | ||
.modify("services")("redis")("image") | ||
.setValue("openjdk:11") | ||
.orThrow | ||
|
||
val modifiedYaml = modifiedNode.asYaml | ||
|
||
val exptectedYaml = | ||
s"""version: 3.9 | ||
|services: | ||
| web: | ||
| build: . | ||
| volumes: | ||
| - .:/code | ||
| - logvolume01:/var/log | ||
| ports: | ||
| - 6000:6000 | ||
| redis: | ||
| image: openjdk:11 | ||
|""".stripMargin | ||
|
||
assertEquals(modifiedYaml, exptectedYaml) | ||
} | ||
} |