{-# language PackageImports #-} module System.ProgressBar ( -- * Progress bars ProgressBar , progressBar , autoProgressBar , hProgressBar , mkProgressBar -- * Progress state , Progress(..) -- * Labels , Label , noLabel , msg , percentage , exact -- * Auto printing , ProgressRef , startProgress , incProgress ) where import "async" Control.Concurrent.Async ( Async ) import "base" System.IO ( Handle, stderr ) import "this" System.ProgressBar.State ( Progress(..) ) import qualified "this" System.ProgressBar.State as State -- | Type of functions producing a progress bar. type ProgressBar a = Label -- ^ Prefixed label. -> Label -- ^ Postfixed label. -> Integer -- ^ Total progress bar width in characters. Either used as given -- or as a default when the width of the terminal can not be -- determined. -- -- See 'autoProgressBar'. -> Progress -- ^ Current progress. -> a -- | Print a progress bar to 'stderr' -- -- See 'hProgressBar'. progressBar :: ProgressBar (IO ()) progressBar = hProgressBar stderr -- | Print a progress bar to 'stderr' which takes up all available space. -- -- The given width will be used if the width of the terminal can not -- be determined. -- -- See 'hProgressBar'. autoProgressBar :: ProgressBar (IO ()) autoProgressBar = State.autoProgressBar -- | Print a progress bar to a file handle. -- -- Erases the current line! (by outputting '\r') Does not print a -- newline '\n'. Subsequent invocations will overwrite the previous -- output. hProgressBar :: Handle -> ProgressBar (IO ()) hProgressBar = State.hProgressBar -- | Renders a progress bar -- -- >>> mkProgressBar (msg "Working") percentage 40 30 100 -- "Working [=======>.................] 30%" mkProgressBar :: ProgressBar String mkProgressBar = State.mkProgressBar -- | A label that can be pre- or postfixed to a progress bar. type Label = Progress -- ^ Current progress. -> String -- ^ Resulting label. -- | The empty label. -- -- >>> noLabel 30 100 -- "" noLabel :: Label noLabel = State.noLabel -- | A label consisting of a static string. -- -- >>> msg "foo" 30 100 -- "foo" msg :: String -> Label msg = State.msg -- | A label which displays the progress as a percentage. -- -- Constant width property: -- ∀ d t : ℕ. d ≤ t → length (percentage d t) ≡ 4 -- -- >>> percentage 30 100 -- " 30%" -- -- __Note__: if no work is to be done (todo == 0) the percentage will -- always be 100%. -- ∀ d t : ℕ. d ≤ t -> length (percentage d t) ≡ 3 percentage :: Label percentage = State.percentage -- | A label which displays the progress as a fraction of the total -- amount of work. -- -- Equal width property: -- ∀ d₁ d₂ t : ℕ. d₁ ≤ d₂ ≤ t → length (exact d₁ t) ≡ length (exact d₂ t) -- -- >>> exact 30 100 -- " 30/100" -- ∀ d₁ d₂ t : ℕ. d₁ ≤ d₂ ≤ t -> length (exact d₁ t) ≡ length (exact d₂ t) exact :: Label exact = State.exact -- * Auto-Printing Progress type ProgressRef = State.ProgressRef Progress -- | Start a thread to automatically display progress. Use incProgress to step -- the progress bar. startProgress :: Label -- ^ Prefixed label. -> Label -- ^ Postfixed label. -> Integer -- ^ Total progress bar width in characters. Only used if the -- width can not be automatically determined. -> Progress -- ^ Initial progress state. -> IO (ProgressRef, Async ()) startProgress = State.startProgress -- | Increment the progress bar. Negative values will reverse the progress. -- Progress will never be negative and will silently stop taking data -- when it completes. incProgress :: ProgressRef -> Integer -> IO () incProgress pr amount = State.incProgress pr (\st -> st { progressDone = progressDone st + amount })