-- SPDX-FileCopyrightText: 2026 Alexandra de Wit
--
-- SPDX-License-Identifier: MIT

{- | The Simple-index walk for full and selected reads. It emits the fields that
"Ecluse.Core.Registry.PyPI.Streaming" emits for the same mode, in the same order, and builds each
retained file once. A full read interns each file's keys and strings as read.
-}
module Ecluse.Core.Registry.PyPI.Reader (
    pypiWalk,
    fileUniqueFields,
) where

import Data.Aeson (Value (Null, String))
import Data.Aeson.Key qualified as Key
import Data.JsonStream.TokenParser (Element (..), TokenResult)

import Ecluse.Core.Registry.Json.Intern (InternTable, nameBytes, nameText)
import Ecluse.Core.Registry.Json.Shape (Mode (..), Shape (..), Trees (..), knownMembers, namedMembers, readShape)
import Ecluse.Core.Registry.Json.Walk (Step, Steps (..), Walk, Walked (..), eachItem, eachMember, pureStep, skipFrom, tooDeep, withElement)
import Ecluse.Core.Registry.Json.Walk qualified as Walk
import Ecluse.Core.Registry.PyPI.Project (FileProject, fileProject)
import Ecluse.Core.Registry.PyPI.Streaming (PyPIField (..), PyPIRead (..), SelectedFile (..), SelectedFileEvent (..), collectSelected, fileScalars, finishSelected, hashNames)
import Ecluse.Core.Security (LimitError)

-- | Members whose values differ in every file, so the table keeps them as read.
fileUniqueFields :: [Text]
fileUniqueFields :: [Text]
fileUniqueFields = [Text
"filename", Text
"url", Text
"hashes", Text
"upload-time", Text
"provenance"]

{- | Walk one project's Simple index, passing each field to the step as it completes. Only a file a
full read keeps enters the table, and only the first of each member it repeats.
-}
pypiWalk :: Int -> PyPIRead -> (s -> PyPIField -> Either LimitError s) -> (s -> Bool) -> InternTable -> s -> TokenResult -> Step s
{-# INLINE pypiWalk #-}
pypiWalk :: forall s.
Int
-> PyPIRead
-> (s -> PyPIField -> Either LimitError s)
-> (s -> Bool)
-> InternTable
-> s
-> TokenResult
-> Step s
pypiWalk Int
depth PyPIRead
mode s -> PyPIField -> Either LimitError s
step s -> Bool
keeps InternTable
table0 s
initial = TokenResult -> Steps Identity s
start
  where
    start :: TokenResult -> Steps Identity s
start TokenResult
tokens
        | Int
depth Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 = TokenResult
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall r.
Walk r =>
TokenResult -> (Element -> TokenResult -> r) -> r
withElement TokenResult
tokens Element -> TokenResult -> Steps Identity s
forall r. Walk r => Element -> TokenResult -> r
tooDeep
        | Bool
otherwise = TokenResult
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall r.
Walk r =>
TokenResult -> (Element -> TokenResult -> r) -> r
withElement TokenResult
tokens ((Element -> TokenResult -> Steps Identity s) -> Steps Identity s)
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall a b. (a -> b) -> a -> b
$ \Element
element TokenResult
rest -> case Element
element of
            Element
ObjectBegin -> (Walked s
 -> Name
 -> TokenResult
 -> (Walked s -> TokenResult -> Steps Identity s)
 -> Steps Identity s)
-> (Walked s -> TokenResult -> Steps Identity s)
-> Walked s
-> TokenResult
-> Steps Identity s
forall r st.
Walk r =>
(st -> Name -> TokenResult -> (st -> TokenResult -> r) -> r)
-> (st -> TokenResult -> r) -> st -> TokenResult -> r
eachMember Walked s
-> Name
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
topField (\(Walked InternTable
_ s
acc) TokenResult
_ -> s -> Steps Identity s
forall (m :: * -> *) s. s -> Steps m s
Finished s
acc) (InternTable -> s -> Walked s
forall s. InternTable -> s -> Walked s
Walked InternTable
table0 s
initial) TokenResult
rest
            Element
_ -> Element
-> TokenResult
-> (TokenResult -> Steps Identity s)
-> Steps Identity s
forall r.
Walk r =>
Element -> TokenResult -> (TokenResult -> r) -> r
skipFrom Element
element TokenResult
rest (Steps Identity s -> TokenResult -> Steps Identity s
forall a b. a -> b -> a
const (s -> Steps Identity s
forall (m :: * -> *) s. s -> Steps m s
Finished s
initial))
    full :: Bool
full = case PyPIRead
mode of
        PyPIRead
FullRead -> Bool
True
        SelectedRead{} -> Bool
False
    emit :: s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
emit = FieldStep s PyPIField (Steps Identity s)
-> s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
forall r s field.
Walk r =>
FieldStep s field r -> s -> field -> (s -> r) -> r
Walk.emit ((s -> PyPIField -> Either LimitError s)
-> FieldStep s PyPIField (Steps Identity s)
forall s field r.
(s -> field -> Either LimitError s) -> FieldStep s field r
pureStep s -> PyPIField -> Either LimitError s
step)
    topField :: Walked s
-> Name
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
topField walked :: Walked s
walked@(Walked InternTable
table s
acc) Name
name TokenResult
after Walked s -> TokenResult -> Steps Identity s
continue = case Name -> ByteString
nameBytes Name
name of
        ByteString
"name" -> Text -> Shape -> Steps Identity s
envelope Text
"name" (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))
        ByteString
