diff --git a/README.md b/README.md
index 7567c2a..b7ac8f5 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,6 @@ This library is not production ready yet. There is a lot of work to do to comple
- Reach a good code coverage with the tests (using munit)
- Improve documentation
-Contributions are more than welcome! 💪
Given `Foo` class
```scala
@@ -79,7 +78,7 @@ val node: XmlNode = XmlNode("Foo")
val result: Modifier.Result[XmlNode] = Root
.modify(_.withText("NEW"))
.apply(node)
-// result: Either[ModifierFailure, XmlNode] = Right(
+// result: Modifier.Result[XmlNode] = Right(
// value = NEW
// )
-```
+```
\ No newline at end of file
diff --git a/build.sbt b/build.sbt
index bf684a4..4526445 100644
--- a/build.sbt
+++ b/build.sbt
@@ -210,4 +210,4 @@ def scalacSettings(scalaVersion: String): Seq[String] =
}
//=============================== ALIASES ===============================
-addCommandAlias("check", ";clean;test")
+addCommandAlias("check", "scalafmtAll;clean;coverage;test;coverageAggregate")
diff --git a/core/example/untitled.sc b/core/example/untitled.sc
index 7082c3d..e708ee8 100644
--- a/core/example/untitled.sc
+++ b/core/example/untitled.sc
@@ -1,52 +1,73 @@
import cats.xml.codec.Decoder
import cats.xml.XmlNode
+import cats.xml.cursor.NodeCursor.Root
import cats.xml.implicits._
+import cats.xml.modifier.Modifier
////
//////############### PARSING from NODESEQ ###############
////val n1: XmlNode = TEST
////
//////############### CURSOR ###############
-////val node: XmlNode =
-////
-////
-////
-////
-////
-////
-////
-////
-////
-////
-//// LOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREA LOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREA
-////
-////
-////
-////
-////
-////
-////
-////
-////
-////
+val node: XmlNode =
+ xml"""
+
+
+
+
+
+
+
+
+
+ LOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREA LOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREALOREA
+
+
+
+
+
+
+
+
+
+ """.get
+
+
+
////
////node.findDeepChild("roar")
////Xml.toNodeSeq(node)
////
-////val result1: CursorResult[Int] =
-//// Root
-//// .down("foo")
-//// .down("bar")
-//// .down("root")
-//// .down("foo")
-//// .down("bar")
-//// .down("root")
-//// .down("foo")
-//// .down("bar")
-//// .down("roar")
-//// .attr("a")
-//// .as[Int]
-//// .focus(node)
+//val result1: FreeCursor.Result[Int] =
+// Root
+// .down("foo")
+// .down("bar")
+// .down("root")
+// .down("foo")
+// .down("bar")
+// .down("root")
+// .down("foo")
+// .down("bar")
+// .down("roar")
+// .attr("a")
+// .as[Int]
+// .focus(node)
+
+val result1: Modifier.Result[XmlNode] =
+ Root
+ .down("foo")
+ .down("bar")
+ .down("root")
+ .down("foo")
+ .down("bar")
+ .down("root")
+ .down("foo")
+ .down("bar")
+ .down("roar")
+ .attr("a")
+ .modify(_ => "TEST")
+ .apply(node)
+
////
////
////val result1: CursorResult[Int] =
diff --git a/core/src/main/scala/cats/xml/XmlAttribute.scala b/core/src/main/scala/cats/xml/XmlAttribute.scala
index c7abf83..84a20dd 100644
--- a/core/src/main/scala/cats/xml/XmlAttribute.scala
+++ b/core/src/main/scala/cats/xml/XmlAttribute.scala
@@ -1,10 +1,16 @@
package cats.xml
-import cats.{Eq, Show}
+import cats.{Endo, Eq, Show}
import cats.xml.codec.DataEncoder
case class XmlAttribute(key: String, value: XmlData) extends Xml with Serializable {
+ def map[T: DataEncoder](f: XmlData => T): XmlAttribute =
+ map(value => DataEncoder[T].encode(f(value)))
+
+ def map(f: Endo[XmlData]): XmlAttribute =
+ XmlAttribute(key, f(value))
+
override def toString: String = XmlAttribute.stringify(this)
override def equals(obj: Any): Boolean =
diff --git a/core/src/main/scala/cats/xml/XmlNode.scala b/core/src/main/scala/cats/xml/XmlNode.scala
index d929adf..07c2f34 100644
--- a/core/src/main/scala/cats/xml/XmlNode.scala
+++ b/core/src/main/scala/cats/xml/XmlNode.scala
@@ -35,9 +35,6 @@ class XmlNode private (
def withAttributes(attr: XmlAttribute, attrs: XmlAttribute*): XmlNode =
updateAttrs(_ => attr +: attrs)
- def withAttributesMap(values: Map[String, String]): XmlNode =
- updateAttrs(_ => XmlAttribute.fromMap(values))
-
def prependAttr(newAttr: XmlAttribute): XmlNode =
updateAttrs(ls => newAttr +: ls)
@@ -50,6 +47,9 @@ class XmlNode private (
def updateAttrs(f: Endo[Seq[XmlAttribute]]): XmlNode =
copy(attributes = f(attributes))
+ def updateAttr(key: String)(f: Endo[XmlAttribute]): XmlNode =
+ updateAttrs(_.map(attr => if (attr.key == key) f(attr) else attr))
+
// ------ CONTENT ------
val hasChildren: Boolean = children.nonEmpty
@@ -107,18 +107,18 @@ class XmlNode private (
def findDeepChild(thatLabel: String): Option[XmlNode] =
deepSubNodes.find(_.label == thatLabel)
- def deepSubNodes: List[XmlNode] = {
+ def deepSubNodes: LazyList[XmlNode] = {
@tailrec
- def rec(left: List[XmlNode], acc: List[XmlNode]): List[XmlNode] =
+ def rec(left: List[XmlNode], acc: LazyList[XmlNode]): LazyList[XmlNode] =
left match {
case Nil => acc
case head :: tail => rec(tail, acc ++ head.deepSubNodes)
}
content.children match {
- case Nil => Nil
- case currentNodeChildren => rec(currentNodeChildren, Nil)
+ case Nil => LazyList.empty
+ case currentNodeChildren => rec(currentNodeChildren, LazyList.empty)
}
}
diff --git a/core/src/main/scala/cats/xml/codec/Codec.scala b/core/src/main/scala/cats/xml/codec/Codec.scala
index 0d91522..3e9e729 100644
--- a/core/src/main/scala/cats/xml/codec/Codec.scala
+++ b/core/src/main/scala/cats/xml/codec/Codec.scala
@@ -1,7 +1,6 @@
package cats.xml.codec
import cats.xml.Xml
-import cats.xml.cursor.Cursor
/** Isomorphism
*/
@@ -10,9 +9,6 @@ case class Codec[T] private (
encoder: Encoder[T]
) {
- def decodeCursorResult(cursorResult: Cursor.Result[Xml]): Decoder.Result[T] =
- decoder.decodeCursorResult(cursorResult)
-
def decode(xml: Xml): Decoder.Result[T] =
decoder.decode(xml)
diff --git a/core/src/main/scala/cats/xml/codec/Encoder.scala b/core/src/main/scala/cats/xml/codec/Encoder.scala
index c809e66..c29bbc3 100644
--- a/core/src/main/scala/cats/xml/codec/Encoder.scala
+++ b/core/src/main/scala/cats/xml/codec/Encoder.scala
@@ -49,8 +49,9 @@ object DataEncoder extends DataEncoderPrimitivesInstances {
}
private[xml] trait DataEncoderPrimitivesInstances {
- implicit val encoderUnit: DataEncoder[Unit] = DataEncoder.of(_ => XmlNull)
- implicit val encoderString: DataEncoder[String] = DataEncoder.of(XmlString(_))
+ implicit val encoderXmlData: DataEncoder[XmlData] = DataEncoder.of(identity)
+ implicit val encoderUnit: DataEncoder[Unit] = DataEncoder.of(_ => XmlNull)
+ implicit val encoderString: DataEncoder[String] = DataEncoder.of(XmlString(_))
implicit val encoderBoolean: DataEncoder[Boolean] = encoderString.contramap {
case true => "true"
case false => "false"
diff --git a/core/src/main/scala/cats/xml/cursor/AttrCursor.scala b/core/src/main/scala/cats/xml/cursor/AttrCursor.scala
index 5653c1a..53ec82f 100644
--- a/core/src/main/scala/cats/xml/cursor/AttrCursor.scala
+++ b/core/src/main/scala/cats/xml/cursor/AttrCursor.scala
@@ -1,19 +1,32 @@
package cats.xml.cursor
import cats.Show
-import cats.xml.{XmlAttribute, XmlNode}
+import cats.xml.{XmlAttribute, XmlData, XmlNode}
+import cats.xml.codec.DataEncoder
import cats.xml.cursor.AttrCursor.Op
import cats.xml.cursor.Cursor.CursorOp
+import cats.xml.modifier.{Modifier, ModifierFailure}
/** Horizontal cursor for node attributes
*/
class AttrCursor(protected val vCursor: NodeCursor, op: AttrCursor.Op)
- extends HCursor[XmlAttribute, NodeCursor, AttrCursor] {
+ extends HCursor[XmlAttribute, NodeCursor, AttrCursor] { $this =>
import cats.implicits.*
lazy val path: String = s"${vCursor.path}$op"
+ // modify
+ def modify[T: DataEncoder](f: XmlData => T): Modifier[XmlNode] =
+ Modifier(node =>
+ $this.focus(node) match {
+ case Right(attr) =>
+ vCursor.modify(_.updateAttr(attr.key)(_ => attr.map(f)))(node)
+ case Left(failure) =>
+ ModifierFailure.CursorFailed(failure).asLeft
+ }
+ )
+
// focus
override def focus(xml: XmlNode): Cursor.Result[XmlAttribute] = {
diff --git a/core/src/main/scala/cats/xml/cursor/NodeCursor.scala b/core/src/main/scala/cats/xml/cursor/NodeCursor.scala
index 97957ef..3b5169c 100644
--- a/core/src/main/scala/cats/xml/cursor/NodeCursor.scala
+++ b/core/src/main/scala/cats/xml/cursor/NodeCursor.scala
@@ -3,7 +3,7 @@ package cats.xml.cursor
import cats.{Endo, Show}
import cats.xml.XmlNode
import cats.xml.cursor.Cursor.CursorOp
-import cats.xml.modifier.Modifier
+import cats.xml.modifier.{Modifier, ModifierFailure}
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
@@ -17,7 +17,16 @@ sealed trait NodeCursor extends Dynamic with VCursor[XmlNode, NodeCursor] {
override lazy val path: String = CursorOp.buildOpsPath(history)
def modify(modifier: Endo[XmlNode]): Modifier[XmlNode] =
- Modifier.fromNodeCursor(this, modifier)
+ Modifier(node => {
+ val nodeClone = node.copy()
+ focus(nodeClone) match {
+ case Right(focus) =>
+ focus.mute(modifier)
+ Right(focus)
+ case Left(failure) =>
+ Left(ModifierFailure.CursorFailed(failure))
+ }
+ })
// node
def selectDynamic(nodeName: String): NodeCursor =
diff --git a/core/src/main/scala/cats/xml/cursor/TextCursor.scala b/core/src/main/scala/cats/xml/cursor/TextCursor.scala
index 4d32cfb..c79a8e6 100644
--- a/core/src/main/scala/cats/xml/cursor/TextCursor.scala
+++ b/core/src/main/scala/cats/xml/cursor/TextCursor.scala
@@ -2,17 +2,27 @@ package cats.xml.cursor
import cats.xml.{XmlData, XmlNode}
import cats.xml.codec.DataEncoder
-import cats.xml.modifier.Modifier
+import cats.xml.modifier.{Modifier, ModifierFailure}
/** Vertical cursor for node Text
*/
class TextCursor(protected[xml] val lastCursor: NodeCursor) extends VCursor[XmlData, NodeCursor] {
+ $this =>
+
+ import cats.implicits.*
override lazy val path: String = lastCursor.path
// modify
- def modify[T: DataEncoder](f: Option[XmlData] => T): Modifier[XmlNode] =
- Modifier.fromTextCursor(this, f)
+ def modify[T: DataEncoder](f: XmlData => T): Modifier[XmlNode] =
+ Modifier(node =>
+ $this.focus(node) match {
+ case Right(textValue) =>
+ lastCursor.modify(_.withText(f(textValue)))(node)
+ case Left(failure) =>
+ ModifierFailure.CursorFailed(failure).asLeft
+ }
+ )
// focus
override def focus(node: XmlNode): Cursor.Result[XmlData] =
diff --git a/core/src/main/scala/cats/xml/modifier/Modifier.scala b/core/src/main/scala/cats/xml/modifier/Modifier.scala
index 441cd7d..16b825e 100644
--- a/core/src/main/scala/cats/xml/modifier/Modifier.scala
+++ b/core/src/main/scala/cats/xml/modifier/Modifier.scala
@@ -1,11 +1,8 @@
package cats.xml.modifier
-import cats.{Endo, Monoid}
-import cats.xml.{XmlData, XmlNode}
-import cats.xml.codec.DataEncoder
-import cats.xml.cursor.{NodeCursor, TextCursor}
+import cats.Monoid
-/** Create a modified copy of input [[XmlNode]]
+/** Create a modified copy of input 'XmlNode'
*/
trait Modifier[T] { $this =>
@@ -36,27 +33,6 @@ object Modifier extends ModifierInstances {
type Result[+T] = Either[ModifierFailure, T]
- def fromNodeCursor(
- cursor: NodeCursor,
- modifier: Endo[XmlNode]
- ): Modifier[XmlNode] =
- Modifier(node => {
- val nodeClone = node.copy()
- cursor.focus(nodeClone) match {
- case Right(focus) =>
- focus.mute(modifier)
- focus.asRight
- case Left(failure) =>
- ModifierFailure.CursorFailed(failure).asLeft
- }
- })
-
- def fromTextCursor[T: DataEncoder](
- cursor: TextCursor,
- modifier: Option[XmlData] => T
- ): Modifier[XmlNode] =
- cursor.lastCursor.modify(n => n.withText(modifier(n.text)))
-
def apply[T](
f: T => Modifier.Result[T]
): Modifier[T] = (input: T) => f(input)
diff --git a/docs/compiled/README.md b/docs/compiled/README.md
index f427f88..b7ac8f5 100644
--- a/docs/compiled/README.md
+++ b/docs/compiled/README.md
@@ -78,7 +78,7 @@ val node: XmlNode = XmlNode("Foo")
val result: Modifier.Result[XmlNode] = Root
.modify(_.withText("NEW"))
.apply(node)
-// result: Either[ModifierFailure, XmlNode] = Right(
+// result: Modifier.Result[XmlNode] = Right(
// value = NEW
// )
```
\ No newline at end of file