Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop I type parameter for FreeCursor #142

Merged
merged 1 commit into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/xml/codec/Decoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cats.data.*
import cats.xml.*
import cats.xml.cursor.{Cursor, FreeCursor, NodeCursor}
import cats.xml.cursor.NodeCursor.Root
import cats.xml.XmlData.{XmlNumber, *}
import cats.xml.XmlData.*

import scala.collection.Factory
import scala.reflect.ClassTag
Expand Down Expand Up @@ -82,7 +82,7 @@ object Decoder extends DecoderInstances with DecoderSyntax {
.reduceLeft(_ or _)

def fromCursor[U](
f: NodeCursor => FreeCursor[Xml, U]
f: NodeCursor => FreeCursor[U]
): Decoder[U] = Decoder.of {
case Left(failure) => DecoderFailure.CursorFailed(failure).invalidNel
case Right(xml: Xml) =>
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/xml/cursor/Cursor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ sealed trait Cursor[+X <: Xml] extends Serializable {
* @return
* A new `FreeCursor`
*/
def as[T: Decoder]: FreeCursor[Xml, T] =
def as[T: Decoder]: FreeCursor[T] =
FreeCursor[T](this)

/** A String representation of the cursor.
Expand Down
66 changes: 26 additions & 40 deletions core/src/main/scala/cats/xml/cursor/FreeCursor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@
import cats.xml.codec.{Decoder, DecoderFailure}
import cats.xml.validator.Validator

/** `FreeCursor` represent a cursor with a free `O` type as result of the focusing and a free `I`
* type as target of the focusing.
/** `FreeCursor` represent a cursor with a free `O` type as result of the focusing.
*
* @tparam I
* Input type of the `FreeCursor`
* @tparam O
* Output type of the `FreeCursor`
*/
sealed trait FreeCursor[I, +O] extends Serializable { $this =>
sealed trait FreeCursor[+O] extends Serializable { $this =>

/** Apply the current cursor to the specified input of type `I`. This allows to select a precise
* part of the input `I` tree.
Expand All @@ -27,7 +24,7 @@
* @return
* `Right` when succeed `Left` when fail
*/
def focus(input: I): FreeCursor.Result[O]
def focus(input: Xml): FreeCursor.Result[O]

/** Map the result of this cursor when succeed
* @param f
Expand All @@ -38,7 +35,7 @@
* A new `FreeCursor` which once applied, apply this cursor and then if succeed apply the
* function `f` in order to map the result
*/
def map[U](f: O => U): FreeCursor[I, U] =
def map[U](f: O => U): FreeCursor[U] =
FreeCursor.of($this.focus(_).map(f))

/** Create a new `FreeCursor` where the output of this cursor is validated with the specified
Expand All @@ -50,8 +47,8 @@
* @return
* A new validated `FreeCursor`
*/
def validate[OO >: O](validator: Validator[OO]): FreeCursor[I, OO] =
FreeCursor.of((input: I) =>
def validate[OO >: O](validator: Validator[OO]): FreeCursor[OO] =

Check notice

Code scanning / Codacy-scalameta-pro (reported by Codacy)

Enforces type parameters naming convention Note

Type parameter 'OO' does not conform with naming standard.
FreeCursor.of((input: Xml) =>

Check warning on line 51 in core/src/main/scala/cats/xml/cursor/FreeCursor.scala

View check run for this annotation

Codecov / codecov/patch

core/src/main/scala/cats/xml/cursor/FreeCursor.scala#L50-L51

Added lines #L50 - L51 were not covered by tests
$this.focus(input).andThen(o =>
validator(o)
.leftMap(eNel => NonEmptyList.one(CursorFailure.ValidationsFailed("** UNKNOWN **", eNel)))
Expand All @@ -64,27 +61,24 @@

type Result[+T] = ValidatedNel[CursorFailure, T]

def id[T]: FreeCursor[T, T] =
FreeCursor.of(_.validNel)

def pure[I, O](value: O): FreeCursor[I, O] =
def pure[O](value: O): FreeCursor[O] =

Check warning on line 64 in core/src/main/scala/cats/xml/cursor/FreeCursor.scala

View check run for this annotation

Codecov / codecov/patch

core/src/main/scala/cats/xml/cursor/FreeCursor.scala#L64

Added line #L64 was not covered by tests
const(value.validNel)

def failure[I, O](value: NonEmptyList[CursorFailure]): FreeCursor[I, O] =
def failure[O](value: NonEmptyList[CursorFailure]): FreeCursor[O] =

Check warning on line 67 in core/src/main/scala/cats/xml/cursor/FreeCursor.scala

View check run for this annotation

Codecov / codecov/patch

core/src/main/scala/cats/xml/cursor/FreeCursor.scala#L67

Added line #L67 was not covered by tests
const(value.invalid)

def const[I, O](result: FreeCursor.Result[O]): FreeCursor[I, O] =
def const[O](result: FreeCursor.Result[O]): FreeCursor[O] =

Check warning on line 70 in core/src/main/scala/cats/xml/cursor/FreeCursor.scala

View check run for this annotation

Codecov / codecov/patch

core/src/main/scala/cats/xml/cursor/FreeCursor.scala#L70

Added line #L70 was not covered by tests
FreeCursor.of(_ => result)

private[xml] def of[I, O](f: I => FreeCursor.Result[O]): FreeCursor[I, O] =
new FreeCursor[I, O] {
override def focus(input: I): Result[O] = f(input)
private[xml] def of[O](f: Xml => FreeCursor.Result[O]): FreeCursor[O] =
new FreeCursor[O] {
override def focus(input: Xml): Result[O] = f(input)
}

def apply[O: Decoder](
cursor: Cursor[Xml]
): FreeCursor[Xml, O] =
new FreeCursor[Xml, O] { $this =>
): FreeCursor[O] =
new FreeCursor[O] { $this =>
override def focus(xml: Xml): FreeCursor.Result[O] = {

val cursorResult: Cursor.Result[Xml] = xml match {
Expand Down Expand Up @@ -123,7 +117,7 @@
}
}

override def validate[OO >: O](validator: Validator[OO]): FreeCursor[Xml, OO] =
override def validate[OO >: O](validator: Validator[OO]): FreeCursor[OO] =

Check notice

Code scanning / Codacy-scalameta-pro (reported by Codacy)

Enforces type parameters naming convention Note

Type parameter 'OO' does not conform with naming standard.
FreeCursor.of((input: Xml) =>
$this.focus(input).andThen(o =>
validator(o)
Expand All @@ -135,38 +129,30 @@

private[xml] trait FreeCursorInstances {

implicit def applicativeErrorForFreeCursor[I]
: ApplicativeError[FreeCursor[I, *], NonEmptyList[CursorFailure]] =
new ApplicativeError[FreeCursor[I, *], NonEmptyList[CursorFailure]] {
implicit val applicativeErrorForFreeCursor
: ApplicativeError[FreeCursor, NonEmptyList[CursorFailure]] =
new ApplicativeError[FreeCursor, NonEmptyList[CursorFailure]] {

override def map[A, B](fa: FreeCursor[I, A])(f: A => B): FreeCursor[I, B] =
override def map[A, B](fa: FreeCursor[A])(f: A => B): FreeCursor[B] =
fa.map(f)

def pure[A](a: A): FreeCursor[I, A] =
def pure[A](a: A): FreeCursor[A] =
FreeCursor.pure(a)

def ap[A, B](ff: FreeCursor[I, A => B])(fa: FreeCursor[I, A]): FreeCursor[I, B] =
FreeCursor.of((input: I) => fa.focus(input).ap(ff.focus(input)))

override def product[A, B](
fa: FreeCursor[I, A],
fb: FreeCursor[I, B]
): FreeCursor[I, (A, B)] =
FreeCursor.of((input: I) => fa.focus(input).product(fb.focus(input)))

override def unit: FreeCursor[I, Unit] = pure(())
def ap[A, B](ff: FreeCursor[A => B])(fa: FreeCursor[A]): FreeCursor[B] =
FreeCursor.of((input: Xml) => fa.focus(input).ap(ff.focus(input)))

def handleErrorWith[A](
fa: FreeCursor[I, A]
)(f: NonEmptyList[CursorFailure] => FreeCursor[I, A]): FreeCursor[I, A] =
FreeCursor.of((input: I) =>
fa: FreeCursor[A]
)(f: NonEmptyList[CursorFailure] => FreeCursor[A]): FreeCursor[A] =
FreeCursor.of((input: Xml) =>
fa.focus(input) match {
case Validated.Invalid(e) => f(e).focus(input)
case v @ Validated.Valid(_) => v
}
)

def raiseError[A](e: NonEmptyList[CursorFailure]): FreeCursor[I, A] =
def raiseError[A](e: NonEmptyList[CursorFailure]): FreeCursor[A] =
FreeCursor.const(Validated.Invalid(e))
}
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/xml/xmlNode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ sealed trait XmlNodeSyntax {
* @return
* Cursor result, Left when fails Right when succeed
*/
def focus[T](f: NodeCursor.Root.type => FreeCursor[Xml, T]): FreeCursor.Result[T] =
def focus[T](f: NodeCursor.Root.type => FreeCursor[T]): FreeCursor.Result[T] =
f(Root).focus(node)

def modify(f: NodeCursor.Root.type => Modifier[XmlNode]): Modifier.Result[XmlNode] =
Expand Down
18 changes: 9 additions & 9 deletions core/src/test/scala/cats/xml/cursor/FreeCursorSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package cats.xml.cursor

import cats.data.NonEmptyList
import cats.data.Validated.{Invalid, Valid}
import cats.xml.{Xml, XmlNode}
import cats.xml.XmlNode
import cats.xml.cursor.NodeCursor.Root
import cats.xml.validator.Validator

class FreeCursorSuite extends munit.FunSuite {

test("FreeCursor.focus - valid") {

val cursor: FreeCursor[Xml, Int] = Root.bar.test.as[Int]
val cursor: FreeCursor[Int] = Root.bar.test.as[Int]
val node: XmlNode =
XmlNode("foo")
.withChildren(
Expand All @@ -28,8 +28,8 @@ class FreeCursorSuite extends munit.FunSuite {

test("FreeCursor.focus - invalid") {

val cursor: FreeCursor[Xml, Int] = Root.bar.test.as[Int]
val incompleteNode: XmlNode = XmlNode("foo").withChildren(XmlNode("bar"))
val cursor: FreeCursor[Int] = Root.bar.test.as[Int]
val incompleteNode: XmlNode = XmlNode("foo").withChildren(XmlNode("bar"))

assertEquals(
obtained = cursor.focus(incompleteNode),
Expand All @@ -39,7 +39,7 @@ class FreeCursorSuite extends munit.FunSuite {

test("FreeCursor.map") {

val cursor: FreeCursor[Xml, Int] = Root.bar.test.as[Int]
val cursor: FreeCursor[Int] = Root.bar.test.as[Int]
val node: XmlNode =
XmlNode("foo")
.withChildren(
Expand All @@ -57,7 +57,7 @@ class FreeCursorSuite extends munit.FunSuite {

test("FreeCursor.validate - valid") {

val cursor: FreeCursor[Xml, Int] = Root.bar.test.as[Int]
val cursor: FreeCursor[Int] = Root.bar.test.as[Int]
val node: XmlNode =
XmlNode("foo")
.withChildren(
Expand All @@ -77,7 +77,7 @@ class FreeCursorSuite extends munit.FunSuite {

test("FreeCursor.validate - invalid") {

val cursor: FreeCursor[Xml, Int] = Root.bar.test.as[Int]
val cursor: FreeCursor[Int] = Root.bar.test.as[Int]
val node: XmlNode =
XmlNode("foo")
.withChildren(
Expand Down Expand Up @@ -112,11 +112,11 @@ class FreeCursorSuite extends munit.FunSuite {
// import cats.laws.discipline.arbitrary.*
// import cats.xml.testing.codec.arbitrary.*
//
// implicit def eqFreeCursor[I, O]: Eq[FreeCursor[I, O]] = Eq.allEqual
// implicit def eqFreeCursor[O]: Eq[FreeCursor[O]] = Eq.allEqual
//
// checkAll(
// "FreeCursor.ApplicativeErrorTests",
// ApplicativeErrorTests[FreeCursor[XmlNode, *], NonEmptyList[CursorFailure]]
// ApplicativeErrorTests[FreeCursor, NonEmptyList[CursorFailure]]
// .applicativeError[Int, Int, String]
// )
//}
4 changes: 3 additions & 1 deletion docs/compiled/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,15 @@ val encoder: Encoder[Foo] = Encoder.of(t =>
```

### Navigating

```scala
import cats.xml.XmlNode
import cats.xml.cursor.Cursor
import cats.xml.cursor.FreeCursor
import cats.xml.implicits.*

val node = xml"""
val node =
xml"""
<wrapper>
<root>
<foo>1</foo>
Expand Down
4 changes: 3 additions & 1 deletion docs/source/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,15 @@ val encoder: Encoder[Foo] = Encoder.of(t =>
```

### Navigating

```scala mdoc:reset
import cats.xml.XmlNode
import cats.xml.cursor.Cursor
import cats.xml.cursor.FreeCursor
import cats.xml.implicits.*

val node = xml"""
val node =
xml"""
<wrapper>
<root>
<foo>1</foo>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cats.xml.generic

import cats.data.NonEmptyList
import cats.xml.XmlNode
import cats.xml.codec.{Decoder, DecoderFailure}
import cats.xml.cursor.{CursorFailure, FreeCursor}
import cats.xml.{Xml, XmlNode}
import cats.xml.utils.generic.ParamName
import magnolia1.{CaseClass, Param, SealedTrait, Subtype}

Expand Down Expand Up @@ -34,7 +34,7 @@ object MagnoliaDecoder {
val normalizedLabel: String = paramInfo.labelMapper(param.label)

// find and decoder element
val result: Option[FreeCursor[Xml, param.PType]] = paramInfo.elemType match {
val result: Option[FreeCursor[param.PType]] = paramInfo.elemType match {
case XmlElemType.Attribute => Some(c.attr(normalizedLabel).as[param.PType])
case XmlElemType.Child => Some(c.down(normalizedLabel).as[param.PType])
case XmlElemType.Text => Some(c.text.as[param.PType])
Expand Down Expand Up @@ -90,11 +90,11 @@ object MagnoliaDecoder {
// Internal error: unable to find the outer accessor symbol of class $read
private def useDefaultParameterIfPresentToRecoverMissing[F[_], T, PT](
param: Param[F, T]
): PartialFunction[NonEmptyList[CursorFailure], FreeCursor[Xml, PT]] = { failures =>
): PartialFunction[NonEmptyList[CursorFailure], FreeCursor[PT]] = { failures =>
if (failures.forall(_.isMissing))
param.default match {
case Some(value) =>
FreeCursor.const[Xml, PT](value.asInstanceOf[PT].validNel[CursorFailure])
FreeCursor.const[PT](value.asInstanceOf[PT].validNel[CursorFailure])
case None => FreeCursor.failure(failures)
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
// val normalizedLabel: String = paramInfo.labelMapper(param.label)
//
// // find and decoder element
// val result: Option[FreeCursor[Xml, param.PType]] = paramInfo.elemType match {
// val result: Option[FreeCursor[param.PType]] = paramInfo.elemType match {
// case XmlElemType.Attribute => Some(c.attr(normalizedLabel).as[param.PType])
// case XmlElemType.Child => Some(c.down(normalizedLabel).as[param.PType])
// case XmlElemType.Text => Some(c.text.as[param.PType])
Expand Down Expand Up @@ -82,7 +82,7 @@
// // Internal error: unable to find the outer accessor symbol of class $read
// private def useDefaultParameterIfPresentToRecoverMissing[F[_], T, PT](
// param: Param[F, T]
// ): PartialFunction[NonEmptyList[CursorFailure], FreeCursor[Xml, PT]] = { failures =>
// ): PartialFunction[NonEmptyList[CursorFailure], FreeCursor[PT]] = { failures =>
// if (failures.forall(_.isMissing))
// param.default match {
// case Some(value) =>
Expand Down
Loading