"meta" -> Text -> Shape -> Steps Identity s
envelope Text
"meta" (Int -> Members -> Shape -> Shape
ObjectWith (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Members
metaFields (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)))
        ByteString
"project-status" | Bool
full -> Text -> Shape -> Steps Identity s
envelope Text
"project-status" (Int -> Members -> Shape -> Shape
ObjectWith (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Members
statusFields (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)))
        ByteString
"alternate-locations" | Bool
full -> Text -> Shape -> Steps Identity s
envelope Text
"alternate-locations" (Int -> Shape -> Shape -> Shape
ArrayWith (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2)) (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)))
        ByteString
"files" -> (Bool -> PyPIField)
-> ((Walked s -> TokenResult -> Steps Identity s)
    -> Walked s -> TokenResult -> Steps Identity s)
-> Walked s
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
shaped Bool -> PyPIField
FilesShape ((Walked s
 -> Int
 -> Element
 -> TokenResult
 -> (Walked s -> TokenResult -> Steps Identity s)
 -> Steps Identity s)
-> (Walked s -> TokenResult -> Steps Identity s)
-> Walked s
-> TokenResult
-> Steps Identity s
forall r st.
Walk r =>
(st
 -> Int -> Element -> TokenResult -> (st -> TokenResult -> r) -> r)
-> (st -> TokenResult -> r) -> st -> TokenResult -> r
eachItem Walked s
-> Int
-> Element
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
file) Walked s
walked TokenResult
after Walked s -> TokenResult -> Steps Identity s
continue
        ByteString
"versions" | Bool
full -> (Bool -> PyPIField)
-> ((Walked s -> TokenResult -> Steps Identity s)
    -> Walked s -> TokenResult -> Steps Identity s)
-> Walked s
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
shaped Bool -> PyPIField
VersionsShape ((Walked s
 -> Int
 -> Element
 -> TokenResult
 -> (Walked s -> TokenResult -> Steps Identity s)
 -> Steps Identity s)
-> (Walked s -> TokenResult -> Steps Identity s)
-> Walked s
-> TokenResult
-> Steps Identity s
forall r st.
Walk r =>
(st
 -> Int -> Element -> TokenResult -> (st -> TokenResult -> r) -> r)
-> (st -> TokenResult -> r) -> st -> TokenResult -> r
eachItem Walked s
-> Int
-> Element
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
version) Walked s
walked TokenResult
after Walked s -> TokenResult -> Steps Identity s
continue
        ByteString
