@@ -14,7 +14,7 @@ import scala.language.implicitConversions
14
14
* @tparam A
15
15
* type of the error that this PathSegment produces on "illegal" url paths.
16
16
*/
17
- trait PathSegment [T , + A ] extends UrlPart [T , A ] {
17
+ trait PathSegment [T , A ] extends UrlPart [T , A ] {
18
18
19
19
/** Tries to match the list of [[urldsl.vocabulary.Segment ]]s to create an instance of `T`. If it can not, it returns
20
20
* an error indicating the reason of the failure. If it could, it returns the value of `T`, as well as the list of
@@ -31,6 +31,30 @@ trait PathSegment[T, +A] extends UrlPart[T, A] {
31
31
*/
32
32
def matchSegments (segments : List [Segment ]): Either [A , PathMatchOutput [T ]]
33
33
34
+ protected implicit def errorImpl : PathMatchingError [A ]
35
+
36
+ /** Tries to match the provided list of [[urldsl.vocabulary.Segment ]]s to create an instance of `T`.
37
+ *
38
+ * If it can't, it returns an error indicating the reason of the failure. If it can but there are unused segments
39
+ * left over, it fails with a `endOfSegmentRequired` error. If it can, it returns the output.
40
+ *
41
+ * It is thus similar to [[matchSegments ]], but requiring that all segments have been consumed.
42
+ *
43
+ * @see
44
+ * [[matchSegments ]]
45
+ *
46
+ * @param segments
47
+ * The list of [[urldsl.vocabulary.Segment ]] to match this path segment again.
48
+ * @return
49
+ */
50
+ def matchFullSegments (segments : List [Segment ]): Either [A , T ] = for {
51
+ matchOuptput <- matchSegments(segments)
52
+ t <- matchOuptput.unusedSegments match {
53
+ case Nil => Right (matchOuptput.output)
54
+ case segments => Left (errorImpl.endOfSegmentRequired(segments))
55
+ }
56
+ } yield t
57
+
34
58
/** Matches the given raw `url` using the given [[urldsl.url.UrlStringParserGenerator ]] for creating a
35
59
* [[urldsl.url.UrlStringParser ]].
36
60
*
@@ -52,10 +76,10 @@ trait PathSegment[T, +A] extends UrlPart[T, A] {
52
76
url : String ,
53
77
urlStringParserGenerator : UrlStringParserGenerator = UrlStringParserGenerator .defaultUrlStringParserGenerator
54
78
): Either [A , T ] =
55
- matchSegments (urlStringParserGenerator.parser(url).segments).map(_.output )
79
+ matchFullSegments (urlStringParserGenerator.parser(url).segments)
56
80
57
81
def matchPath (path : String , decoder : UrlStringDecoder = UrlStringDecoder .defaultDecoder): Either [A , T ] =
58
- matchSegments (decoder.decodePath(path)).map(_.output )
82
+ matchFullSegments (decoder.decodePath(path))
59
83
60
84
/** Generate a list of segments representing the argument `t`.
61
85
*
@@ -85,8 +109,8 @@ trait PathSegment[T, +A] extends UrlPart[T, A] {
85
109
/** Concatenates `this` [[urldsl.language.PathSegment ]] with `that` one, "tupling" the types with the [[Composition ]]
86
110
* rules.
87
111
*/
88
- final def / [U , A1 >: A ](that : PathSegment [U , A1 ])(implicit c : Composition [T , U ]): PathSegment [c.Composed , A1 ] =
89
- PathSegment .factory[c.Composed , A1 ](
112
+ final def / [U ](that : PathSegment [U , A ])(implicit c : Composition [T , U ]): PathSegment [c.Composed , A ] =
113
+ PathSegment .factory[c.Composed , A ](
90
114
(segments : List [Segment ]) =>
91
115
for {
92
116
firstOut <- this .matchSegments(segments)
@@ -118,16 +142,16 @@ trait PathSegment[T, +A] extends UrlPart[T, A] {
118
142
* - in a multi-part segment, ensure consistency between the different component (e.g., a range of two integers
119
143
* that should not be too large...)
120
144
*/
121
- final def filter [ A1 >: A ] (predicate : T => Boolean , error : List [Segment ] => A1 ): PathSegment [T , A1 ] =
122
- PathSegment .factory[T , A1 ](
145
+ final def filter (predicate : T => Boolean , error : List [Segment ] => A ): PathSegment [T , A ] =
146
+ PathSegment .factory[T , A ](
123
147
(segments : List [Segment ]) =>
124
148
matchSegments(segments)
125
149
.filterOrElse(((_ : PathMatchOutput [T ]).output).andThen(predicate), error(segments)),
126
150
createSegments
127
151
)
128
152
129
153
/** Sugar for when `A =:= DummyError` */
130
- final def filter (predicate : T => Boolean )(implicit ev : A <:< DummyError ): PathSegment [T , DummyError ] = {
154
+ final def filter (predicate : T => Boolean )(implicit ev : A =:= DummyError ): PathSegment [T , DummyError ] = {
131
155
// type F[+E] = PathSegment[T, E]
132
156
// ev.liftCo[F].apply(this).filter(predicate, _ => DummyError.dummyError)
133
157
// we keep the ugliness below while supporting 2.12 todo[scala3] remove this
@@ -137,8 +161,8 @@ trait PathSegment[T, +A] extends UrlPart[T, A] {
137
161
/** Builds a [[PathSegment ]] that first tries to match with this one, then tries to match with `that` one. If both
138
162
* fail, the error of the second is returned (todo[behaviour]: should that change?)
139
163
*/
140
- final def || [U , A1 >: A ](that : PathSegment [U , A1 ]): PathSegment [Either [T , U ], A1 ] =
141
- PathSegment .factory[Either [T , U ], A1 ](
164
+ final def || [U ](that : PathSegment [U , A ]): PathSegment [Either [T , U ], A ] =
165
+ PathSegment .factory[Either [T , U ], A ](
142
166
segments =>
143
167
this .matchSegments(segments) match {
144
168
case Right (output) => Right (PathMatchOutput (Left (output.output), output.unusedSegments))
@@ -169,19 +193,21 @@ trait PathSegment[T, +A] extends UrlPart[T, A] {
169
193
(_ : Unit ) => createSegments(default)
170
194
)
171
195
196
+ final def ignoreRemaining : PathSegment [T , A ] = this / PathSegment .remainingSegments.ignore(Nil )
197
+
172
198
/** Forgets the information contained in the path parameter by injecting one. This turn this "dynamic" [[PathSegment ]]
173
199
* into a fix one.
174
200
*/
175
- final def provide [ A1 >: A ] (
201
+ final def provide (
176
202
t : T
177
- )(implicit pathMatchingError : PathMatchingError [ A1 ], printer : Printer [T ]): PathSegment [Unit , A1 ] =
178
- PathSegment .factory[Unit , A1 ](
203
+ )(implicit printer : Printer [T ]): PathSegment [Unit , A ] =
204
+ PathSegment .factory[Unit , A ](
179
205
segments =>
180
206
for {
181
207
tMatch <- matchSegments(segments)
182
208
PathMatchOutput (tOutput, unusedSegments) = tMatch
183
209
unitMatched <-
184
- if (tOutput != t) Left (pathMatchingError .wrongValue(printer(t), printer(tOutput)))
210
+ if (tOutput != t) Left (errorImpl .wrongValue(printer(t), printer(tOutput)))
185
211
else Right (PathMatchOutput ((), unusedSegments))
186
212
} yield unitMatched,
187
213
(_ : Unit ) => createSegments(t)
@@ -195,7 +221,11 @@ trait PathSegment[T, +A] extends UrlPart[T, A] {
195
221
final def withFragment [FragmentType , FragmentError ](
196
222
fragment : Fragment [FragmentType , FragmentError ]
197
223
): PathQueryFragmentRepr [T , A , Unit , Nothing , FragmentType , FragmentError ] =
198
- new PathQueryFragmentRepr (this , QueryParameters .ignore, fragment)
224
+ new PathQueryFragmentRepr [T , A , Unit , Nothing , FragmentType , FragmentError ](
225
+ this ,
226
+ QueryParameters .ignore,
227
+ fragment
228
+ )
199
229
200
230
}
201
231
@@ -211,16 +241,18 @@ object PathSegment {
211
241
def factory [T , A ](
212
242
matching : List [Segment ] => Either [A , PathMatchOutput [T ]],
213
243
creating : T => List [Segment ]
214
- ): PathSegment [T , A ] = new PathSegment [T , A ] {
244
+ )(implicit errors : PathMatchingError [A ]): PathSegment [T , A ] = new PathSegment [T , A ] {
245
+ protected def errorImpl : PathMatchingError [A ] = errors
246
+
215
247
def matchSegments (segments : List [Segment ]): Either [A , PathMatchOutput [T ]] = matching(segments)
216
248
217
249
def createSegments (t : T ): List [Segment ] = creating(t)
218
250
}
219
251
220
252
/** Simple path segment that matches everything by passing segments down the line. */
221
- final def empty : PathSegment [Unit , Nothing ] =
222
- factory[Unit , Nothing ](segments => Right (PathMatchOutput ((), segments)), _ => Nil )
223
- final def root : PathSegment [Unit , Nothing ] = empty
253
+ final def empty [ A ]( implicit pathMatchingError : PathMatchingError [ A ]) : PathSegment [Unit , A ] =
254
+ factory[Unit , A ](segments => Right (PathMatchOutput ((), segments)), _ => Nil )
255
+ final def root [ A ]( implicit pathMatchingError : PathMatchingError [ A ]) : PathSegment [Unit , A ] = empty
224
256
225
257
/** Simple path segment that matches nothing. This is the neutral of the || operator. */
226
258
final def noMatch [A ](implicit pathMatchingError : PathMatchingError [A ]): PathSegment [Unit , A ] =
@@ -274,10 +306,11 @@ object PathSegment {
274
306
*
275
307
* This can be useful for static resources.
276
308
*/
277
- final def remainingSegments [A ]: PathSegment [List [String ], A ] = factory[List [String ], A ](
278
- segments => Right (PathMatchOutput (segments.map(_.content), Nil )),
279
- _.map(Segment .apply)
280
- )
309
+ final def remainingSegments [A ](implicit pathMatchingError : PathMatchingError [A ]): PathSegment [List [String ], A ] =
310
+ factory[List [String ], A ](
311
+ segments => Right (PathMatchOutput (segments.map(_.content), Nil )),
312
+ _.map(Segment .apply)
313
+ )
281
314
282
315
/** [[PathSegment ]] that matches one of the given different possibilities.
283
316
*
0 commit comments