aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2016-02-15 19:37:25 +0000
committerGregor Kleen <gkleen@yggdrasil.li>2016-02-15 19:37:25 +0000
commit8e8394452565dbd338427c6304c324d5fb51b908 (patch)
treea48cf9b3e07597ad8ba2d7ca5f8336a1a57cb00b
parent294300ea870cc107541302ff1cb034a5ae092bbc (diff)
downloadthermoprint-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.hs106
-rw-r--r--server/thermoprint-server.cabal1
-rw-r--r--server/thermoprint-server.nix12
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
5module Thermoprint.Server.Printer.Generic 7module Thermoprint.Server.Printer.Generic
6 ( genericPrint 8 ( genericPrint
@@ -13,7 +15,7 @@ import Thermoprint.Server.Printer
13import System.FileLock 15import System.FileLock
14import System.IO 16import System.IO
15 17
16import Data.Sequence (Seq, (|>), (<|), ViewL(..), viewl) 18import Data.Sequence (Seq, (|>), (<|), ViewL(..), viewl, ViewR(..), viewr)
17import qualified Data.Sequence as Seq 19import qualified Data.Sequence as Seq
18 20
19import qualified Data.ByteString.Lazy as Lazy (ByteString) 21import qualified Data.ByteString.Lazy as Lazy (ByteString)
@@ -22,14 +24,18 @@ import qualified Data.ByteString.Lazy.Char8 as CLBS
22 24
23import qualified Data.Text.Lazy as Lazy (Text) 25import qualified Data.Text.Lazy as Lazy (Text)
24import qualified Data.Text.Lazy as TL 26import qualified Data.Text.Lazy as TL
27
28import qualified Data.Text as T
25 29
26import Data.Encoding 30import Data.Encoding
27import Data.Encoding.CP437 31import Data.Encoding.CP437
32import Data.Binary.Put
28import Data.Word 33import Data.Word
29 34
30import Control.Monad 35import Control.Monad
31import Control.Monad.Reader 36import Control.Monad.Reader
32import Control.Monad.Trans.State 37import Control.Monad.Trans.State
38import Control.Monad.Logger
33import Control.Monad.IO.Class 39import Control.Monad.IO.Class
34 40
35import Control.Monad.Trans.Resource 41import Control.Monad.Trans.Resource
@@ -40,58 +46,55 @@ import Control.Monad.Catch
40 46
41import Data.Foldable 47import Data.Foldable
42 48
43import Data.List (genericReplicate, intercalate) 49import Data.List (genericReplicate, genericLength, intercalate)
50
51import Data.Monoid
44 52
45import Data.Int (Int64) 53import Data.Int (Int64)
46 54
47import Prelude hiding (mapM_, sequence_) 55import Prelude hiding (mapM_, sequence_, lines)
48 56
49genericPrint :: FilePath -> PrinterMethod 57genericPrint :: FilePath -> PrinterMethod
50genericPrint path = PM $ genericPrint' path 58genericPrint path = PM $ genericPrint' path
51 59
52genericPrint' :: (MonadIO m, MonadMask m) => FilePath -> Printout -> m (Maybe PrintingError) 60genericPrint' :: (MonadIO m, MonadMask m, MonadLogger m) => FilePath -> Printout -> m (Maybe PrintingError)
53genericPrint' path = flip catches handlers . withLockedFile path . print 61genericPrint' 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)
61withLockedFile :: (MonadIO m, MonadMask m) => FilePath -> (Handle -> m a) -> m a 69 where
62withLockedFile path f = withEx (lockedFile path) (f . snd) 70 printout' = runPut $ initialize >> render printout >> finalize
63
64lockedFile :: FilePath -> Acquire (FileLock, Handle)
65lockedFile f = mkAcquire ((,) <$> lockFile f Exclusive <*> openFile f WriteMode) (\(lock, h) -> () <$ hClose h <* unlockFile lock)
66
67type Render = ReaderT Handle IO ()
68 71
69encode' :: ByteSink m => String -> m () 72encode' :: ByteSink m => String -> m ()
70encode' = encode CP437 73encode' = encode CP437
71 74
72width :: Int64 75width :: Integer
73width = 32 76width = 32
74 77
75esc :: Word8 78esc :: Word8
76esc = 27 79esc = 27
77 80
78escSequence :: [Word8] -> Render 81escSequence :: [Word8] -> Put
79escSequence = mapM_ pushWord8 . (esc:) 82escSequence = mapM_ pushWord8 . (esc:)
80 83
81initialize :: Render 84initialize :: Put
82initialize = replicateM_ 2 $ escSequence [64] 85initialize = replicateM_ 2 $ escSequence [64]
83 86
84newl :: Lazy.ByteString 87newl :: Lazy.ByteString
85newl = "\r\n" 88newl = encodeLazyByteString CP437 "\n"
86 89
87newls :: Integer -> Lazy.ByteString 90newls :: Integer -> Lazy.ByteString
88newls i = mconcat $ genericReplicate i newl 91newls i = mconcat $ genericReplicate i newl
89 92
90newls' :: Integer -> Render 93newls' :: Integer -> Put
91newls' = mapM_ pushWord8 . LBS.unpack . newls 94newls' = mapM_ pushWord8 . LBS.unpack . newls
92 95
93finalize :: Render -- TODO: adjust this to produce proper padding 96finalize :: Put
94finalize = newls' 2 97finalize = newls' 3 >> encode' " " >> newls' 1
95 98
96intersperse :: b -> (a -> b) -> Seq a -> Seq b 99intersperse :: b -> (a -> b) -> Seq a -> Seq b
97intersperse _ _ (viewl -> EmptyL) = Seq.empty 100intersperse _ _ (viewl -> EmptyL) = Seq.empty
@@ -101,16 +104,59 @@ intersperse b f (viewl -> x :< xs) = f x <| b <| intersperse b f
101intersperse' :: Monad m => m b -> (a -> m b) -> Seq a -> m () 104intersperse' :: Monad m => m b -> (a -> m b) -> Seq a -> m ()
102intersperse' b f = sequence_ . intersperse b f 105intersperse' b f = sequence_ . intersperse b f
103 106
104render :: Printout -> Render 107render :: Printout -> Put
105render = intersperse' (newls' 1) renderPar 108render = intersperse' (newls' 2) renderPar
106 109
107renderPar :: Paragraph -> Render 110renderPar :: Paragraph -> Put
108renderPar = mapM_ renderChunk 111renderPar = 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
113renderBlock :: Block -> Render 116data Doc = Doc
114renderBlock (VSpace n) = newls' n 117 { lines :: Seq Put
115renderBlock (NewlSep xs) = intersperse' (newls' 1) renderBlock xs 118 , currentLine :: Put
116renderBlock (Line x) = undefined 119 , space :: Integer
120 , remainingSpace :: Integer
121 , overflows :: Integer
122 }
123
124initDoc :: Integer -> Doc
125initDoc space = Doc { lines = Seq.empty
126 , currentLine = return ()
127 , space = space
128 , remainingSpace = space
129 , overflows = 0
130 }
131
132breakLine :: Doc -> Doc
133breakLine doc@(Doc{..}) = doc { remainingSpace = space, lines = lines |> currentLine, currentLine = return () }
134
135renderDoc :: Doc -> Put
136renderDoc Doc{..} = intersperse' (newls' 1) id lines >> currentLine
137
138renderBlock :: Block -> State Doc ()
139renderBlock (VSpace n) = sequence_ . genericReplicate n $ modify' breakLine
140renderBlock (NewlSep xs) = intersperse' (modify' breakLine) renderBlock xs
141renderBlock (Line x) = renderLine x
142
143renderLine :: Line -> State Doc ()
144renderLine (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 }
150renderLine (JuxtaPos xs) = mapM_ renderLine xs
151renderLine 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