_ -> TokenResult
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall r.
Walk r =>
TokenResult -> (Element -> TokenResult -> r) -> r
withElement TokenResult
after ((Element -> TokenResult -> Steps Identity s) -> Steps Identity s)
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall a b. (a -> b) -> a -> b
$ \Element
element TokenResult
afterKey -> Element
-> TokenResult
-> (TokenResult -> Steps Identity s)
-> Steps Identity s
forall r.
Walk r =>
Element -> TokenResult -> (TokenResult -> r) -> r
skipFrom Element
element TokenResult
afterKey (Walked s -> TokenResult -> Steps Identity s
continue Walked s
walked)
      where
        envelope :: Text -> Shape -> Steps Identity s
envelope Text
key Shape
shape = TokenResult
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall r.
Walk r =>
TokenResult -> (Element -> TokenResult -> r) -> r
withElement TokenResult
after ((Element -> TokenResult -> Steps Identity s) -> Steps Identity s)
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall a b. (a -> b) -> a -> b
$ \Element
element TokenResult
afterKey ->
            Trees
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built Trees -> InternTable -> TokenResult -> Steps Identity s)
-> Steps Identity s
forall b r.
Build b r =>
b
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built b -> InternTable -> TokenResult -> r)
-> r
readShape Trees
Trees Shape
shape Mode
Keep InternTable
table Element
element TokenResult
afterKey ((Built Trees -> InternTable -> TokenResult -> Steps Identity s)
 -> Steps Identity s)
-> (Built Trees -> InternTable -> TokenResult -> Steps Identity s)
-> Steps Identity s
forall a b. (a -> b) -> a -> b
$ \Built Trees
value InternTable
_ TokenResult
afterValue ->
                s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
