module Parse.Internal.Parse (parseParts, ParseTuple, parseTuple) where

import Data.List (inits, tails)
import Data.List.Split (splitOn)
import Data.Maybe (mapMaybe, catMaybes)

import Parse.Internal.Instances

parseParts :: [String] -> String -> Either String [String]
parseParts :: [String] -> String -> Either String [String]
parseParts (String
part:parts :: [String]
parts@(String
nextPart:[String]
_)) String
str = do
  case String -> String -> Maybe String
dropBy String
part String
str of
    Just String
str' ->
      let candidates :: [(String, String)]
candidates = ((String, String) -> Bool)
-> [(String, String)] -> [(String, String)]
forall a. (a -> Bool) -> [a] -> [a]
filter (String -> String -> Bool
beginEqual String
nextPart (String -> Bool)
-> ((String, String) -> String) -> (String, String) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, String) -> String
forall a b. (a, b) -> b
snd) (String -> [(String, String)]
forall a. [a] -> [([a], [a])]
splits String
str')
          (String
parsed, String
rest) = [(String, String)] -> (String, String)
forall a. [a] -> a
head [(String, String)]
candidates
      in if [(String, String)] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(String, String)]
candidates
         then String -> Either String [String]
forall a b. a -> Either a b
Left (String -> Either String [String])
-> String -> Either String [String]
forall a b. (a -> b) -> a -> b
$ String
"No candidates for \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
part String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\" in \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
str String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\"."
         else (String
parsedString -> [String] -> [String]
forall a. a -> [a] -> [a]
:) ([String] -> [String])
-> Either String [String] -> Either String [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [String] -> String -> Either String [String]
parseParts [String]
parts String
rest
    Maybe String
Nothing -> String -> Either String [String]
forall a b. a -> Either a b
Left (String -> Either String [String])
-> String -> Either String [String]
forall a b. (a -> b) -> a -> b
$ String
"Expected \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
part String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\", instead got \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
str String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\"."

  where
    beginEqual :: String -> String -> Bool
    beginEqual :: String -> String -> Bool
beginEqual String
"" String
target = String
target String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
""
    beginEqual String
str String
target = Int -> String -> String
forall a. Int -> [a] -> [a]
take (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
str) String
target String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
str

    dropBy :: String -> String -> Maybe String
    dropBy :: String -> String -> Maybe String
dropBy String
str String
target =
      if Int -> String -> String
forall a. Int -> [a] -> [a]
take (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
str) String
target String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
str
      then String -> Maybe String
forall a. a -> Maybe a
Just (Int -> String -> String
forall a. Int -> [a] -> [a]
drop (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
str) String
target)
      else Maybe String
forall a. Maybe a
Nothing

    splits :: [a] -> [([a], [a])]
    splits :: [a] -> [([a], [a])]
splits [a]
x = [[a]] -> [[a]] -> [([a], [a])]
forall a b. [a] -> [b] -> [(a, b)]
zip ([a] -> [[a]]
forall a. [a] -> [[a]]
inits [a]
x) ([a] -> [[a]]
forall a. [a] -> [[a]]
tails [a]
x)

parseParts [String]
_ String
_ = [String] -> Either String [String]
forall a b. b -> Either a b
Right []

$(parseTupleInstances [2..62])

instance ParseTuple String where
  parseTuple :: String -> String -> Either String String
parseTuple String
format String
str =
    case [String] -> String -> Either String [String]
parseParts (String -> String -> [String]
forall a. Eq a => [a] -> [a] -> [[a]]
splitOn String
"{}" String
format) String
str of
      Right [String
x] -> String -> Either String String
forall a b. b -> Either a b
Right String
x
      Right [String]
result -> String -> Either String String
forall a b. a -> Either a b
Left (String -> Either String String) -> String -> Either String String
forall a b. (a -> b) -> a -> b
$ String
"Parsed " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ([String] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
result) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" values, expected 1."
      Left String
x -> String -> Either String String
forall a b. a -> Either a b
Left String
x