-
Notifications
You must be signed in to change notification settings - Fork 3
/
DirectiveAlg.scala
76 lines (69 loc) · 2.87 KB
/
DirectiveAlg.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/*
* Copyright 2023 Valdemar Grange
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gql.preparation
import gql.parser._
import cats._
import cats.implicits._
import gql._
class DirectiveAlg[F[_], C](
positions: Map[String, List[Position[F, ?]]],
ap: ArgParsing[C]
) {
type G[A] = Alg[C, A]
val G = Alg.Ops[C]
def parseArg[P[x] <: Position[F, x], A](p: P[A], args: Option[QueryAst.Arguments[C, AnyValue]], context: List[C]): G[A] = {
p.directive.arg match {
case EmptyableArg.Empty =>
args match {
case Some(_) => G.raise(s"Directive '${p.directive.name}' does not expect arguments", context)
case None => G.unit
}
case EmptyableArg.Lift(a) =>
val argFields = args.toList.flatMap(_.nel.toList).map(a => a.name -> a.value.map(List(_))).toMap
ap.decodeArg(a, argFields, ambigiousEnum = false, context)
}
}
def foldDirectives[P[x] <: Position[F, x]]: DirectiveAlg.PartiallyAppliedFold[F, C, P] =
new DirectiveAlg.PartiallyAppliedFold[F, C, P] {
override def apply[H[_]: Traverse, A](directives: Option[QueryAst.Directives[C, AnyValue]], context: List[C])(base: A)(
f: PartialFunction[(A, P[Any], QueryAst.Directive[C, AnyValue]), Alg[C, H[A]]]
)(implicit H: Monad[H]): Alg[C, H[A]] = {
def foldNext(rest: List[QueryAst.Directive[C, AnyValue]], accum: A): Alg[C, H[A]] =
rest match {
case Nil => G.pure(H.pure(accum))
case x :: xs =>
val name = x.name
positions.get(name) match {
case None => G.raise(s"Couldn't find directive '$name'", context)
case Some(d) =>
val faOpt = d.map(p => (accum, p, x)).collectFirst { case f(fa) => fa }
G.raiseOpt(faOpt, s"Directive '$name' cannot appear here", context)
.flatten
.flatMap(_.flatTraverse(a => foldNext(xs, a)))
}
}
foldNext(directives.map(_.nel.toList).getOrElse(Nil), base)
}
}
}
object DirectiveAlg {
trait PartiallyAppliedFold[F[_], C, P[x] <: Position[F, x]] {
def apply[H[_]: Traverse, A](
directives: Option[QueryAst.Directives[C, AnyValue]],
context: List[C]
)(base: A)(f: PartialFunction[(A, P[Any], QueryAst.Directive[C, AnyValue]), Alg[C, H[A]]])(implicit H: Monad[H]): Alg[C, H[A]]
}
}