emit s
acc (Text -> Value -> PyPIField
EnvelopeField Text
key Value
Built Trees
value) (\s
acc' -> Walked s -> TokenResult -> Steps Identity s
continue (InternTable -> s -> Walked s
forall s. InternTable -> s -> Walked s
Walked InternTable
table s
acc') TokenResult
afterValue)

    -- The first array claims the key and its end yields an ignored field. Null claims it empty.
    shaped :: (Bool -> PyPIField)
-> ((Walked s -> TokenResult -> Steps Identity s)
    -> Walked s -> TokenResult -> Steps Identity s)
-> Walked s
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
shaped Bool -> PyPIField
claim (Walked s -> TokenResult -> Steps Identity s)
-> Walked s -> TokenResult -> Steps Identity s
items (Walked InternTable
table s
acc) TokenResult
after Walked s -> TokenResult -> Steps Identity s
continue = TokenResult
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall r.
Walk r =>
TokenResult -> (Element -> TokenResult -> r) -> r
withElement TokenResult
after ((Element -> TokenResult -> Steps Identity s) -> Steps Identity s)
-> (Element -> TokenResult -> Steps Identity s) -> Steps Identity s
forall a b. (a -> b) -> a -> b
$ \Element
element TokenResult
rest -> case Element
element of
        Element
ArrayBegin -> s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
emit s
acc (Bool -> PyPIField
claim Bool
True) ((s -> Steps Identity s) -> Steps Identity s)
-> (s -> Steps Identity s) -> Steps Identity s
forall a b. (a -> b) -> a -> b
$ \s
claimed ->
            (Walked s -> TokenResult -> Steps Identity s)
-> Walked s -> TokenResult -> Steps Identity s
items (\(Walked InternTable
table' s
acc') TokenResult
afterArray -> s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
emit s
acc' PyPIField
IgnoredField (\s
ended -> Walked s -> TokenResult -> Steps Identity s
continue (InternTable -> s -> Walked s
forall s. InternTable -> s -> Walked s
Walked InternTable
table' s
ended) TokenResult
afterArray)) (InternTable -> s -> Walked s
forall s. InternTable -> s -> Walked s
Walked InternTable
table s
claimed) TokenResult
rest
        JValue Value
Null -> s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
emit s
acc (Bool -> PyPIField
claim Bool
True) (\s
claimed -> Walked s -> TokenResult -> Steps Identity s
continue (InternTable -> s -> Walked s
forall s. InternTable -> s -> Walked s
Walked InternTable
table s
claimed) TokenResult
rest)
        Element
_ -> Element
-> TokenResult
-> (TokenResult -> Steps Identity s)
-> Steps Identity s
forall r.
Walk r =>
Element -> TokenResult -> (TokenResult -> r) -> r
skipFrom Element
element TokenResult
rest (\TokenResult
afterValue -> s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
emit s
acc (Bool -> PyPIField
claim Bool
False) (\s
marked -> Walked s -> TokenResult -> Steps Identity s
continue (InternTable -> s -> Walked s
forall s. InternTable -> s -> Walked s
Walked InternTable
table s
marked) TokenResult
afterValue))

    file :: Walked s
-> Int
-> Element
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
file (Walked InternTable
table s
acc) Int
position Element
element TokenResult
rest Walked s -> TokenResult -> Steps Identity s
continue
        | Int
depth Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
2 = Element -> TokenResult -> Steps Identity s
forall r. Walk r => Element -> TokenResult -> r
tooDeep Element
element TokenResult
rest
        | Bool
otherwise = case PyPIRead
mode of
            PyPIRead
FullRead -> Trees
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built Trees -> InternTable -> TokenResult -> Steps Identity s)
-> Steps Identity s
forall b r.
Build b r =>
b
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built b -> InternTable -> TokenResult -> r)
-> r
readShape Trees
Trees (Value -> Members -> Shape
ObjectOr Value
Null Members
fileFields) (if s -> Bool
keeps s
acc then Mode
Share else Mode
Keep) InternTable
table Element
element TokenResult
rest ((Value -> Maybe Value)
-> Value -> InternTable -> TokenResult -> Steps Identity s
forall {t}.
(t -> Maybe Value)
-> t -> InternTable -> TokenResult -> Steps Identity s
retained Value -> Maybe Value
forall a. a -> Maybe a
Just)
            SelectedRead PackageName
name Text
wanted -> Int
-> FileProject
-> Text
-> InternTable
-> Element
-> TokenResult
-> (Maybe Value -> InternTable -> TokenResult -> Steps Identity s)
-> Steps Identity s
forall r.
Walk r =>
Int
-> FileProject
-> Text
-> InternTable
-> Element
-> TokenResult
-> (Maybe Value -> InternTable -> TokenResult -> r)
-> r
selectedFile (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3) (PackageName -> FileProject
fileProject PackageName
name) Text
wanted InternTable
table Element
element TokenResult
rest ((Maybe Value -> Maybe Value)
-> Maybe Value -> InternTable -> TokenResult -> Steps Identity s
forall {t}.
(t -> Maybe Value)
-> t -> InternTable -> TokenResult -> Steps Identity s
retained Maybe Value -> Maybe Value
forall a. a -> a
id)
      where
        retained :: (t -> Maybe Value)
-> t -> InternTable -> TokenResult -> Steps Identity s
retained t -> Maybe Value
wrap t
payload InternTable
table' TokenResult
afterValue = s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
emit s
acc (Int -> Maybe Value -> PyPIField
FileField Int
position (t -> Maybe Value
wrap t
payload)) (\s
acc' -> Walked s -> TokenResult -> Steps Identity s
continue (InternTable -> s -> Walked s
forall s. InternTable -> s -> Walked s
Walked InternTable
table' s
acc') TokenResult
afterValue)

    version :: Walked s
-> Int
-> Element
-> TokenResult
-> (Walked s -> TokenResult -> Steps Identity s)
-> Steps Identity s
version (Walked InternTable
table s
acc) Int
position Element
element TokenResult
rest Walked s -> TokenResult -> Steps Identity s
continue =
        Trees
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built Trees -> InternTable -> TokenResult -> Steps Identity s)
-> Steps Identity s
forall b r.
Build b r =>
b
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built b -> InternTable -> TokenResult -> r)
-> r
readShape Trees
Trees (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2)) Mode
Keep InternTable
table Element
element TokenResult
rest ((Built Trees -> InternTable -> TokenResult -> Steps Identity s)
 -> Steps Identity s)
