|
@@ -11,16 +11,14 @@ open ExecNodes
|
|
|
open RouteNode
|
|
|
|
|
|
|
|
|
-type Zapp<'T> = State<'T> -> unit
|
|
|
-
|
|
|
-
|
|
|
+//type State<'T> -> unit = State<'T> -> unit
|
|
|
|
|
|
|
|
|
// RouteNodes
|
|
|
//////////////////
|
|
|
type ParseFn<'a> = string * Range -> ValueOption< 'a>
|
|
|
|
|
|
-type RouteNode1<'T,'a>(inext:INode<'T>,ifail:INode<'T>,current:'a -> Zapp<'T>,parseA:ParseFn<'a>) =
|
|
|
+type RouteNode1<'T,'a>(inext:INode<'T>,ifail:INode<'T>,current:'a -> State<'T> -> unit,parseA:ParseFn<'a>) =
|
|
|
let mutable next = inext
|
|
|
let mutable fail = ifail
|
|
|
//let parseA : string * Range -> ValueOption< ^a> = Parse $ Unchecked.defaultof< ^a>
|
|
@@ -28,13 +26,14 @@ type RouteNode1<'T,'a>(inext:INode<'T>,ifail:INode<'T>,current:'a -> Zapp<'T>,pa
|
|
|
|
|
|
// Parse like a specialised Apply so routing functions need concrete type
|
|
|
interface IRouteNode<'T> with
|
|
|
- member x.Parse (range:Range [],ctx:State<'T>) =
|
|
|
+ member x.Parse (range:Range [],ctx: State<'T>) : bool =
|
|
|
let path = ctx.HttpContext.Request.Path.Value
|
|
|
let v1 = parseA(path,range.[0])
|
|
|
if v1.HasValue then
|
|
|
ctx.DNode <- x
|
|
|
fnOpt.Invoke(v1.Value,ctx)
|
|
|
- else fail.Apply ctx
|
|
|
+ true
|
|
|
+ else false
|
|
|
|
|
|
interface INode<'T> with
|
|
|
member __.Next with get () = next and set v = next <- v
|
|
@@ -43,7 +42,7 @@ type RouteNode1<'T,'a>(inext:INode<'T>,ifail:INode<'T>,current:'a -> Zapp<'T>,pa
|
|
|
|
|
|
///////////////////
|
|
|
|
|
|
-type RouteNode2<'T,'a,'b>(inext:INode<'T>,ifail:INode<'T>,fn:^a -> ^b -> Zapp<'T>,
|
|
|
+type RouteNode2<'T,'a,'b>(inext:INode<'T>,ifail:INode<'T>,fn:^a -> ^b -> State<'T> -> unit,
|
|
|
parseA:ParseFn<'a>,parseB:ParseFn<'b>) =
|
|
|
let mutable next = inext
|
|
|
let mutable fail = ifail
|
|
@@ -58,15 +57,16 @@ type RouteNode2<'T,'a,'b>(inext:INode<'T>,ifail:INode<'T>,fn:^a -> ^b -> Zapp<'T
|
|
|
if v2.HasValue then
|
|
|
ctx.DNode <- x
|
|
|
fnOpt.Invoke(v1.Value,v2.Value,ctx)
|
|
|
- else fail.Apply ctx
|
|
|
- else fail.Apply ctx
|
|
|
+ true
|
|
|
+ else false
|
|
|
+ else false
|
|
|
|
|
|
interface INode<'T> with
|
|
|
member __.Next with get () = next and set v = next <- v
|
|
|
member __.Fail with get () = fail and set v = fail <- v
|
|
|
member x.Apply (_) = failwith "Who's calling apply on a route node !? ya dipshit"
|
|
|
|
|
|
-type RouteNode3<'T,'a,'b,'c>(inext:INode<'T>,ifail:INode<'T>,fn:'a -> 'b -> 'c -> Zapp<'T>,
|
|
|
+type RouteNode3<'T,'a,'b,'c>(inext:INode<'T>,ifail:INode<'T>,fn:'a -> 'b -> 'c -> State<'T> -> unit,
|
|
|
parseA:ParseFn<'a>,parseB:ParseFn<'b>,parseC:ParseFn<'c>) =
|
|
|
let mutable next = inext
|
|
|
let mutable fail = ifail
|
|
@@ -83,9 +83,10 @@ type RouteNode3<'T,'a,'b,'c>(inext:INode<'T>,ifail:INode<'T>,fn:'a -> 'b -> 'c -
|
|
|
if v3.HasValue then
|
|
|
ctx.DNode <- x
|
|
|
fnOpt.Invoke(v1.Value,v2.Value,v3.Value,ctx)
|
|
|
- else fail.Apply ctx
|
|
|
- else fail.Apply ctx
|
|
|
- else fail.Apply ctx
|
|
|
+ true
|
|
|
+ else false
|
|
|
+ else false
|
|
|
+ else false
|
|
|
|
|
|
interface INode<'T> with
|
|
|
member __.Next with get () = next and set v = next <- v
|
|
@@ -93,7 +94,7 @@ type RouteNode3<'T,'a,'b,'c>(inext:INode<'T>,ifail:INode<'T>,fn:'a -> 'b -> 'c -
|
|
|
member x.Apply (_) = failwith "Who's calling apply on a route node !? ya dipshit"
|
|
|
|
|
|
|
|
|
-type RouteNode4<'T,'a,'b,'c,'d>(inext:INode<'T>,ifail:INode<'T>,fn:'a -> 'b -> 'c -> 'd -> Zapp<'T>,
|
|
|
+type RouteNode4<'T,'a,'b,'c,'d>(inext:INode<'T>,ifail:INode<'T>,fn:'a -> 'b -> 'c -> 'd -> State<'T> -> unit,
|
|
|
parseA:ParseFn<'a>,parseB:ParseFn<'b>,parseC:ParseFn<'c>,parseD:ParseFn<'d>) =
|
|
|
let mutable next = inext
|
|
|
let mutable fail = ifail
|
|
@@ -112,10 +113,11 @@ type RouteNode4<'T,'a,'b,'c,'d>(inext:INode<'T>,ifail:INode<'T>,fn:'a -> 'b -> '
|
|
|
if v4.HasValue then
|
|
|
ctx.DNode <- x
|
|
|
fnOpt.Invoke(v1.Value,v2.Value,v3.Value,v4.Value,ctx)
|
|
|
- else fail.Apply ctx
|
|
|
- else fail.Apply ctx
|
|
|
- else fail.Apply ctx
|
|
|
- else fail.Apply ctx
|
|
|
+ true
|
|
|
+ else false
|
|
|
+ else false
|
|
|
+ else false
|
|
|
+ else false
|
|
|
|
|
|
interface INode<'T> with
|
|
|
member __.Next with get () = next and set v = next <- v
|
|
@@ -217,13 +219,15 @@ let routef (method:METHODS) (pattern : string) (fn:IRouteNode<'T>) (argCount:int
|
|
|
let last = pattern.Length - 1
|
|
|
|
|
|
let rec go (i:int,ts:int,pcount,node:RNode<'T>) =
|
|
|
+ node.MethodFilters.Add method |> ignore
|
|
|
+
|
|
|
let pl = pattern.IndexOf('%',i)
|
|
|
if pl < 0 || pl = last then
|
|
|
//Match Complete (no futher parse '%' chars
|
|
|
if pcount = 0 then
|
|
|
failwith "'routef' (route Parse) used with no arguments? please add % format args or change to simple 'route' for non-parse routes"
|
|
|
else
|
|
|
- RNode.ExtendPath node (pattern -| ts) ( Complete( method,fn )) //todo: boxing & upcasting bad for performance, need to fix
|
|
|
+ RNode.ExtendPath node (pattern -| ts) ( Complete( method,fn ))
|
|
|
else
|
|
|
let fmtChar = pattern.[pl + 1]
|
|
|
// overrided %% -> % case
|
|
@@ -237,12 +241,15 @@ let routef (method:METHODS) (pattern : string) (fn:IRouteNode<'T>) (argCount:int
|
|
|
// if node.MidFns |> List.exists (function | ApplyMatchAndComplete(c,_,_) -> fmtChar = c | _ -> false )
|
|
|
// then sprintf "duplicate paths detected '%s', Trie Build skipping..." pattern |> failwith
|
|
|
// else
|
|
|
- RNode.ExtendPath node (pattern.Substring(ts,pl - ts)) (MatchComplete( method,pcount,fn ))
|
|
|
+ let nnode = RNode.ExtendPath node (pattern.Substring(ts,pl - ts)) (MatchComplete( method,pcount,fn ))
|
|
|
+ nnode.MethodFilters.Add method |> ignore
|
|
|
+ nnode
|
|
|
else //otherwise add mid pattern parse apply
|
|
|
//get node this parser will be on
|
|
|
let nnode = RNode.ExtendPath node (pattern.Substring(ts,pl - ts)) Empty
|
|
|
let cnode,midFns = getPostMatchNode argCount pcount fmtChar pattern.[pl+2] nnode.MidFns
|
|
|
nnode.MidFns <- midFns //update adjusted functions
|
|
|
+ nnode.MethodFilters.Add method |> ignore
|
|
|
go(pl + 2, pl + 2, pcount + 1, cnode)
|
|
|
// badly formated format string that has unknown char after %
|
|
|
else
|
|
@@ -258,19 +265,19 @@ let routef (method:METHODS) (pattern : string) (fn:IRouteNode<'T>) (argCount:int
|
|
|
////////////////////////////////
|
|
|
|
|
|
/// **get1**: GET Method filtered route with **one** parameter to be parsed and applied
|
|
|
-let inline get1 (fmt:PrintfFormat< ^a -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
+let inline get1 (fmt:PrintfFormat< ^a -> State<'T> -> unit,_,_,State<'T> -> unit>) =
|
|
|
RouteBase<'T, ^a>(METHODS.GET,fmt.Value)
|
|
|
|
|
|
/// **get2**: GET Method filtered route with **two** parameter to be parsed and applied
|
|
|
-let inline get2 (fmt:PrintfFormat< ^a -> ^b -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
+let inline get2 (fmt:PrintfFormat< ^a -> ^b -> State<'T> -> unit,_,_,State<'T> -> unit>) =
|
|
|
RouteBase<'T, ^a, ^b>(METHODS.GET,fmt.Value)
|
|
|
|
|
|
/// **get3**: GET Method filtered route with **three** parameter to be parsed and applied
|
|
|
-let inline get3 (fmt:PrintfFormat< ^a -> ^b -> ^c -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
+let inline get3 (fmt:PrintfFormat< ^a -> ^b -> ^c -> State<'T> -> unit,_,_,State<'T> -> unit>) =
|
|
|
RouteBase<'T, ^a, ^b, ^c>(METHODS.GET,fmt.Value)
|
|
|
|
|
|
/// **get4**: GET Method filtered route with **four** parameter to be parsed and applied
|
|
|
-let inline get4 (fmt:PrintfFormat< ^a -> ^b -> ^c -> ^d -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
+let inline get4 (fmt:PrintfFormat< ^a -> ^b -> ^c -> ^d -> State<'T> -> unit,_,_,State<'T> -> unit>) =
|
|
|
RouteBase<'T, ^a, ^b, ^c>(METHODS.GET,fmt.Value)
|
|
|
|
|
|
|
|
@@ -278,59 +285,40 @@ let inline get4 (fmt:PrintfFormat< ^a -> ^b -> ^c -> ^d -> Zapp<'T>,_,_,Zapp<'T>
|
|
|
//////////////////////////////////
|
|
|
|
|
|
/// **post1**: POST Method filtered route with **one** parameter to be parsed and applied
|
|
|
-let inline post1 (fmt:PrintfFormat< ^a -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
+let inline post1 (fmt:PrintfFormat< ^a -> State<'T> -> unit,_,_,State<'T> -> unit>) =
|
|
|
RouteBase<'T, ^a>(METHODS.POST,fmt.Value)
|
|
|
|
|
|
/// **post2**: POST Method filtered route with **two** parameter to be parsed and applied
|
|
|
-let inline post2 (fmt:PrintfFormat< ^a -> ^b -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
+let inline post2 (fmt:PrintfFormat< ^a -> ^b -> State<'T> -> unit,_,_,State<'T> -> unit>) =
|
|
|
RouteBase<'T, ^a, ^b>(METHODS.POST,fmt.Value)
|
|
|
|
|
|
/// **post3**: POST Method filtered route with **three** parameter to be parsed and applied
|
|
|
-let inline post3 (fmt:PrintfFormat< ^a -> ^b -> ^c -> Zapp<'T>,_,_,Zapp<'T>>) (fn: ^a -> ^b -> ^c -> Zapp<'T>) =
|
|
|
+let inline post3 (fmt:PrintfFormat< ^a -> ^b -> ^c -> State<'T> -> unit,_,_,State<'T> -> unit>) (fn: ^a -> ^b -> ^c -> State<'T> -> unit) =
|
|
|
RouteBase<'T, ^a, ^b, ^c>(METHODS.POST,fmt.Value)
|
|
|
|
|
|
/// **post4**: POST Method filtered route with **four** parameter to be parsed and applied
|
|
|
-let inline post4 (fmt:PrintfFormat< ^a -> ^b -> ^c -> ^d -> Zapp<'T>,_,_,Zapp<'T>>) (fn: ^a -> ^b -> ^c -> ^d -> Zapp<'T>) =
|
|
|
+let inline post4 (fmt:PrintfFormat< ^a -> ^b -> ^c -> ^d -> State<'T> -> unit,_,_,State<'T> -> unit>) (fn: ^a -> ^b -> ^c -> ^d -> State<'T> -> unit) =
|
|
|
RouteBase<'T, ^a, ^b, ^c>(METHODS.POST,fmt.Value)
|
|
|
|
|
|
// PUT Functions
|
|
|
///////////////////////////////////
|
|
|
|
|
|
/// **put1**: PUT Method filtered route with **one** parameter to be parsed and applied
|
|
|
-let inline put1 (fmt:PrintfFormat< ^a -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
+let inline put1 (fmt:PrintfFormat< ^a -> State<'T> -> unit,_,_,State<'T> -> unit>) =
|
|
|
RouteBase<'T, ^a>(METHODS.PUT,fmt.Value)
|
|
|
|
|
|
/// **put2**: PUT Method filtered route with **two** parameter to be parsed and applied
|
|
|
-let inline put2 (fmt:PrintfFormat< ^a -> ^b -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
+let inline put2 (fmt:PrintfFormat< ^a -> ^b -> State<'T> -> unit,_,_,State<'T> -> unit>) =
|
|
|
RouteBase<'T, ^a, ^b>(METHODS.PUT,fmt.Value)
|
|
|
|
|
|
/// **put3**: PUT Method filtered route with **three** parameter to be parsed and applied
|
|
|
-let inline put3 (fmt:PrintfFormat< ^a -> ^b -> ^c -> Zapp<'T>,_,_,Zapp<'T>>) (fn: ^a -> ^b -> ^c -> Zapp<'T>) =
|
|
|
+let inline put3 (fmt:PrintfFormat< ^a -> ^b -> ^c -> State<'T> -> unit,_,_,State<'T> -> unit>) (fn: ^a -> ^b -> ^c -> State<'T> -> unit) =
|
|
|
RouteBase<'T, ^a, ^b, ^c>(METHODS.PUT,fmt.Value)
|
|
|
|
|
|
/// **put4**: PUT Method filtered route with **four** parameter to be parsed and applied
|
|
|
-let inline put4 (fmt:PrintfFormat< ^a -> ^b -> ^c -> ^d -> Zapp<'T>,_,_,Zapp<'T>>) (fn: ^a -> ^b -> ^c -> ^d -> Zapp<'T>) =
|
|
|
+let inline put4 (fmt:PrintfFormat< ^a -> ^b -> ^c -> ^d -> State<'T> -> unit,_,_,State<'T> -> unit>) (fn: ^a -> ^b -> ^c -> ^d -> State<'T> -> unit) =
|
|
|
RouteBase<'T, ^a, ^b, ^c>(METHODS.PUT,fmt.Value)
|
|
|
|
|
|
-// DELETE Functions
|
|
|
-///////////////////////////////////
|
|
|
-
|
|
|
-/// **delete1**: delete Method filtered route with **one** parameter to be parsed and applied
|
|
|
-let inline delete1 (fmt:PrintfFormat< ^a -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
- RouteBase<'T, ^a>(METHODS.DELETE,fmt.Value)
|
|
|
-
|
|
|
-/// **delete2**: delete Method filtered route with **two** parameter to be parsed and applied
|
|
|
-let inline delete2 (fmt:PrintfFormat< ^a -> ^b -> Zapp<'T>,_,_,Zapp<'T>>) =
|
|
|
- RouteBase<'T, ^a, ^b>(METHODS.DELETE,fmt.Value)
|
|
|
-
|
|
|
-/// **delete3**: delete Method filtered route with **three** parameter to be parsed and applied
|
|
|
-let inline delete3 (fmt:PrintfFormat< ^a -> ^b -> ^c -> Zapp<'T>,_,_,Zapp<'T>>) (fn: ^a -> ^b -> ^c -> Zapp<'T>) =
|
|
|
- RouteBase<'T, ^a, ^b, ^c>(METHODS.DELETE,fmt.Value)
|
|
|
-
|
|
|
-/// **delete4**: delete Method filtered route with **four** parameter to be parsed and applied
|
|
|
-let inline delete4 (fmt:PrintfFormat< ^a -> ^b -> ^c -> ^d -> Zapp<'T>,_,_,Zapp<'T>>) (fn: ^a -> ^b -> ^c -> ^d -> Zapp<'T>) =
|
|
|
- RouteBase<'T, ^a, ^b, ^c>(METHODS.DELETE,fmt.Value)
|
|
|
-
|
|
|
|
|
|
// Runtime Processor
|
|
|
/////////////////////////////////////////
|
|
@@ -354,7 +342,7 @@ type getNodeCompletionResult<'T> =
|
|
|
|
|
|
let private emptyRange = Unchecked.defaultof<Range []>
|
|
|
|
|
|
-let processPath (abort:INode<'T>) (root:RNode<'T>) : Zapp<'T> =
|
|
|
+let processPath (abort:INode<'T>) (root:RNode<'T>) : State<'T> -> unit =
|
|
|
|
|
|
fun ctx ->
|
|
|
|
|
@@ -362,36 +350,17 @@ let processPath (abort:INode<'T>) (root:RNode<'T>) : Zapp<'T> =
|
|
|
|
|
|
let path : string = ctx.HttpContext.Request.Path.Value
|
|
|
let last = path.Length - 1
|
|
|
-
|
|
|
- let rec crawl (pos:int , node:RNode<'T>, mf , ef) =
|
|
|
- if node.Token.Length > 0 then
|
|
|
- let cp = commonPathIndex path pos node.Token
|
|
|
- if cp = node.Token.Length then
|
|
|
- let nxtChar = pos + node.Token.Length
|
|
|
- if (nxtChar - 1 ) = last then //if have reached end of path through nodes, run HandlerFn
|
|
|
- ef(node.EndFns, pos, emptyRange )
|
|
|
- else
|
|
|
- match node.TryGetValue path.[nxtChar] with
|
|
|
- | true, cnode ->
|
|
|
- if (pos + cnode.Token.Length ) = last then //if have reached end of path through nodes, run HandlerFn
|
|
|
- ef(cnode.EndFns, pos + node.Token.Length, emptyRange )
|
|
|
- else //need to continue down chain till get to end of path
|
|
|
- crawl (nxtChar,cnode,mf,ef)
|
|
|
- | false, _ ->
|
|
|
- // no further nodes, either a static url didnt match or there is a pattern match required
|
|
|
- mf( node.MidFns, nxtChar, emptyRange )
|
|
|
- else
|
|
|
- abort.Apply ctx
|
|
|
- elif node.Token.Length = 0 then
|
|
|
- match node.TryGetValue path.[pos] with
|
|
|
- | true, cnode ->
|
|
|
- crawl (pos,cnode,mf,ef)
|
|
|
- | false, _ ->
|
|
|
- // no further nodes, either a static url didnt match or there is a pattern match required
|
|
|
- mf( node.MidFns, pos , emptyRange )
|
|
|
- else
|
|
|
- //printfn ">> failed to match %s path with %s token, commonPath=%i" (path.Substring(pos)) (node.Token) (commonPathIndex path pos node.Token)
|
|
|
- abort.Apply ctx
|
|
|
+ let method =
|
|
|
+ match ctx.HttpContext.Request.Method with
|
|
|
+ | "GET" -> METHODS.GET
|
|
|
+ | "POST" -> METHODS.POST
|
|
|
+ | "PUT" -> METHODS.PUT
|
|
|
+ | "DELETE" -> METHODS.DELETE
|
|
|
+ | "PATCH" -> METHODS.PATCH
|
|
|
+ | _ -> METHODS.UNKNOWN
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
let rec checkCompletionPath (pos:int,node:RNode<'T>) = // this funciton is only used by parser paths
|
|
|
//this function doesn't test array bounds as all callers do so before
|
|
@@ -432,23 +401,23 @@ let processPath (abort:INode<'T>) (root:RNode<'T>) : Zapp<'T> =
|
|
|
//let createResult (args:obj list) (argCount:int) (pfc:ParseFnCache) =
|
|
|
|
|
|
|
|
|
- let rec processEnd (fns:Cont<'T> list, pos, range:Range []) =
|
|
|
+ let rec processEnd (fns:Cont<'T> list, pos, range:Range []) : bool =
|
|
|
match fns with
|
|
|
- | [] -> abort.Apply ctx
|
|
|
+ | [] -> false
|
|
|
| h :: t ->
|
|
|
match h with
|
|
|
| HandlerMap(method,inode) ->
|
|
|
if methodMatch(ctx,method) then
|
|
|
ctx.PathPosition <- pos
|
|
|
- inode.Apply ctx
|
|
|
- else
|
|
|
- abort.Apply ctx
|
|
|
+ inode.Apply (ctx)
|
|
|
+ true
|
|
|
+ else false
|
|
|
| Complete (method,fn) ->
|
|
|
if methodMatch(ctx,method) then
|
|
|
ctx.PathPosition <- pos
|
|
|
- fn.Parse(range,ctx)
|
|
|
- else
|
|
|
- abort.Apply ctx
|
|
|
+ if fn.Parse(range,ctx) then true
|
|
|
+ else processEnd (t,pos,range)
|
|
|
+ else false
|
|
|
| x -> failwithf "Cont Mapping failed: %A in processEnd" x
|
|
|
|
|
|
let rec processMid (fns:Cont<'T> list,pos, range) =
|
|
@@ -456,9 +425,9 @@ let processPath (abort:INode<'T>) (root:RNode<'T>) : Zapp<'T> =
|
|
|
let inline applyMatchAndComplete pos pcount (range:Range []) (fn:IRouteNode<'T>) tail =
|
|
|
range.[pcount] <- Range(pos,last)
|
|
|
ctx.PathPosition <- pos
|
|
|
- fn.Parse(range,ctx) /// fall back on list not included and using default abort need tail fallback ???
|
|
|
- // else processMid(tail, pos, range) // ??????????????????
|
|
|
-
|
|
|
+ if fn.Parse(range,ctx) then true
|
|
|
+ else processMid(tail, pos, range)
|
|
|
+
|
|
|
let rec applyMatch pos pcount (f:char) (ca:char[]) n (range:Range []) tail =
|
|
|
let nc = getNodeCompletion(ca, pos, n)
|
|
|
match nc.Success with
|
|
@@ -477,7 +446,7 @@ let processPath (abort:INode<'T>) (root:RNode<'T>) : Zapp<'T> =
|
|
|
applyMatch pos 0 fmt nextChars node range tail // Apply match using 0 inital paramter position
|
|
|
|
|
|
match fns with
|
|
|
- | [] -> abort.Apply ctx
|
|
|
+ | [] -> false
|
|
|
| h :: t ->
|
|
|
match h with
|
|
|
| InitialMatch (argCount,fmt,nextChars,node) -> InitialMatch argCount fmt nextChars node t
|
|
@@ -488,11 +457,45 @@ let processPath (abort:INode<'T>) (root:RNode<'T>) : Zapp<'T> =
|
|
|
applyMatchAndComplete pos pcount [|Range(pos,last)|] fn t //<< HACK
|
|
|
else
|
|
|
applyMatchAndComplete pos pcount range fn t
|
|
|
- else abort.Apply ctx
|
|
|
+ else false
|
|
|
| x -> failwithf "Cont Mapping failed: %A in processMid" x
|
|
|
|
|
|
+ let rec crawl (pos:int , node:RNode<'T>) : bool =
|
|
|
+ if node.MethodFilters.Count > 0 && not (node.MethodFilters.Contains method) then
|
|
|
+ false
|
|
|
+ else
|
|
|
+ if node.Token.Length > 0 then
|
|
|
+ let cp = commonPathIndex path pos node.Token
|
|
|
+ if cp = node.Token.Length then
|
|
|
+ let nxtChar = pos + node.Token.Length
|
|
|
+ if (nxtChar - 1 ) = last then //if have reached end of path through nodes, run HandlerFn
|
|
|
+ processEnd(node.EndFns, pos, emptyRange )
|
|
|
+ else
|
|
|
+ match node.TryGetValue path.[nxtChar] with
|
|
|
+ | true, cnode ->
|
|
|
+ if (pos + cnode.Token.Length ) = last then //if have reached end of path through nodes, run HandlerFn
|
|
|
+ processEnd(cnode.EndFns, pos + node.Token.Length, emptyRange )
|
|
|
+ else //need to continue down chain till get to end of path
|
|
|
+ crawl (nxtChar,cnode)
|
|
|
+ | false, _ ->
|
|
|
+ // no further nodes, either a static url didnt match or there is a pattern match required
|
|
|
+ processMid( node.MidFns, nxtChar, emptyRange )
|
|
|
+ else
|
|
|
+ false
|
|
|
+ elif node.Token.Length = 0 then
|
|
|
+ match node.TryGetValue path.[pos] with
|
|
|
+ | true, cnode ->
|
|
|
+ crawl (pos,cnode)
|
|
|
+ | false, _ ->
|
|
|
+ // no further nodes, either a static url didnt match or there is a pattern match required
|
|
|
+ processMid( node.MidFns, pos , emptyRange )
|
|
|
+ else
|
|
|
+ //printfn ">> failed to match %s path with %s token, commonPath=%i" (path.Substring(pos)) (node.Token) (commonPathIndex path pos node.Token)
|
|
|
+ false
|
|
|
+
|
|
|
// begin path crawl process
|
|
|
- crawl(ctx.PathPosition,root,processMid,processEnd)
|
|
|
+ if crawl(ctx.PathPosition,root) then ()
|
|
|
+ else abort.Apply(ctx)
|
|
|
|
|
|
|
|
|
type RouterNode<'T>(inext:INode<'T>,ifail:INode<'T>,routes:((INode<'T> * INode<'T>) -> RNode<'T> -> RNode<'T>) list) =
|
|
@@ -510,8 +513,7 @@ type RouterNode<'T>(inext:INode<'T>,ifail:INode<'T>,routes:((INode<'T> * INode<'
|
|
|
member __.Next with get () = Unchecked.defaultof<INode<'T>> and set _ = ()
|
|
|
member __.Fail with get () = Unchecked.defaultof<INode<'T>> and set _ = ()
|
|
|
member x.Apply (state:State<'T>) =
|
|
|
- processPath fail inode state
|
|
|
-
|
|
|
+ processPath fail inode (state)
|
|
|
|
|
|
let inline router (routes:((INode<'T> * INode<'T>) -> RNode<'T> -> RNode<'T>) list) =
|
|
|
fun (next:INode<'T>,fail:INode<'T>) ->
|
|
@@ -525,14 +527,6 @@ let subRoute (path:string) (fns:((INode<'T> * INode<'T>) -> RNode<'T>->RNode<'T>
|
|
|
fn itree child |> ignore
|
|
|
child
|
|
|
|
|
|
-type ChooseWrap<'T>(fns:PipeLine<'T> list) =
|
|
|
- member x.Apply(next:INode<'T>,fail:INode<'T>) =
|
|
|
- let rec go (ls:PipeLine<'T> list) =
|
|
|
- match ls with
|
|
|
- | [] -> fail
|
|
|
- | h :: t ->
|
|
|
- h(next,go t)
|
|
|
- go fns
|
|
|
|
|
|
/// **Description**
|
|
|
/// Choose provides a list of options the app attempts in order listed, returning false in any pipeline will proceed to the next pipeline on the list
|
|
@@ -549,10 +543,35 @@ type ChooseWrap<'T>(fns:PipeLine<'T> list) =
|
|
|
type PipelineList<'T> = PipeLine<'T> list
|
|
|
type ActionList<'T> = (State<'T> -> unit) list
|
|
|
|
|
|
-let choose (fns:PipeLine<'T> list) = ChooseWrap<'T>(fns)
|
|
|
|
|
|
// Wraps a Handler (Zapp) in a Pipeline function to allow binding in choose and other fixed seq type scenarios
|
|
|
-let pipeline (fn:Zapp<'T>) =
|
|
|
+let pipeline (fn:State<'T> -> unit) =
|
|
|
fun (next:INode<'T>,fail:INode<'T>) ->
|
|
|
ChoiceNode(next,fail,fn) :> INode<'T>
|
|
|
|
|
|
+type ChooseWrap<'T> =
|
|
|
+| ChooseWrap of (INode<'T> * INode<'T> -> INode<'T>)
|
|
|
+
|
|
|
+type ChooseBuilder() =
|
|
|
+ member x.Delay(f) = f()
|
|
|
+ member x.YieldFrom(pipe:PipeLine<'T>) = ChooseWrap(pipe)
|
|
|
+
|
|
|
+ member x.Yield(action:State<'T> -> unit) = ChooseWrap(fun (next:INode<'T>,fail:INode<'T>) -> ChoiceNode(next,fail,action) :> INode<'T> )
|
|
|
+
|
|
|
+ member x.Combine(ChooseWrap(parentFn),ChooseWrap(childFn)) =
|
|
|
+ ChooseWrap(fun (next:INode<'T>,fail:INode<'T>) ->
|
|
|
+ let child = childFn(next,fail)
|
|
|
+ parentFn(next,child)
|
|
|
+ )
|
|
|
+
|
|
|
+let choose = ChooseBuilder()
|
|
|
+
|
|
|
+let choosePipe (fns:PipeLine<'T> list) =
|
|
|
+ ChooseWrap(fun (next:INode<'T>,fail:INode<'T>) ->
|
|
|
+ let rec go (ls:PipeLine<'T> list) =
|
|
|
+ match ls with
|
|
|
+ | [] -> fail
|
|
|
+ | h :: t ->
|
|
|
+ h(next,go t)
|
|
|
+ go fns)
|
|
|
+
|