Code source du site TeacherCorner.lamdera.app, contenant une suite d'outils permettant d'automatiser la production de documents pédagogiques.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

247 lines
5.3 KiB

module Zip exposing
( Zip
, fromBytes
, entries
, getEntry
, count
, isEmpty
, empty
, fromEntries
, insert
, filter
, toBytes
)
{-| Work with [Zip archives](https://en.wikipedia.org/wiki/ZIP_file_format).
@docs Zip
# Read an archive
@docs fromBytes
# Access the content
Once you have a `Zip`, you can use it to access its files and directories.
Use the [Zip.Entry module](./Zip-Entry#Entry) to do read their content and metadata.
@docs entries
@docs getEntry
@docs count
@docs isEmpty
# Build an archive
You can alter archives or create your own.
Checkout the [Build section](./Zip-Entry#build) of the `Zip.Entry` module to learn how to make your own entries.
@docs empty
@docs fromEntries
@docs insert
@docs filter
## ...and when it's ready
@docs toBytes
-}
import Bytes exposing (Bytes)
import Internal.Decode exposing (readDirectory)
import Internal.Encode exposing (writeArchive)
import Internal.Format exposing (Entry)
import Zip.Entry as Entry
{-| Represents a Zip archive.
An archive is comprised of [entries](./Zip-Entry#Entry) which represent files -that may be compressed- and directories.
-}
type Zip
= Zip (List Entry)
{-| Read a `Zip` from `Bytes`.
If you have [an uploaded File](https://package.elm-lang.org/packages/elm/file/latest/File) of an archive,
you can use [`File.toBytes`](https://package.elm-lang.org/packages/elm/file/latest/File#toBytes) to read it:
import File exposing (File)
import Task exposing (Task)
import Zip exposing (Zip)
type Msg
= GotZip (Maybe Zip)
readArchive : File -> Cmd Msg
readArchive file =
file
|> File.toBytes
|> Task.map Zip.fromBytes
|> Task.perform GotZip
You can also get `Bytes` from somewhere else, such as [an HTTP request](https://package.elm-lang.org/packages/elm/http/latest/Http#expectBytes),
or even from [within another archive](./Zip-Entry#toBytes).
-}
fromBytes : Bytes -> Maybe Zip
fromBytes bytes =
readDirectory bytes |> Maybe.map Zip
{-| Write a `Zip` to `Bytes`.
From here, you can [download the archive](https://package.elm-lang.org/packages/elm/file/latest/File-Download#bytes),
[upload it to a server](https://package.elm-lang.org/packages/elm/http/latest/Http#bytesBody>), etc.
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
DownloadArchive ->
( model
, model.zip
|> Zip.toBytes
|> File.Download.bytes "archive.zip" "application/zip"
)
-}
toBytes : Zip -> Bytes
toBytes (Zip allEntries) =
writeArchive allEntries
{-| Get all [entries](./Zip-Entry#Entry) in the archive.
allEntries =
Zip.entries zip
Files and directories get their own entries.
If you only care about one kind, you can use the [`Zip.Entry.isDirectory`](./Zip-Entry#isDirectory) function to filter them:
allFiles =
zip
|> Zip.entries
|> List.filter (not << Entry.isDirectory)
-}
entries : Zip -> List Entry
entries (Zip allEntries) =
allEntries
{-| Get an [entry](./Zip-Entry#Entry) by its absolute path.
zip |> Zip.getEntry "versions/v1.txt"
`Nothing` is returned if no entry matches the path exactly.
Directory entries are typically stored in the archive with a slash at the end:
zip |> Zip.getEntry "versions" == Nothing
zip |> Zip.getEntry "versions/" == Just (Entry(..))
-}
getEntry : String -> Zip -> Maybe Entry
getEntry path =
entries >> find (Entry.path >> (==) path)
{-| Count the number of entries in an archive.
-}
count : Zip -> Int
count =
entries >> List.length
{-| Determine if an archive is empty.
-}
isEmpty : Zip -> Bool
isEmpty =
entries >> List.isEmpty
{-| An empty archive with no entries.
From here, you can use [`insert`](#insert) to add some entries.
-}
empty : Zip
empty =
Zip []
{-| Create an archive from a list of entries.
-}
fromEntries : List Entry -> Zip
fromEntries =
Zip
{-| Add a new entry to the archive.
This function replaces entries with the same path. You can conditionally add it by checking existence with the [`getEntry`](#getEntry) function:
case zip |> Zip.getEntry path of
Nothing ->
-- Entry does not exist, create and add it
zip |> Zip.insert (createEntry ())
Just _ ->
-- Entry already exists, leave archive as it is
zip
-}
insert : Entry -> Zip -> Zip
insert entry (Zip currentEntries) =
currentEntries
|> List.filter (Entry.path >> (/=) (Entry.path entry))
|> (::) entry
|> Zip
{-| Only keep entries that pass a given test.
### Examples
Remove entries by path:
filter (\entry -> Entry.path entry /= "sample/version.json") zip
Keep all files under 1MB:
filter (\entry -> Entry.extractedSize entry < 1048576) zip
Keep only `.txt` files:
filter (Entry.path >> String.endsWith ".txt") zip
-}
filter : (Entry -> Bool) -> Zip -> Zip
filter check (Zip currentEntries) =
currentEntries
|> List.filter check
|> Zip
find : (a -> Bool) -> List a -> Maybe a
find check list =
case list of
[] ->
Nothing
item :: tail ->
if check item then
Just item
else
find check tail