diff --git a/cats/jvm/src/test/scala/com/flowtick/graphs/cat/GraphCatsSpec.scala b/cats/jvm/src/test/scala/com/flowtick/graphs/cat/GraphCatsSpec.scala index 0e9fc8e..8095465 100644 --- a/cats/jvm/src/test/scala/com/flowtick/graphs/cat/GraphCatsSpec.scala +++ b/cats/jvm/src/test/scala/com/flowtick/graphs/cat/GraphCatsSpec.scala @@ -3,10 +3,11 @@ package com.flowtick.graphs.cat import com.flowtick.graphs._ import com.flowtick.graphs.defaults._ import cats.implicits._ +import org.scalatest.diagrams.Diagrams import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class GraphCatsSpec extends AnyFlatSpec with Matchers { +class GraphCatsSpec extends AnyFlatSpec with Matchers with Diagrams { import com.flowtick.graphs.cat.instances._ "Graph Monoid" should "combine graphs" in { diff --git a/cats/shared/src/main/scala/com/flowtick/graphs/cat/package.scala b/cats/shared/src/main/scala/com/flowtick/graphs/cat/package.scala index f7a871d..d5baec8 100644 --- a/cats/shared/src/main/scala/com/flowtick/graphs/cat/package.scala +++ b/cats/shared/src/main/scala/com/flowtick/graphs/cat/package.scala @@ -12,17 +12,19 @@ package object cat { * @tparam N * node type */ - class GraphMonoid[E, N](nodeId: Identifiable[N]) extends Monoid[Graph[E, N]] { - override def empty: Graph[E, N] = Graph.empty[E, N](nodeId) + class GraphMonoid[E, N](nodeId: Identifiable[N], edgeId: Identifiable[E]) + extends Monoid[Graph[E, N]] { + override def empty: Graph[E, N] = Graph.empty[E, N](nodeId, edgeId) override def combine(x: Graph[E, N], y: Graph[E, N]): Graph[E, N] = (x.edges ++ y.edges) - .foldLeft(Graph.empty[E, N](nodeId))(_ withEdge _) + .foldLeft(Graph.empty[E, N](nodeId, edgeId))(_ withEdge _) .withNodes(x.nodes ++ y.nodes) } object GraphMonoid { - def apply[E, N](implicit nodeId: Identifiable[N]) = new GraphMonoid[E, N](nodeId) + def apply[E, N](implicit nodeId: Identifiable[N], edgeId: Identifiable[E]) = + new GraphMonoid[E, N](nodeId, edgeId) } class GraphNodeApplicative[E](implicit id: Identifiable[Any]) extends Applicative[Graph[E, *]] { @@ -30,24 +32,22 @@ package object cat { override def ap[A, B](functionGraph: Graph[E, A => B])(graph: Graph[E, A]): Graph[E, B] = GraphMonoid[E, B].combineAll(graph.nodes.map(node => { - Graph - .empty[E, B] - .addNodes( - functionGraph.nodes - .filter(node => graph.outgoing(node.id).isEmpty && graph.incoming(node.id).isEmpty) - .map(_.value(node.value)) - ) - .withEdges( - functionGraph.nodes.flatMap(f => - graph - .outgoing(node.id) - .flatMap(edge => { - for { - toNode <- graph.findNode(edge.to) - } yield Edge.of(edge.value, id(f.value(node.value)), id(f.value(toNode.value))) - }) - ) - ) + functionGraph.nodes.foldLeft(Graph.empty[E, B]) { case (acc, f) => + graph.outgoing(node.id).foldLeft(acc) { case (result, edge) => + val fromNode = f.value(node.value) + graph + .findNode(edge.to) + .map(existingToNode => { + val toNode = f.value(existingToNode.value) + + result + .addNode(fromNode) + .addNode(toNode) + .withEdgeValue(edge.value, id(fromNode), id(toNode)) + }) + .getOrElse(result) + } + } })) } @@ -96,7 +96,10 @@ package object cat { } trait GraphInstances { - implicit def graphMonoid[E, N](implicit nodeId: Identifiable[N]): Monoid[Graph[E, N]] = + implicit def graphMonoid[E, N](implicit + nodeId: Identifiable[N], + edgeId: Identifiable[E] + ): Monoid[Graph[E, N]] = GraphMonoid[E, N] implicit def graphNodeApplicative[E](implicit id: Identifiable[Any]): Applicative[Graph[E, *]] = GraphApplicative[E] diff --git a/core/jvm/src/test/scala/com/flowtick/graphs/GraphSpec.scala b/core/jvm/src/test/scala/com/flowtick/graphs/GraphSpec.scala index 69c3bd8..20ac9ba 100644 --- a/core/jvm/src/test/scala/com/flowtick/graphs/GraphSpec.scala +++ b/core/jvm/src/test/scala/com/flowtick/graphs/GraphSpec.scala @@ -91,7 +91,7 @@ class GraphSpec extends AnyFlatSpec with Matchers { it should "have nodes after adding an edge" in { val intGraph = - Graph.empty[Option[Unit], Int].withEdgeValue(None, Node.of(1), Node.of(2)) + Graph.empty[Option[Unit], Int].addEdge(None, 1, 2) intGraph.nodes should contain theSameElementsAs List( Node("1", 1), Node("2", 2) @@ -109,7 +109,7 @@ class GraphSpec extends AnyFlatSpec with Matchers { .addNode(node3) intGraph.removeNodeValue(node3) should be( - Graph.empty[Unit, Int].withEdgeValue((), Node.of(1), Node.of(2)) + Graph.empty[Unit, Int].addEdge((), 1, 2) ) val expected = Graph @@ -123,8 +123,8 @@ class GraphSpec extends AnyFlatSpec with Matchers { it should "remove edges" in { val intGraph = Graph .empty[Unit, Int] - .withEdgeValue((), Node.of(1), Node.of(2)) - .withNode(Node.of(3)) + .addEdge((), 1, 2) + .addNode(3) val expected = Graph .empty[Unit, Int] diff --git a/core/shared/src/main/scala/com/flowtick/graphs/Graph.scala b/core/shared/src/main/scala/com/flowtick/graphs/Graph.scala index c3d7d43..5a52cd1 100644 --- a/core/shared/src/main/scala/com/flowtick/graphs/Graph.scala +++ b/core/shared/src/main/scala/com/flowtick/graphs/Graph.scala @@ -63,16 +63,17 @@ final case class Relation[+E, +N]( to: Node[N], symmetric: Boolean = false ) { - def toEdges: Iterable[Edge[E]] = + def toEdges(implicit id: Identifiable[E]): Iterable[Edge[E]] = if (symmetric) Iterable(Edge.of(value, from.id, to.id), Edge.of(value, to.id, from.id)) else Iterable(Edge.of(value, from.id, to.id)) } object Edge { - def of[E, N](value: E, from: String, to: String): Edge[E] = - Edge(s"$from-$to", value, from, to) - def unit[N](from: String, to: String): Edge[Unit] = Edge.of((), from, to) + def of[E](value: E, from: String, to: String)(implicit id: Identifiable[E]): Edge[E] = + Edge(s"$from-${id(value)}-$to", value, from, to) + def unit(from: String, to: String)(implicit id: Identifiable[Unit]): Edge[Unit] = + Edge.of((), from, to) } final case class Node[+N](id: String, value: N) { @@ -88,6 +89,7 @@ object Node { private[graphs] final case class GraphInstance[E, N]( nodeId: Identifiable[N], + edgeId: Identifiable[E], nodesById: scala.collection.Map[String, Node[N]] = scala.collection.immutable.TreeMap.empty[String, Node[N]], incomingById: scala.collection.Map[String, Set[String]] = @@ -195,6 +197,7 @@ private[graphs] final case class GraphInstance[E, N]( // #graph trait Graph[E, N] { def nodeId: Identifiable[N] + def edgeId: Identifiable[E] def edges: Iterable[Edge[E]] def nodes: Iterable[Node[N]] @@ -228,15 +231,15 @@ trait Graph[E, N] { def addNodes(nodeValues: Iterable[N]): Graph[E, N] = nodeValues.foldLeft(this)(_ addNode _) def addEdge(value: E, from: N, to: N): Graph[E, N] = - withEdge(Edge.of(value, nodeId(from), nodeId(to))) + withEdge(Edge.of(value, nodeId(from), nodeId(to))(edgeId)) .addNode(from) .addNode(to) def withEdge(edge: Edge[E]): Graph[E, N] def withNode(node: Node[N]): Graph[E, N] - def withEdgeValue(value: E, from: Node[N], to: Node[N]): Graph[E, N] = - withEdge(Edge.of(value, from.id, to.id)).withNode(from).withNode(to) + def withEdgeValue(value: E, fromId: String, toId: String): Graph[E, N] = + withEdge(Edge.of(value, fromId, toId)(edgeId)) def withNodes(nodes: Iterable[Node[N]]): Graph[E, N] = nodes.foldLeft(this)(_ withNode _) @@ -251,10 +254,11 @@ object Graph { def apply[E, N]( edges: Iterable[Edge[E]] = Iterable.empty, nodes: Iterable[Node[N]] = Iterable.empty - )(implicit nodeId: Identifiable[N]): Graph[E, N] = + )(implicit nodeId: Identifiable[N], edgeId: Identifiable[E]): Graph[E, N] = empty[E, N].withEdges(edges).withNodes(nodes) - def empty[E, N](implicit nodeId: Identifiable[N]): Graph[E, N] = GraphInstance[E, N](nodeId) + def empty[E, N](implicit nodeId: Identifiable[N], edgeId: Identifiable[E]): Graph[E, N] = + GraphInstance[E, N](nodeId, edgeId) /** utility method to create a unit typed graph from iterable relations * @@ -269,7 +273,7 @@ object Graph { */ def fromEdges[E, N]( relations: Iterable[Relation[E, N]] - )(implicit nodeId: Identifiable[N]): Graph[E, N] = + )(implicit nodeId: Identifiable[N], edgeId: Identifiable[E]): Graph[E, N] = relations.foldLeft(empty[E, N]) { (acc, relation) => acc .withNode(relation.from) diff --git a/core/shared/src/main/scala/com/flowtick/graphs/defaults/package.scala b/core/shared/src/main/scala/com/flowtick/graphs/defaults/package.scala index 8471097..a676eaf 100644 --- a/core/shared/src/main/scala/com/flowtick/graphs/defaults/package.scala +++ b/core/shared/src/main/scala/com/flowtick/graphs/defaults/package.scala @@ -8,6 +8,14 @@ package object defaults { implicit val identifiableInt: Identifiable[Int] = Identifiable.identify(int => int.toString) + private final case class IdentifiableOption[T](id: Identifiable[T]) + extends Identifiable[Option[T]] { + override def apply(value: Option[T]): String = value.map(id(_)).getOrElse("none") + } + + implicit def identifiableOption[T](implicit id: Identifiable[T]): Identifiable[Option[T]] = + IdentifiableOption(id) + object id { implicit val identifyAny: Identifiable[Any] = (value: Any) => value.toString } diff --git a/editor/shared/src/main/scala/com/flowtick/graphs/editor/EditorGraph.scala b/editor/shared/src/main/scala/com/flowtick/graphs/editor/EditorGraph.scala index 5b9701b..776bd79 100644 --- a/editor/shared/src/main/scala/com/flowtick/graphs/editor/EditorGraph.scala +++ b/editor/shared/src/main/scala/com/flowtick/graphs/editor/EditorGraph.scala @@ -57,6 +57,10 @@ object EditorGraphNode { } object EditorGraphEdge { + implicit val identifiableEditorEdge = new Identifiable[EditorGraphEdge] { + override def apply(value: EditorGraphEdge): String = value.id + } + implicit val editorEdgeStyleRef: StyleRef[Edge[EditorGraphEdge]] = new StyleRef[Edge[EditorGraphEdge]] { override def id(element: Edge[EditorGraphEdge]): Option[String] = Some( diff --git a/editor/shared/src/main/scala/com/flowtick/graphs/editor/EditorModel.scala b/editor/shared/src/main/scala/com/flowtick/graphs/editor/EditorModel.scala index 71c1688..302a4a5 100644 --- a/editor/shared/src/main/scala/com/flowtick/graphs/editor/EditorModel.scala +++ b/editor/shared/src/main/scala/com/flowtick/graphs/editor/EditorModel.scala @@ -5,7 +5,7 @@ import cats.data.ValidatedNel import com.flowtick.graphs.Graph import com.flowtick.graphs.graphml.{Datatype, GraphMLKey} import com.flowtick.graphs.json.schema.Schema -import com.flowtick.graphs.layout.{GraphLayout, GraphLayoutLike, GraphLayouts} +import com.flowtick.graphs.layout.{GraphLayoutLike, GraphLayouts} import com.flowtick.graphs.style._ import io.circe.Json diff --git a/graphml/jvm/src/test/scala/com/flowtick/graphs/graphml/GraphMLDatatypeSpec.scala b/graphml/jvm/src/test/scala/com/flowtick/graphs/graphml/GraphMLDatatypeSpec.scala index 1d88ca2..4953ae5 100644 --- a/graphml/jvm/src/test/scala/com/flowtick/graphs/graphml/GraphMLDatatypeSpec.scala +++ b/graphml/jvm/src/test/scala/com/flowtick/graphs/graphml/GraphMLDatatypeSpec.scala @@ -199,7 +199,9 @@ class GraphMLDatatypeSpec extends AnyFlatSpec with Matchers { ) should contain theSameElementsAs graphML.graph.nodes.map(_.id) parsedGraph.graph.edges.map( _.id - ) should contain theSameElementsAs graphML.graph.edges.map(_.id) + ) should contain theSameElementsAs graphML.graph.edges.map { edge => + s"${edge.from}-${edge.to}" + } case Left(errors) => fail(s"parsing errors ${errors.toString}") } diff --git a/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/GraphMLDatatype.scala b/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/GraphMLDatatype.scala index da82e1e..cc13d08 100644 --- a/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/GraphMLDatatype.scala +++ b/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/GraphMLDatatype.scala @@ -41,8 +41,11 @@ final case class GraphMLProperty( class GraphMLDatatype[E, N]( nodeDataType: Datatype[GraphMLNode[N]], edgeDataType: Datatype[GraphMLEdge[E]] -)(implicit edgeLabel: Labeled[Edge[GraphMLEdge[E]], String]) - extends Datatype[GraphMLGraph[E, N]] { +)(implicit + edgeLabel: Labeled[Edge[GraphMLEdge[E]], String], + nodeId: Identifiable[GraphMLNode[N]], + edgeId: Identifiable[GraphMLEdge[E]] +) extends Datatype[GraphMLGraph[E, N]] { val nodeTargetHint = Some("node") val edgeTargetHint = Some("edge") val metaTargetHint = Some("meta") @@ -130,7 +133,7 @@ class GraphMLDatatype[E, N]( resources: Seq[GraphMLResource] ): Validated[NonEmptyList[Throwable], GraphMLGraph[E, N]] = parseGraphNodes(graph, graphKeys).andThen { parsedGraph => - parseEdges(parsedGraph.edgesXml, parsedGraph.nodes, graphKeys).andThen { edges => + parseEdges(parsedGraph.edgesXml, graphKeys).andThen { edges => valid( GraphMLGraph( Graph(edges, parsedGraph.nodes.values), @@ -142,7 +145,6 @@ class GraphMLDatatype[E, N]( protected def parseEdges( edgeXmlNodes: List[scala.xml.Node], - nodes: scala.collection.Map[String, Node[GraphMLNode[N]]], keys: scala.collection.Map[String, GraphMLKey] ): Validated[NonEmptyList[Throwable], List[Edge[GraphMLEdge[E]]]] = { edgeXmlNodes.map { edgeNode => @@ -224,7 +226,8 @@ class GraphMLDatatype[E, N]( object GraphMLDatatype { def apply[E, N](implicit - identifiable: Identifiable[GraphMLNode[N]], + nodeId: Identifiable[GraphMLNode[N]], + edgeId: Identifiable[GraphMLEdge[E]], edgeLabel: Labeled[Edge[GraphMLEdge[E]], String], nodeDataType: Datatype[N], edgeDataType: Datatype[E] diff --git a/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/GraphMLGraph.scala b/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/GraphMLGraph.scala index 6ae7c99..71c6681 100644 --- a/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/GraphMLGraph.scala +++ b/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/GraphMLGraph.scala @@ -1,7 +1,7 @@ package com.flowtick.graphs.graphml import com.flowtick.graphs.layout.{DefaultGeometry, EdgePath, Geometry} -import com.flowtick.graphs.{Edge, Graph, Node, Relation} +import com.flowtick.graphs.{Edge, Graph, Identifiable, Node, Relation} import com.flowtick.graphs.style._ final case class GraphMLKey( @@ -96,7 +96,10 @@ final case class GraphMLMeta( ) object GraphML { - def empty[E, N]: GraphMLGraph[E, N] = GraphMLGraph[E, N]( + def empty[E, N](implicit + nodeId: Identifiable[GraphMLNode[N]], + edgeId: Identifiable[GraphMLEdge[E]] + ): GraphMLGraph[E, N] = GraphMLGraph[E, N]( Graph.empty[GraphMLEdge[E], GraphMLNode[N]], GraphMLMeta() ) @@ -106,12 +109,18 @@ object GraphML { edges: Iterable[Edge[GraphMLEdge[E]]], nodes: Iterable[Node[GraphMLNode[N]]] = Iterable.empty, keys: Seq[GraphMLKey] = Seq.empty + )(implicit + nodeId: Identifiable[GraphMLNode[N]], + edgeId: Identifiable[GraphMLEdge[E]] ): GraphMLGraph[E, N] = { GraphMLGraph(Graph(edges = edges, nodes = nodes), GraphMLMeta(keys = keys)) } def fromEdges[E, N]( edges: Iterable[Relation[GraphMLEdge[E], GraphMLNode[N]]] + )(implicit + nodeId: Identifiable[GraphMLNode[N]], + edgeId: Identifiable[GraphMLEdge[E]] ): GraphMLGraph[E, N] = GraphMLGraph(Graph.fromEdges(edges), GraphMLMeta()) } diff --git a/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/package.scala b/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/package.scala index 0bb7eaa..49cacbb 100644 --- a/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/package.scala +++ b/graphml/shared/src/main/scala/com/flowtick/graphs/graphml/package.scala @@ -10,7 +10,7 @@ import xmls.XMLS import scala.collection.GenTraversable import scala.reflect.ClassTag import scala.util.{Either, Left, Right} -import scala.xml.{Elem, NodeSeq, Text} +import scala.xml.{Elem, NodeSeq} package object graphml { trait Serializer[T] { @@ -333,7 +333,8 @@ package object graphml { } implicit def graphMLDataType[E, N](implicit - identifiable: Identifiable[GraphMLNode[N]], + nodeId: Identifiable[GraphMLNode[N]], + edgeId: Identifiable[GraphMLEdge[E]], edgeLabel: Labeled[Edge[GraphMLEdge[E]], String], nodeDataType: Datatype[N], edgeDataType: Datatype[E] @@ -362,6 +363,9 @@ package object graphml { implicit def graphMLNodeIdentifiable[N]: Identifiable[GraphMLNode[N]] = (node: GraphMLNode[N]) => node.id + implicit def graphMLEdgeIdentifiable[E]: Identifiable[GraphMLEdge[E]] = + (edge: GraphMLEdge[E]) => edge.id + implicit def graphMLEdgeLabel[V, N]: Labeled[Edge[GraphMLEdge[V]], String] = (edge: Edge[GraphMLEdge[V]]) => edge.value.id @@ -443,7 +447,7 @@ package object graphml { labelValue = edgeLabel(edge) ) - Edge.of(mlEdge, edge.from, edge.to) + Edge(edge.id, mlEdge, edge.from, edge.to) } GraphMLGraph(Graph(edges = mlEdges, nodes = mlNodes), GraphMLMeta()) diff --git a/json/jvm/src/test/scala/com/flowtick/graphs/json/JsonSpec.scala b/json/jvm/src/test/scala/com/flowtick/graphs/json/JsonSpec.scala index a5ccb32..a4ca240 100644 --- a/json/jvm/src/test/scala/com/flowtick/graphs/json/JsonSpec.scala +++ b/json/jvm/src/test/scala/com/flowtick/graphs/json/JsonSpec.scala @@ -5,10 +5,11 @@ import com.flowtick.graphs.{Graph, Node} import io.circe._ import io.circe.syntax._ import io.circe.parser._ +import org.scalatest.diagrams.Diagrams import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -class JsonSpec extends AnyFlatSpec with Matchers { +class JsonSpec extends AnyFlatSpec with Matchers with Diagrams { val numberGraph: Graph[Unit, Int] = Graph.fromEdges(Set(1 --> 2)).withNode(Node.of(3)) @@ -40,7 +41,7 @@ class JsonSpec extends AnyFlatSpec with Matchers { | ], | "edges" : [ | { - | "id" : "1-2", + | "id" : "1-()-2", | "value" : null, | "from" : "1", | "to" : "2" @@ -75,7 +76,7 @@ class JsonSpec extends AnyFlatSpec with Matchers { import com.flowtick.graphs.json.format.default._ val parsed = - Graph.empty[Unit, Int].withEdgeValue((), Node("1", 1), Node("2", 2)).asJson + Graph.empty[Unit, Int].addEdge((), 1, 2).asJson val edgesJson = parsed.hcursor .downField("edges") @@ -92,27 +93,31 @@ class JsonSpec extends AnyFlatSpec with Matchers { val emptyGraph = s""" |{ | "nodes": [1,2], - | "edges": [{ "id": "1-2", "from": "1", "to": "2"}] + | "edges": [{ "id": "1-none-2", "from": "1", "to": "2"}] |} |""".stripMargin import com.flowtick.graphs.json.format.embedded._ - val parsed = decode[Graph[Option[Unit], Int]](emptyGraph) - parsed should be( - Right(Graph.empty[Option[Unit], Int].withEdgeValue(None, Node.of(1), Node.of(2))) - ) + val expected = Graph.empty[Option[Unit], Int].addEdge(None, 1, 2) + + decode[Graph[Option[Unit], Int]](emptyGraph) match { + case Right(parsed) => + println("foo") + parsed.edgeId should be(expected.edgeId) + parsed should equal(expected) + } } it should "create embedded graph json" in { val expectedGraph = s""" |{ | "nodes": [1,2], - | "edges": [{ "id": "1-2", "from": "1", "to": "2", "value": null}] + | "edges": [{ "id": "1-none-2", "from": "1", "to": "2", "value": null}] |} |""".stripMargin import com.flowtick.graphs.json.format.embedded._ - val json = Graph.empty[Option[Unit], Int].withEdgeValue(None, Node.of(1), Node.of(2)).asJson + val json = Graph.empty[Option[Unit], Int].addEdge(None, 1, 2).asJson io.circe.parser.decode[Json](expectedGraph) match { case Right(expectedJson) => expectedJson.spaces2 should be(json.spaces2) diff --git a/json/shared/src/main/scala/com/flowtick/graphs/json/package.scala b/json/shared/src/main/scala/com/flowtick/graphs/json/package.scala index 6024417..573abd2 100644 --- a/json/shared/src/main/scala/com/flowtick/graphs/json/package.scala +++ b/json/shared/src/main/scala/com/flowtick/graphs/json/package.scala @@ -17,23 +17,23 @@ package object json { import io.circe.generic.semiauto._ - implicit def nodeEncoder[N](implicit + implicit def wrappedNodeEncoder[N](implicit nodeEncoder: Encoder[N] ): Encoder[Node[N]] = deriveEncoder[Node[N]] - implicit def nodeDecoder[N](implicit + implicit def wrappedNodeDecoder[N](implicit nodeDecoder: Decoder[N] ): Decoder[Node[N]] = deriveDecoder[Node[N]] - implicit def edgeEncoder[E, N](implicit + implicit def wrappedEdgeEncoder[E, N](implicit edgeEncoder: Encoder[E] ): Encoder[Edge[E]] = graphsEdgeEncoder[E, N] - implicit def edgeDecoder[E, N](implicit + implicit def wrappedEdgeDecoder[E, N](implicit edgeDecoder: Decoder[E] ): Decoder[Edge[E]] = deriveDecoder[Edge[E]] implicit def defaultGraphEncoder[E, N](implicit - nodesEncoder: Encoder[Node[N]], - edgesEncoder: Encoder[Edge[E]] + nodeEncoder: Encoder[Node[N]], + edgeEncoder: Encoder[Edge[E]] ): Encoder[Graph[E, N]] = new Encoder[Graph[E, N]] { override def apply(a: Graph[E, N]): Json = { val fields = new ListBuffer[(String, Json)] @@ -44,9 +44,10 @@ package object json { } implicit def defaultGraphDecoder[E, N](implicit - nodesDecoder: Decoder[Node[N]], + nodeDecoder: Decoder[Node[N]], edgeDecoder: Decoder[Edge[E]], - nodeId: Identifiable[N] + nodeId: Identifiable[N], + edgeId: Identifiable[E] ): Decoder[Graph[E, N]] = new Decoder[Graph[E, N]] { override def apply(c: HCursor): Result[Graph[E, N]] = for { nodes <- c @@ -87,16 +88,17 @@ package object json { nodeDecoder: Decoder[N] ): Decoder[Node[N]] = deriveDecoder[Node[N]] - implicit def edgeEncoder[E, N](implicit + implicit def wrappedEdgeEncoder[E, N](implicit edgeEncoder: Encoder[E] ): Encoder[Edge[E]] = graphsEdgeEncoder[E, N] - implicit def edgeDecoder[E, N](implicit + + implicit def wrappedEdgeDecoder[E, N](implicit edgeDecoder: Decoder[E] ): Decoder[Edge[E]] = deriveDecoder[Edge[E]] implicit def embeddedGraphEncoder[E, N](implicit - nodesEncoder: Encoder[N], - edgesEncoder: Encoder[Edge[E]] + nodeEncoder: Encoder[N], + edgeEncoder: Encoder[E] ): Encoder[Graph[E, N]] = new Encoder[Graph[E, N]] { override def apply(a: Graph[E, N]): Json = Json .obj( @@ -107,10 +109,10 @@ package object json { } implicit def embeddedGraphDecoder[E, N](implicit - nodesDecoder: Decoder[N], + nodeDecoder: Decoder[N], edgeDecoder: Decoder[E], - edgesDecoder: Decoder[Edge[E]], - nodeId: Identifiable[N] + nodeId: Identifiable[N], + edgeId: Identifiable[E] ): Decoder[Graph[E, N]] = new Decoder[Graph[E, N]] { override def apply(c: HCursor): Result[Graph[E, N]] = for { nodes <- c.downField("nodes").as[List[N]]