-> (Built Trees -> InternTable -> TokenResult -> Steps Identity s)
-> Steps Identity s
forall a b. (a -> b) -> a -> b
$ \Built Trees
value InternTable
_ TokenResult
afterValue ->
            let field :: PyPIField
field = case Built Trees
value of
                    String Text
_ -> PyPIField
IgnoredField
                    Built Trees
_ -> Int -> Value -> PyPIField
InvalidVersionField Int
position Value
Built Trees
value
             in s -> PyPIField -> (s -> Steps Identity s) -> Steps Identity s
emit s
acc PyPIField
field (\s
acc' -> Walked s -> TokenResult -> Steps Identity s
continue (InternTable -> s -> Walked s
forall s. InternTable -> s -> Walked s
Walked InternTable
table s
acc') TokenResult
afterValue)

    fileFields :: Members
fileFields = [(Text, Shape)] -> Members
namedMembers ((Text
"hashes", Int -> Members -> Shape -> Shape
ObjectWith (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3) ([Text] -> Shape -> Members
knownMembers [Text]
hashNames (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
4))) (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3))) (Text, Shape) -> [(Text, Shape)] -> [(Text, Shape)]
forall a. a -> [a] -> [a]
: [(Text
key, Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3)) | Text
key <- [Text]
fileScalars])
    metaFields :: Members
metaFields = [(Text, Shape)] -> Members
namedMembers ([(Text
"tracks", Int -> Shape -> Shape -> Shape
ArrayWith (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3)) (Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2))) | Bool
full] [(Text, Shape)] -> [(Text, Shape)] -> [(Text, Shape)]
forall a. Semigroup a => a -> a -> a
<> [(Text
"api-version", Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2))] [(Text, Shape)] -> [(Text, Shape)] -> [(Text, Shape)]
forall a. Semigroup a => a -> a -> a
<> [(Text
"_last-serial", Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2)) | Bool
full])
    statusFields :: Members
statusFields = [(Text, Shape)] -> Members
namedMembers [(Text
key, Int -> Shape
Scalar (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2)) | Text
key <- [Text
"status", Text
"reason"]]

data Selecting = Selecting !InternTable SelectedFile

