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 |