Skip to content

Commit db19ff4

Browse files
committed
add Progress file
1 parent 3c47764 commit db19ff4

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

cardano-db/src/Cardano/Db/Progress.hs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
3+
module Cardano.Db.Progress (
4+
-- * Types
5+
Progress(..),
6+
ProgressRef,
7+
8+
-- * Progress creation and management
9+
initProgress,
10+
updateProgress,
11+
12+
-- * Rendering
13+
renderProgressBar,
14+
withProgress,
15+
) where
16+
17+
import Control.Concurrent (threadDelay)
18+
import Control.Monad.IO.Class (MonadIO, liftIO)
19+
import Data.IORef (IORef, newIORef, readIORef, modifyIORef')
20+
import Data.Text (Text)
21+
import qualified Data.Text as Text
22+
import Data.Time.Clock (UTCTime, getCurrentTime, diffUTCTime, NominalDiffTime)
23+
import System.IO (hFlush, stdout)
24+
import Text.Printf (printf)
25+
26+
-- | Generic progress tracking data type
27+
data Progress = Progress
28+
{ pCurrentStep :: !Int
29+
, pTotalSteps :: !Int
30+
, pCurrentPhase :: !Text
31+
, pStartTime :: !UTCTime
32+
}
33+
deriving (Show)
34+
35+
type ProgressRef = IORef Progress
36+
37+
-- | Initialize a new progress tracker
38+
initProgress :: MonadIO m => Int -> Text -> m ProgressRef
39+
initProgress totalSteps initialPhase = liftIO $ do
40+
startTime <- getCurrentTime
41+
newIORef $ Progress 0 totalSteps initialPhase startTime
42+
43+
-- | Update progress with new step and phase
44+
updateProgress :: MonadIO m => ProgressRef -> Int -> Text -> m ()
45+
updateProgress progressRef step phase = liftIO $ do
46+
modifyIORef' progressRef $ \p -> p
47+
{ pCurrentStep = step
48+
, pCurrentPhase = phase
49+
}
50+
renderProgressBar =<< readIORef progressRef
51+
52+
-- | Render the progress bar to stdout
53+
renderProgressBar :: Progress -> IO ()
54+
renderProgressBar progress = do
55+
let percentage :: Double
56+
percentage = if pTotalSteps progress == 0
57+
then 0
58+
else fromIntegral (pCurrentStep progress) / fromIntegral (pTotalSteps progress) * 100
59+
barWidth = 50
60+
filled = round (fromIntegral barWidth * percentage / 100)
61+
bar = replicate filled '' ++ replicate (barWidth - filled) ''
62+
63+
-- Calculate elapsed time
64+
currentTime <- getCurrentTime
65+
let elapsed = diffUTCTime currentTime (pStartTime progress)
66+
elapsedStr = formatDuration elapsed
67+
68+
putStr $
69+
"\r\ESC[K" -- Clear entire line
70+
++ show (pCurrentStep progress)
71+
++ "/"
72+
++ show (pTotalSteps progress)
73+
++ " ["
74+
++ bar
75+
++ "] "
76+
++ printf "%.1f%% - " percentage
77+
++ Text.unpack (pCurrentPhase progress)
78+
++ " (" ++ elapsedStr ++ ")"
79+
hFlush stdout
80+
81+
-- | Format duration as MM:SS or HH:MM:SS
82+
formatDuration :: NominalDiffTime -> String
83+
formatDuration duration
84+
| totalSeconds < 3600 = printf "%02d:%02d" minutes seconds
85+
| otherwise = printf "%02d:%02d:%02d" hours minutes seconds
86+
where
87+
totalSeconds = round duration :: Int
88+
hours = totalSeconds `div` 3600
89+
minutes = (totalSeconds `mod` 3600) `div` 60
90+
seconds = totalSeconds `mod` 60
91+
92+
-- | Run an action with progress tracking, cleaning up the display afterward
93+
withProgress :: MonadIO m => Int -> Text -> (ProgressRef -> m a) -> m a
94+
withProgress totalSteps initialPhase action = do
95+
-- liftIO $ putStrLn "" -- Start with a new line
96+
progressRef <- initProgress totalSteps initialPhase
97+
liftIO $ renderProgressBar =<< readIORef progressRef
98+
result <- action progressRef
99+
liftIO $ threadDelay 100000 -- Small delay to make progress visible
100+
liftIO $ do
101+
putStrLn "✅ Operation completed!"
102+
pure result

0 commit comments

Comments
 (0)