-- json-stream's selected-file fold: every member event reaches the fold, even after the name rejects
-- the file. The file's texts keep their own copies, so a rejected file never enters the table.
selectedFile :: (Walk r) => Int -> FileProject -> Text -> InternTable -> Element -> TokenResult -> (Maybe Value -> InternTable -> TokenResult -> r) -> r
{-# INLINEABLE selectedFile #-}
selectedFile :: forall r.
Walk r =>
Int
-> FileProject
-> Text
-> InternTable
-> Element
-> TokenResult
-> (Maybe Value -> InternTable -> TokenResult -> r)
-> r
selectedFile Int
budget FileProject
project Text
wanted InternTable
table0 Element
element TokenResult
rest Maybe Value -> InternTable -> TokenResult -> r
next = case Element
element of
    Element
ObjectBegin -> (Selecting
 -> Name -> TokenResult -> (Selecting -> TokenResult -> r) -> r)
-> (Selecting -> TokenResult -> r) -> Selecting -> TokenResult -> r
forall r st.
Walk r =>
(st -> Name -> TokenResult -> (st -> TokenResult -> r) -> r)
-> (st -> TokenResult -> r) -> st -> TokenResult -> r
eachMember Selecting
-> Name -> TokenResult -> (Selecting -> TokenResult -> r) -> r
forall {r}.
Walk r =>
Selecting
-> Name -> TokenResult -> (Selecting -> TokenResult -> r) -> r
visit (\(Selecting InternTable
table SelectedFile
selected) TokenResult
after -> Maybe Value -> InternTable -> TokenResult -> r
next (SelectedFile -> Maybe Value
finishSelected SelectedFile
selected) InternTable
table TokenResult
after) (InternTable -> SelectedFile -> Selecting
Selecting InternTable
table0 (Bool -> [(Key, Value)] -> Maybe Value -> Bool -> SelectedFile
CandidateFile Bool
False [] Maybe Value
forall a. Maybe a
Nothing Bool
False)) TokenResult
rest
    Element
_ -> Element -> TokenResult -> (TokenResult -> r) -> r
forall r.
Walk r =>
Element -> TokenResult -> (TokenResult -> r) -> r
skipFrom Element
element TokenResult
rest (Maybe Value -> InternTable -> TokenResult -> r
next Maybe Value
forall a. Maybe a
Nothing InternTable
table0)
  where
    collect :: SelectedFile -> SelectedFileEvent -> SelectedFile
collect = FileProject
-> Text -> SelectedFile -> SelectedFileEvent -> SelectedFile
collectSelected FileProject
project Text
wanted
    visit :: Selecting
-> Name -> TokenResult -> (Selecting -> TokenResult -> r) -> r
visit (Selecting InternTable
table SelectedFile
selected) Name
name TokenResult
after Selecting -> TokenResult -> r
continue = TokenResult -> (Element -> TokenResult -> r) -> r
forall r.
Walk r =>
TokenResult -> (Element -> TokenResult -> r) -> r
withElement TokenResult
after ((Element -> TokenResult -> r) -> r)
-> (Element -> TokenResult -> r) -> r
forall a b. (a -> b) -> a -> b
$ \Element
value TokenResult
afterKey -> case Name -> ByteString
nameBytes Name
name of
        ByteString
"hashes"
            | Int
budget Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 -> Element -> TokenResult -> r
forall r. Walk r => Element -> TokenResult -> r
tooDeep Element
value TokenResult
afterKey
            | Element
ObjectBegin <- Element
value ->
                (Selecting
 -> Name -> TokenResult -> (Selecting -> TokenResult -> r) -> r)
-> (Selecting -> TokenResult -> r) -> Selecting -> TokenResult -> r
forall r st.
Walk r =>
(st -> Name -> TokenResult -> (st -> TokenResult -> r) -> r)
-> (st -> TokenResult -> r) -> st -> TokenResult -> r
eachMember
                    Selecting
-> Name -> TokenResult -> (Selecting -> TokenResult -> r) -> r
forall {r}.
Walk r =>
Selecting
-> Name -> TokenResult -> (Selecting -> TokenResult -> r) -> r
hashField
                    (\(Selecting InternTable
table' SelectedFile
hashed) TokenResult
afterObject -> Selecting -> TokenResult -> r
continue (InternTable -> SelectedFile -> Selecting
Selecting InternTable
table' (SelectedFile -> SelectedFileEvent -> SelectedFile
collect SelectedFile
hashed SelectedFileEvent
HashesEnd)) TokenResult
afterObject)
                    (InternTable -> SelectedFile -> Selecting
Selecting InternTable
table (SelectedFile -> SelectedFileEvent -> SelectedFile
collect SelectedFile
selected SelectedFileEvent
HashesStart))
                    TokenResult
afterKey
            | Bool
otherwise -> Trees
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built Trees -> InternTable -> TokenResult -> r)
-> r
forall b r.
Build b r =>
b
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built b -> InternTable -> TokenResult -> r)
-> r
readShape Trees
Trees (Int -> Shape
Scalar Int
budget) Mode
Keep InternTable
table Element
value TokenResult
afterKey ((Built Trees -> InternTable -> TokenResult -> r) -> r)
-> (Built Trees -> InternTable -> TokenResult -> r) -> r
forall a b. (a -> b) -> a -> b
$ \Built Trees
scalar InternTable
table' TokenResult
afterValue ->
                Selecting -> TokenResult -> r
continue (InternTable -> SelectedFile -> Selecting
Selecting InternTable
table' (SelectedFile -> SelectedFileEvent -> SelectedFile
collect SelectedFile
selected (Value -> SelectedFileEvent
HashesValue Value
Built Trees
scalar))) TokenResult
afterValue
        ByteString
bytes
            | ByteString
bytes ByteString -> [ByteString] -> Bool
forall (f :: * -> *) a.
(Foldable f, DisallowElem f, Eq a) =>
a -> f a -> Bool
`elem` [ByteString]
scalarNames -> Trees
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built Trees -> InternTable -> TokenResult -> r)
-> r
forall b r.
Build b r =>
b
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built b -> InternTable -> TokenResult -> r)
-> r
readShape Trees
Trees (Int -> Shape
Scalar Int
budget) Mode
Keep InternTable
table Element
value TokenResult
afterKey ((Built Trees -> InternTable -> TokenResult -> r) -> r)
-> (Built Trees -> InternTable -> TokenResult -> r) -> r
forall a b. (a -> b) -> a -> b
$ \Built Trees
scalar InternTable
table' TokenResult
afterValue ->
                Selecting -> TokenResult -> r
continue (InternTable -> SelectedFile -> Selecting
Selecting InternTable
table' (SelectedFile -> SelectedFileEvent -> SelectedFile
collect SelectedFile
selected (Key -> Value -> SelectedFileEvent
FileScalar (Text -> Key
Key.fromText (Name -> Text
nameText Name
name)) Value
Built Trees
scalar))) TokenResult
afterValue
            | Bool
otherwise -> Element -> TokenResult -> (TokenResult -> r) -> r
forall r.
Walk r =>
Element -> TokenResult -> (TokenResult -> r) -> r
skipFrom Element
value TokenResult
afterKey (Selecting -> TokenResult -> r
continue (InternTable -> SelectedFile -> Selecting
Selecting InternTable
table SelectedFile
selected))
    hashField :: Selecting
-> Name -> TokenResult -> (Selecting -> TokenResult -> r) -> r
hashField (Selecting InternTable
table SelectedFile
selected) Name
name TokenResult
after Selecting -> TokenResult -> r
continue = TokenResult -> (Element -> TokenResult -> r) -> r
forall r.
Walk r =>
TokenResult -> (Element -> TokenResult -> r) -> r
withElement TokenResult
after ((Element -> TokenResult -> r) -> r)
-> (Element -> TokenResult -> r) -> r
forall a b. (a -> b) -> a -> b
$ \Element
value TokenResult
afterKey ->
        Trees
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built Trees -> InternTable -> TokenResult -> r)
-> r
forall b r.
Build b r =>
b
-> Shape
-> Mode
-> InternTable
-> Element
-> TokenResult
-> (Built b -> InternTable -> TokenResult -> r)
-> r
readShape Trees
Trees (Int -> Shape
Scalar (Int
budget Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) Mode
Keep InternTable
table Element
value TokenResult
afterKey ((Built Trees -> InternTable -> TokenResult -> r) -> r)
-> (Built Trees -> InternTable -> TokenResult -> r) -> r
forall a b. (a -> b) -> a -> b
$ \Built Trees
scalar InternTable
table' TokenResult
afterValue ->
            Selecting -> TokenResult -> r
continue (InternTable -> SelectedFile -> Selecting
Selecting InternTable
table' (SelectedFile -> SelectedFileEvent -> SelectedFile
collect SelectedFile
selected (Key -> Value -> SelectedFileEvent
HashField (Text -> Key
Key.fromText (Name -> Text
nameText Name
name)) Value
Built Trees
scalar))) TokenResult
afterValue
    scalarNames :: [ByteString]
scalarNames = (Text -> ByteString) -> [Text] -> [ByteString]
forall a b. (a -> b) -> [a] -> [b]
map Text -> ByteString
forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 [Text]
fileScalars