packages feed

cherry-core-alpha-0.5.0.0: src/File.hs

{-# LANGUAGE PackageImports #-}

{-|

Module      : File
Description : Read and write to a file.
License     : BSD 3
Maintainer  : terezasokol@gmail.com
Stability   : experimental
Portability : POSIX

Read and write to a file.

-}

module File (Path, read, rename, write, delete, doesExist, list) where

import qualified List
import qualified String
import qualified Data.Maybe
import qualified System.IO.Error
import qualified Control.Exception
import qualified Internal.Task as Task
import qualified Internal.Utils as U
import qualified "text-utf8" Data.Text.IO as IO
import qualified System.Directory as Directory
import qualified Prelude as P
import Prelude (return, getContents, sequence)
import Data.List (stripPrefix)
import Basics
import Maybe (Maybe (..))
import Result (Result (..))
import String (String)
import Dict (Dict)
import List (List)
import Array (Array)
import Task (Task)
import Set (Set)
import Char (Char)


{-| -}
type Path
  = String


{-| -}
write :: Path -> String -> Task x ()
write path string =
  Task.Task <| do
    let dir = String.split "/" path |> List.reverse |> List.drop 1 |> List.reverse |> String.join "/"
    Directory.createDirectoryIfMissing True (String.toList dir)
    IO.writeFile (String.toList path) (String.toTextUtf8 string)
    return (Ok ())


{-| -}
rename :: Path -> Path -> Task x ()
rename old new =
  Task.Task <|
    let handleError err =
          if System.IO.Error.isDoesNotExistError err
            then return ()
            else Control.Exception.throwIO err
    in
    P.fmap Ok (Directory.renameFile (String.toList old) (String.toList new) `Control.Exception.catch` handleError)


{-| -}
delete :: Path -> Task x ()
delete path =
  Task.Task <|
    let handleError err =
          if System.IO.Error.isDoesNotExistError err
            then return ()
            else Control.Exception.throwIO err
    in
    P.fmap Ok (Directory.removeFile (String.toList path) `Control.Exception.catch` handleError)


{-| -}
doesExist :: Path -> Task x Bool
doesExist path =
  Task.Task <| do
    bool <- Directory.doesFileExist (String.toList path)
    return (Ok bool)


{-| -}
read :: Path -> Task x String
read filename =
  Task.Task <| do
    contents <- IO.readFile (String.toList filename)
    return (Ok (String.fromTextUtf8 contents))


{-| -}
list :: Path -> Task String (List Path)
list path =
  Task.Task <|
    let
        findFiles :: P.String -> List P.String -> P.IO (List P.String)
        findFiles folder acc = do
          items <- Directory.listDirectory folder
          newfiles <- List.foldl checkOne (return []) (List.map (\i -> folder ++ "/" ++ i) items)
          return (acc ++ newfiles)

        checkOne :: P.String -> P.IO (List P.String) -> P.IO (List P.String)
        checkOne name acc = do
          exists <- Directory.doesDirectoryExist name
          if exists then do
            files <- acc
            findFiles name files
          else
            P.fmap (\fs -> name : fs) acc
    in do
    let directory = String.toList path
    exists <- Directory.doesDirectoryExist directory
    if exists then do
      files <- findFiles directory []
      let final = Data.Maybe.mapMaybe (stripPrefix (directory ++ "/")) files
      return (Ok (List.map String.fromList final))
    else
      return (Err "Given path is not a directory.")