Skip to content

Instantly share code, notes, and snippets.

@BrianHicks
Created September 4, 2019 17:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BrianHicks/d8469943d714dfc3fb39a17d397c9b84 to your computer and use it in GitHub Desktop.
Save BrianHicks/d8469943d714dfc3fb39a17d397c9b84 to your computer and use it in GitHub Desktop.
module Slice exposing
( Slice, slice
, Multiple, Position(..), before, after, getList
, Single, first, last, get, map
)
{-| Bisect a `Todos` in whatever way.
# initializing a slice
@docs Slice, slice
# working with multiple matches
@docs Multiple, Position, before, after, getList
# working with single matches
@docs Single, first, last, get, map
-}
import Filter exposing (Filter)
import List.Extra -- a private implementation, not elm-community's
import Todo exposing (Todo)
import Todos exposing (Todos)
type Slice quantity
= Leaf quantity
| Before (Slice quantity) (Maybe Todo) (List Todo)
| After (List Todo) (Maybe Todo) (Slice quantity)
{-| Having Multiple means that there are many possible remaining options in the
slice. You can get a list of them, but you can't query for an individual yet.
-}
type Multiple
= Multiple (List Todo)
type Position
= First
| Last
slice : Todos -> Slice Multiple
slice todos =
Leaf (Multiple (Todos.toList todos))
before : Position -> Filter -> Slice Multiple -> Slice Multiple
before position filter query_ =
case query_ of
Leaf (Multiple todos) ->
let
( before_, pivot, after_ ) =
split position filter todos
in
Before (Leaf (Multiple before_)) pivot after_
Before next pivot after_ ->
Before (before position filter next) pivot after_
After before_ pivot next ->
After before_ pivot (before position filter next)
after : Position -> Filter -> Slice Multiple -> Slice Multiple
after position filter query_ =
case query_ of
Leaf (Multiple todos) ->
let
( before_, pivot, after_ ) =
split position filter todos
in
After before_ pivot (Leaf (Multiple after_))
Before next pivot after_ ->
Before (after position filter next) pivot after_
After before_ pivot next ->
After before_ pivot (after position filter next)
getList : Slice Multiple -> List Todo
getList query_ =
case query_ of
Before next _ _ ->
getList next
After _ _ next ->
getList next
Leaf (Multiple todos) ->
todos
{-| Having Single means that there is one remaining possible option in the
slice. You can get or map the individual, but you can't go back to having
multiple matches.
-}
type Single
= Single (List Todo) (Maybe Todo) (List Todo)
first : Filter -> Slice Multiple -> Slice Single
first filter query_ =
case query_ of
Leaf (Multiple todos) ->
let
( before_, after_ ) =
List.Extra.splitBefore (Filter.matcher filter) todos
in
Leaf (Single before_ (List.head after_) (Maybe.withDefault [] (List.tail after_)))
Before next pivot after_ ->
Before (first filter next) pivot after_
After before_ pivot next ->
After before_ pivot (first filter next)
last : Filter -> Slice Multiple -> Slice Single
last filter query_ =
case query_ of
Leaf (Multiple todos) ->
let
( retfa, erofeb ) =
List.Extra.splitBefore (Filter.matcher filter) (List.reverse todos)
in
Leaf <|
Single
(erofeb
|> List.tail
|> Maybe.withDefault []
|> List.reverse
)
(List.head erofeb)
(List.reverse retfa)
Before next pivot after_ ->
Before (last filter next) pivot after_
After before_ pivot next ->
After before_ pivot (last filter next)
get : Slice Single -> Maybe Todo
get query_ =
case query_ of
Before next _ _ ->
get next
After _ _ next ->
get next
Leaf (Single _ next _) ->
next
map : (Todo -> Todo) -> Slice Single -> Maybe ( Todos, Todo )
map mapper query_ =
mapHelp mapper query_
|> Maybe.map (Tuple.mapFirst Todos.fromList)
mapHelp : (Todo -> Todo) -> Slice Single -> Maybe ( List Todo, Todo )
mapHelp mapper query_ =
case query_ of
Before next pivot after_ ->
let
middle =
pivot
|> Maybe.map List.singleton
|> Maybe.withDefault []
in
Maybe.map (Tuple.mapFirst (\todos -> todos ++ middle ++ after_))
(mapHelp mapper next)
After before_ pivot next ->
let
middle =
pivot
|> Maybe.map List.singleton
|> Maybe.withDefault []
in
Maybe.map (Tuple.mapFirst (\todos -> before_ ++ middle ++ todos))
(mapHelp mapper next)
Leaf (Single before_ Nothing after_) ->
Nothing
Leaf (Single before_ (Just this) after_) ->
let
mapped =
mapper this
in
Just
( before_ ++ [ mapped ] ++ after_
, mapped
)
split : Position -> Filter -> List Todo -> ( List Todo, Maybe Todo, List Todo )
split position filter todos =
let
splitter =
-- my List.Extra.splitBefore is like elm-community/list-extra's splitWhen but does not return a Maybe
List.Extra.splitBefore (Filter.matcher filter)
in
case position of
First ->
let
( before_, after_ ) =
splitter todos
in
( before_
, List.head after_
, Maybe.withDefault [] (List.tail after_)
)
Last ->
let
( retfa, erofeb ) =
splitter (List.reverse todos)
in
( erofeb
|> List.tail
|> Maybe.withDefault []
|> List.reverse
, List.head erofeb
, List.reverse retfa
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment