Search code examples
haskellhaskell-snap-framework

Converting IO [FilePath] to String or Bytestream


I'm working on this project to get feet wet with Haskell and struggling with finding simple examples.

In this instance, I would like to have a web request handler for Snap, which returns a list of files in a directory.

I believe I'm trying to get the return of getDirectoryContents into a Bytestring which Snap wants.

I am most confused about what to do with the return value I get at the line filenames <- getDirectoryContents "data" below:

import Control.Applicative
import Snap.Core
import Snap.Util.FileServe
import Snap.Http.Server
import System.Directory (getDirectoryContents)

main :: IO ()
main = quickHttpServe site

site :: Snap ()
site =
    ifTop (writeBS "hello world") <|> 
    route [ ("foo", writeBS "bar")
          , ("echo/:echoparam", echoHandler)
          , ("view_root_json_files", listRootFilesHandler)
          ] <|> 
    dir "static" (serveDirectory ".")

echoHandler :: Snap ()
echoHandler = do
    param <- getParam "echoparam"
    maybe (writeBS "must specify echo/param in URL")
          writeBS param

listRootFilesHandler :: Snap ()
listRootFilesHandler = do
    -- read all filenames in /data folders
    filenames <- getDirectoryContents "data"
    writeText filenames

Solution

  • Since you want to use writeText, you need to convert [FilePath] to Text. Luckily, Text is an instance of Monoid, and a list is a instance of Foldable, so we can simply use foldMap pack filenames to get a single text:

    -- import Data.Foldable (foldMap)
    -- import Data.Text (pack, Text)
    
    toText :: [FilePath] -> Text
    toText = foldMap pack
    

    Note that you'll need to use liftIO to actually use a IO a in Snap b, since Snap is an instance of MonadIO:

    listRootFilesHandler :: Snap ()
    listRootFilesHandler = do
        filenames <- liftIO $ getDirectoryContents "data"
        writeText $ toText filenames
    

    If you want to add a newline (or <br/>) after each FilePath, add flip snoc '\n':

    toText = foldMap (flip snoc '\n' . pack)
    -- toText = foldMap (flip append (pack "<br/>") . pack)