diff options
| author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-02-15 19:37:25 +0000 | 
|---|---|---|
| committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-02-15 19:37:25 +0000 | 
| commit | 8e8394452565dbd338427c6304c324d5fb51b908 (patch) | |
| tree | a48cf9b3e07597ad8ba2d7ca5f8336a1a57cb00b /server | |
| parent | 294300ea870cc107541302ff1cb034a5ae092bbc (diff) | |
| download | thermoprint-8e8394452565dbd338427c6304c324d5fb51b908.tar thermoprint-8e8394452565dbd338427c6304c324d5fb51b908.tar.gz thermoprint-8e8394452565dbd338427c6304c324d5fb51b908.tar.bz2 thermoprint-8e8394452565dbd338427c6304c324d5fb51b908.tar.xz thermoprint-8e8394452565dbd338427c6304c324d5fb51b908.zip | |
Working prototype of Generic
Diffstat (limited to 'server')
| -rw-r--r-- | server/src/Thermoprint/Server/Printer/Generic.hs | 106 | ||||
| -rw-r--r-- | server/thermoprint-server.cabal | 1 | ||||
| -rw-r--r-- | server/thermoprint-server.nix | 12 | 
3 files changed, 83 insertions, 36 deletions
| diff --git a/server/src/Thermoprint/Server/Printer/Generic.hs b/server/src/Thermoprint/Server/Printer/Generic.hs index b993eaf..2c88b55 100644 --- a/server/src/Thermoprint/Server/Printer/Generic.hs +++ b/server/src/Thermoprint/Server/Printer/Generic.hs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | {-# LANGUAGE FlexibleContexts #-} | 1 | {-# LANGUAGE FlexibleContexts #-} | 
| 2 | {-# LANGUAGE ViewPatterns #-} | 2 | {-# LANGUAGE ViewPatterns #-} | 
| 3 | {-# LANGUAGE OverloadedStrings #-} | 3 | {-# LANGUAGE OverloadedStrings #-} | 
| 4 | {-# LANGUAGE RecordWildCards #-} | ||
| 5 | {-# LANGUAGE TemplateHaskell #-} | ||
| 4 | 6 | ||
| 5 | module Thermoprint.Server.Printer.Generic | 7 | module Thermoprint.Server.Printer.Generic | 
| 6 | ( genericPrint | 8 | ( genericPrint | 
| @@ -13,7 +15,7 @@ import Thermoprint.Server.Printer | |||
| 13 | import System.FileLock | 15 | import System.FileLock | 
| 14 | import System.IO | 16 | import System.IO | 
| 15 | 17 | ||
| 16 | import Data.Sequence (Seq, (|>), (<|), ViewL(..), viewl) | 18 | import Data.Sequence (Seq, (|>), (<|), ViewL(..), viewl, ViewR(..), viewr) | 
| 17 | import qualified Data.Sequence as Seq | 19 | import qualified Data.Sequence as Seq | 
| 18 | 20 | ||
| 19 | import qualified Data.ByteString.Lazy as Lazy (ByteString) | 21 | import qualified Data.ByteString.Lazy as Lazy (ByteString) | 
| @@ -22,14 +24,18 @@ import qualified Data.ByteString.Lazy.Char8 as CLBS | |||
| 22 | 24 | ||
| 23 | import qualified Data.Text.Lazy as Lazy (Text) | 25 | import qualified Data.Text.Lazy as Lazy (Text) | 
| 24 | import qualified Data.Text.Lazy as TL | 26 | import qualified Data.Text.Lazy as TL | 
| 27 | |||
| 28 | import qualified Data.Text as T | ||
| 25 | 29 | ||
| 26 | import Data.Encoding | 30 | import Data.Encoding | 
| 27 | import Data.Encoding.CP437 | 31 | import Data.Encoding.CP437 | 
| 32 | import Data.Binary.Put | ||
| 28 | import Data.Word | 33 | import Data.Word | 
| 29 | 34 | ||
| 30 | import Control.Monad | 35 | import Control.Monad | 
| 31 | import Control.Monad.Reader | 36 | import Control.Monad.Reader | 
| 32 | import Control.Monad.Trans.State | 37 | import Control.Monad.Trans.State | 
| 38 | import Control.Monad.Logger | ||
| 33 | import Control.Monad.IO.Class | 39 | import Control.Monad.IO.Class | 
| 34 | 40 | ||
| 35 | import Control.Monad.Trans.Resource | 41 | import Control.Monad.Trans.Resource | 
| @@ -40,58 +46,55 @@ import Control.Monad.Catch | |||
| 40 | 46 | ||
| 41 | import Data.Foldable | 47 | import Data.Foldable | 
| 42 | 48 | ||
| 43 | import Data.List (genericReplicate, intercalate) | 49 | import Data.List (genericReplicate, genericLength, intercalate) | 
| 50 | |||
| 51 | import Data.Monoid | ||
| 44 | 52 | ||
| 45 | import Data.Int (Int64) | 53 | import Data.Int (Int64) | 
| 46 | 54 | ||
| 47 | import Prelude hiding (mapM_, sequence_) | 55 | import Prelude hiding (mapM_, sequence_, lines) | 
| 48 | 56 | ||
| 49 | genericPrint :: FilePath -> PrinterMethod | 57 | genericPrint :: FilePath -> PrinterMethod | 
| 50 | genericPrint path = PM $ genericPrint' path | 58 | genericPrint path = PM $ genericPrint' path | 
| 51 | 59 | ||
| 52 | genericPrint' :: (MonadIO m, MonadMask m) => FilePath -> Printout -> m (Maybe PrintingError) | 60 | genericPrint' :: (MonadIO m, MonadMask m, MonadLogger m) => FilePath -> Printout -> m (Maybe PrintingError) | 
| 53 | genericPrint' path = flip catches handlers . withLockedFile path . print | 61 | genericPrint' path = flip catches handlers . withFile path . print | 
| 54 | where | 62 | where | 
| 55 | print printout handle = liftIO $ runReaderT (bracket_ initialize (render printout) finalize) handle >> return Nothing | 63 | withFile path f = flip withEx f $ mkAcquire (openFile path WriteMode >>= (\h -> h <$ hSetBuffering h NoBuffering)) hClose | 
| 56 | handlers = [ Handler $ return . Just . IOError . (show :: IOException -> String) | 64 | handlers = [ Handler $ return . Just . IOError . (show :: IOException -> String) | 
| 57 | , Handler $ return . Just . EncError | 65 | , Handler $ return . Just . EncError | 
| 58 | , Handler $ return . Just | 66 | , Handler $ return . Just | 
| 59 | ] | 67 | ] | 
| 60 | 68 | print printout handle = $(logDebug) (T.pack $ show printout') >> liftIO (LBS.hPutStr handle printout' >> return Nothing) | |
| 61 | withLockedFile :: (MonadIO m, MonadMask m) => FilePath -> (Handle -> m a) -> m a | 69 | where | 
| 62 | withLockedFile path f = withEx (lockedFile path) (f . snd) | 70 | printout' = runPut $ initialize >> render printout >> finalize | 
| 63 | |||
| 64 | lockedFile :: FilePath -> Acquire (FileLock, Handle) | ||
| 65 | lockedFile f = mkAcquire ((,) <$> lockFile f Exclusive <*> openFile f WriteMode) (\(lock, h) -> () <$ hClose h <* unlockFile lock) | ||
| 66 | |||
| 67 | type Render = ReaderT Handle IO () | ||
| 68 | 71 | ||
| 69 | encode' :: ByteSink m => String -> m () | 72 | encode' :: ByteSink m => String -> m () | 
| 70 | encode' = encode CP437 | 73 | encode' = encode CP437 | 
| 71 | 74 | ||
| 72 | width :: Int64 | 75 | width :: Integer | 
| 73 | width = 32 | 76 | width = 32 | 
| 74 | 77 | ||
| 75 | esc :: Word8 | 78 | esc :: Word8 | 
| 76 | esc = 27 | 79 | esc = 27 | 
| 77 | 80 | ||
| 78 | escSequence :: [Word8] -> Render | 81 | escSequence :: [Word8] -> Put | 
| 79 | escSequence = mapM_ pushWord8 . (esc:) | 82 | escSequence = mapM_ pushWord8 . (esc:) | 
| 80 | 83 | ||
| 81 | initialize :: Render | 84 | initialize :: Put | 
| 82 | initialize = replicateM_ 2 $ escSequence [64] | 85 | initialize = replicateM_ 2 $ escSequence [64] | 
| 83 | 86 | ||
| 84 | newl :: Lazy.ByteString | 87 | newl :: Lazy.ByteString | 
| 85 | newl = "\r\n" | 88 | newl = encodeLazyByteString CP437 "\n" | 
| 86 | 89 | ||
| 87 | newls :: Integer -> Lazy.ByteString | 90 | newls :: Integer -> Lazy.ByteString | 
| 88 | newls i = mconcat $ genericReplicate i newl | 91 | newls i = mconcat $ genericReplicate i newl | 
| 89 | 92 | ||
| 90 | newls' :: Integer -> Render | 93 | newls' :: Integer -> Put | 
| 91 | newls' = mapM_ pushWord8 . LBS.unpack . newls | 94 | newls' = mapM_ pushWord8 . LBS.unpack . newls | 
| 92 | 95 | ||
| 93 | finalize :: Render -- TODO: adjust this to produce proper padding | 96 | finalize :: Put | 
| 94 | finalize = newls' 2 | 97 | finalize = newls' 3 >> encode' " " >> newls' 1 | 
| 95 | 98 | ||
| 96 | intersperse :: b -> (a -> b) -> Seq a -> Seq b | 99 | intersperse :: b -> (a -> b) -> Seq a -> Seq b | 
| 97 | intersperse _ _ (viewl -> EmptyL) = Seq.empty | 100 | intersperse _ _ (viewl -> EmptyL) = Seq.empty | 
| @@ -101,16 +104,59 @@ intersperse b f (viewl -> x :< xs) = f x <| b <| intersperse b f | |||
| 101 | intersperse' :: Monad m => m b -> (a -> m b) -> Seq a -> m () | 104 | intersperse' :: Monad m => m b -> (a -> m b) -> Seq a -> m () | 
| 102 | intersperse' b f = sequence_ . intersperse b f | 105 | intersperse' b f = sequence_ . intersperse b f | 
| 103 | 106 | ||
| 104 | render :: Printout -> Render | 107 | render :: Printout -> Put | 
| 105 | render = intersperse' (newls' 1) renderPar | 108 | render = intersperse' (newls' 2) renderPar | 
| 106 | 109 | ||
| 107 | renderPar :: Paragraph -> Render | 110 | renderPar :: Paragraph -> Put | 
| 108 | renderPar = mapM_ renderChunk | 111 | renderPar = mapM_ renderChunk | 
| 109 | where | 112 | where | 
| 110 | renderChunk (Raw bs) = mapM_ pushWord8 $ LBS.unpack bs | 113 | renderChunk (Raw bs) = mapM_ pushWord8 $ LBS.unpack bs | 
| 111 | renderChunk (Cooked block) = renderBlock block | 114 | renderChunk (Cooked block) = renderDoc $ execState (renderBlock block) (initDoc width) | 
| 112 | 115 | ||
| 113 | renderBlock :: Block -> Render | 116 | data Doc = Doc | 
| 114 | renderBlock (VSpace n) = newls' n | 117 | { lines :: Seq Put | 
| 115 | renderBlock (NewlSep xs) = intersperse' (newls' 1) renderBlock xs | 118 | , currentLine :: Put | 
| 116 | renderBlock (Line x) = undefined | 119 | , space :: Integer | 
| 120 | , remainingSpace :: Integer | ||
| 121 | , overflows :: Integer | ||
| 122 | } | ||
| 123 | |||
| 124 | initDoc :: Integer -> Doc | ||
| 125 | initDoc space = Doc { lines = Seq.empty | ||
| 126 | , currentLine = return () | ||
| 127 | , space = space | ||
| 128 | , remainingSpace = space | ||
| 129 | , overflows = 0 | ||
| 130 | } | ||
| 131 | |||
| 132 | breakLine :: Doc -> Doc | ||
| 133 | breakLine doc@(Doc{..}) = doc { remainingSpace = space, lines = lines |> currentLine, currentLine = return () } | ||
| 134 | |||
| 135 | renderDoc :: Doc -> Put | ||
| 136 | renderDoc Doc{..} = intersperse' (newls' 1) id lines >> currentLine | ||
| 137 | |||
| 138 | renderBlock :: Block -> State Doc () | ||
| 139 | renderBlock (VSpace n) = sequence_ . genericReplicate n $ modify' breakLine | ||
| 140 | renderBlock (NewlSep xs) = intersperse' (modify' breakLine) renderBlock xs | ||
| 141 | renderBlock (Line x) = renderLine x | ||
| 142 | |||
| 143 | renderLine :: Line -> State Doc () | ||
| 144 | renderLine (HSpace n) = modify' insertSpace | ||
| 145 | where | ||
| 146 | insertSpace doc@(Doc{..}) | ||
| 147 | | remainingSpace > n = doc { remainingSpace = remainingSpace - n, currentLine = currentLine >> (sequence_ $ genericReplicate n $ encode' " ") } | ||
| 148 | | remainingSpace == n = doc { remainingSpace = space, currentLine = return (), lines = lines |> currentLine } | ||
| 149 | | otherwise = doc { remainingSpace = space, lines = lines |> currentLine, currentLine = return (), overflows = overflows + 1 } | ||
| 150 | renderLine (JuxtaPos xs) = mapM_ renderLine xs | ||
| 151 | renderLine word = modify' $ insertWord | ||
| 152 | where | ||
| 153 | insertWord doc@(Doc{..}) | ||
| 154 | | remainingSpace > length = doc { remainingSpace = remainingSpace - (length + 1), currentLine = currentLine >> word'' } | ||
| 155 | | remainingSpace == length = doc { remainingSpace = space, lines = lines |> (currentLine >> word''), currentLine = return () } | ||
| 156 | | space >= length = doc { remainingSpace = space - length, lines = lines |> currentLine, currentLine = word'' } | ||
| 157 | | length `div` space == 0 = doc { remainingSpace = space, lines = (lines |> currentLine) <> (cs |> c), currentLine = return (), overflows = overflows + (toEnum $ Seq.length cs) } | ||
| 158 | | otherwise = doc { remainingSpace = space - (length `div` space), lines = (lines |> currentLine) <> cs, currentLine = c, overflows = overflows + (toEnum $ Seq.length cs) } | ||
| 159 | word' = cotext (Line word) | ||
| 160 | word'' = encode' $ TL.unpack $ word' | ||
| 161 | length = toInteger $ TL.length word' | ||
| 162 | (cs :> c) = viewr . Seq.fromList . map (encode' . TL.unpack) $ TL.chunksOf (fromInteger length) word' | ||
| diff --git a/server/thermoprint-server.cabal b/server/thermoprint-server.cabal index 5e9c61f..1ad4d4d 100644 --- a/server/thermoprint-server.cabal +++ b/server/thermoprint-server.cabal | |||
| @@ -53,6 +53,7 @@ library | |||
| 53 | , filelock >=0.1.0 && <1 | 53 | , filelock >=0.1.0 && <1 | 
| 54 | , bytestring >=0.10.6 && <1 | 54 | , bytestring >=0.10.6 && <1 | 
| 55 | , encoding >=0.8 && <1 | 55 | , encoding >=0.8 && <1 | 
| 56 | , binary >=0.7.5 && <1 | ||
| 56 | hs-source-dirs: src | 57 | hs-source-dirs: src | 
| 57 | default-language: Haskell2010 | 58 | default-language: Haskell2010 | 
| 58 | 59 | ||
| diff --git a/server/thermoprint-server.nix b/server/thermoprint-server.nix index d4ebb60..77911a8 100644 --- a/server/thermoprint-server.nix +++ b/server/thermoprint-server.nix | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | { mkDerivation, base, bytestring, conduit, containers | 1 | { mkDerivation, base, binary, bytestring, conduit, containers | 
| 2 | , data-default-class, deepseq, dyre, either, encoding, exceptions | 2 | , data-default-class, deepseq, dyre, either, encoding, exceptions | 
| 3 | , extended-reals, filelock, hspec, mmorph, monad-control | 3 | , extended-reals, filelock, hspec, mmorph, monad-control | 
| 4 | , monad-logger, mtl, persistent, persistent-sqlite | 4 | , monad-logger, mtl, persistent, persistent-sqlite | 
| @@ -13,11 +13,11 @@ mkDerivation { | |||
| 13 | isLibrary = true; | 13 | isLibrary = true; | 
| 14 | isExecutable = true; | 14 | isExecutable = true; | 
| 15 | libraryHaskellDepends = [ | 15 | libraryHaskellDepends = [ | 
| 16 | base bytestring conduit containers data-default-class deepseq dyre | 16 | base binary bytestring conduit containers data-default-class | 
| 17 | either encoding exceptions extended-reals filelock mmorph | 17 | deepseq dyre either encoding exceptions extended-reals filelock | 
| 18 | monad-control monad-logger mtl persistent persistent-template | 18 | mmorph monad-control monad-logger mtl persistent | 
| 19 | resourcet servant-server stm text thermoprint-spec time | 19 | persistent-template resourcet servant-server stm text | 
| 20 | transformers wai warp | 20 | thermoprint-spec time transformers wai warp | 
| 21 | ]; | 21 | ]; | 
| 22 | executableHaskellDepends = [ | 22 | executableHaskellDepends = [ | 
| 23 | base monad-logger mtl persistent-sqlite resourcet | 23 | base monad-logger mtl persistent-sqlite resourcet | 
