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 | |
| 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
| -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 |
