Skip to content

Commit

Permalink
Add Pace parsing
Browse files Browse the repository at this point in the history
- Adds parsing for pace type.

- Replace custom singletons w/ singleton library. Somehow, our
  implementation (ostensibly vendored from the singletons lib)
  caused a seg fault when Showing SomePace in ghci! This showed up
  in GHC 9.8 and 9.10, though it was ghci only; running as an exe was
  fine. I'm not sure why, other than it may have something to do
  with withDict.

  In any case, switching to the actual singletons lib fixes the
  error, and thankfully the lib has a very low dependency footprint.

-
  • Loading branch information
tbidne committed Dec 6, 2024
1 parent cddeb6f commit f98060f
Show file tree
Hide file tree
Showing 20 changed files with 567 additions and 201 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: ci

on:
on: # yamllint disable-line rule:truthy rule:comments
push:
branches:
- main
Expand Down
4 changes: 4 additions & 0 deletions .yamllint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
extends: default

ignore-from-file: .gitignore
1 change: 1 addition & 0 deletions hie.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
cradle:
cabal:
- path: "./src"
Expand Down
3 changes: 2 additions & 1 deletion running.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ library
exposed-modules:
Running
Running.Class.Parser
Running.Class.Singleton
Running.Class.Units
Running.Config.Args
Running.Data.Distance
Expand All @@ -61,6 +60,7 @@ library
, megaparsec >=7.0.5 && <9.7
, optparse-applicative >=0.15 && <0.19
, relative-time ^>=0.1
, singletons ^>=3.0.3
, smart-math ^>=0.1
, text >=1.2.3.2 && <2.2
, text-display >=0.0.3.0 && <0.6
Expand Down Expand Up @@ -91,6 +91,7 @@ test-suite unit
Unit.Running.Data.Distance
Unit.Running.Data.Distance.Units
Unit.Running.Data.Duration
Unit.Running.Data.Pace
Unit.Utils

build-depends:
Expand Down
15 changes: 10 additions & 5 deletions src/Running.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ module Running
displaySomePace,
displayPace,
calculatePace,

-- * From Pace
calculateDuration,
)
where

import Running.Class.Singleton (SingI)
import Running.Data.Distance
( Distance (unDistance),
DistanceUnit (Kilometer),
Expand Down Expand Up @@ -52,7 +54,10 @@ calculatePace ::
Pace d PDouble
calculatePace distance duration = mkPace $ duration .% distance.unDistance

-- 3. --pace "5m30s km" --distance marathon
-- Pace Second KM Natural -> Distance Meter Natural -> Duration Second Natural
paceToTime :: Pace d1 a -> Distance d2 a -> Duration Second a
paceToTime = todo
-- | Given a pace and distance, calculates the duration.
calculateDuration ::
(MSemigroup a) =>
Distance d a ->
Pace d a ->
Duration Second a
calculateDuration distance pace = pace.unPace .* distance.unDistance
42 changes: 36 additions & 6 deletions src/Running/Class/Parser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@ module Running.Class.Parser
parseDigits,
parseDigitText,
readDigits,

-- * TimeString
parseTimeString,

-- * Misc
failNonSpace,
nonSpace,
optionalTry,
)
where

import Data.Char qualified as Ch
import Data.Text qualified as T
import Data.Time.Relative qualified as Rel
import Running.Prelude
import Text.Megaparsec (Parsec)
import Text.Megaparsec (Parsec, (<?>))
import Text.Megaparsec qualified as MP
import Text.Read qualified as TR

Expand All @@ -37,25 +46,46 @@ instance Parser PDouble where
Just x -> pure x

-- | Parser combinator for digits with a 'Read' instance.
--
-- @since 0.1
parseDigits :: (Read n) => Parsec Void Text n
parseDigits = parseDigitText >>= readDigits

-- | Parser combinator for digits.
--
-- @since 0.1
parseDigitText :: Parsec Void Text Text
parseDigitText =
MP.takeWhile1P Nothing (\c -> Ch.isDigit c || c == '.')

-- | Read text like "1d2h3m4s", parse w/ relative time into Fractional
-- seconds.
parseTimeString :: forall a. (Fractional a) => MParser a
parseTimeString = do
t <- MP.takeWhile1P Nothing (\c -> Ch.isDigit c || c `T.elem` chars)
case Rel.fromString (unpackText t) of
Left err -> fail $ "Could not read duration: " ++ err
Right rt -> do
let secondsNat = Rel.toSeconds rt
secondsDouble = fromIntegral @_ @Double secondsNat
secondsA = realToFrac @Double @a secondsDouble

pure secondsA
where
chars = "hmds"

readDigits :: (Read n) => Text -> Parsec Void Text n
readDigits b =
case TR.readMaybe (T.unpack b) of
Nothing -> fail $ "Could not read digits: " <> T.unpack b
Just b' -> pure b'

parse :: (Parser a) => Text -> Either Text a
parse t = case MP.runParser parser "Running.Class.Parser.parse" t of
parse t = case MP.runParser (parser <* MP.eof) "Running.Class.Parser.parse" t of
Left err -> Left . T.pack . MP.errorBundlePretty $ err
Right v -> Right v

failNonSpace :: MParser ()
failNonSpace = MP.notFollowedBy nonSpace

nonSpace :: MParser Char
nonSpace = MP.satisfy (not . Ch.isSpace) <?> "non white space"

optionalTry :: MParser a -> MParser (Maybe a)
optionalTry = MP.optional . MP.try
67 changes: 0 additions & 67 deletions src/Running/Class/Singleton.hs

This file was deleted.

4 changes: 0 additions & 4 deletions src/Running/Class/Units.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ module Running.Class.Units
)
where

import Running.Class.Singleton
( SingI (..),
SingKind (Demote, fromSing),
)
import Running.Prelude

-- | Class for units.
Expand Down
15 changes: 11 additions & 4 deletions src/Running/Config/Args.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import Options.Applicative.Help.Chunk (Chunk (Chunk))
import Options.Applicative.Help.Chunk qualified as Chunk
import Options.Applicative.Types (ArgPolicy (Intersperse))
import Running
import Running.Data.Distance (SomeDistance)
import Running.Data.Duration (SomeDuration)
import Running.Data.Pace (Pace)
import Running.Prelude

-- 1. Given pace (min sec), calculate percentages (percent optional, have defaults)
Expand All @@ -42,12 +45,16 @@ import Running.Prelude
--
-- - units? ugh

newtype Args = MkArgs
{ cmd :: Command
}
deriving stock (Eq, Show)

data Command
= Convert
| Scale
deriving stock (Eq, Show)

data Args = MkArgs
{ cmd :: Command
}
deriving stock (Eq, Show)
data ConvertArgs
= ConvertToPace (SomeDistance PDouble) (SomeDuration Double)
| ConvertToDuration (SomeDistance PDouble) (forall d. Pace d Double)
7 changes: 1 addition & 6 deletions src/Running/Data/Distance.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ module Running.Data.Distance
where

import Running.Class.Parser (Parser (parser))
import Running.Class.Singleton
( Sing,
SingI (sing),
fromSingI,
withSingI,
)
import Running.Class.Units (singFactor)
import Running.Data.Distance.Units
( DistanceUnit (Kilometer, Meter, Mile),
Expand Down Expand Up @@ -105,6 +99,7 @@ instance (FromInteger a) => FromInteger (Distance d a) where
instance (ToInteger a) => ToInteger (Distance d a) where
toZ (MkDistance x) = toZ x

-- relies on instances defined in Parser.hs
instance (Parser a) => Parser (Distance d a) where
parser = MkDistance <$> parser

Expand Down
12 changes: 3 additions & 9 deletions src/Running/Data/Distance/Units.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ module Running.Data.Distance.Units
where

import Running.Class.Parser (Parser (parser))
import Running.Class.Singleton
( Sing,
SingI (sing),
SingKind (Demote, fromSing, toSing),
SomeSing (MkSomeSing),
)
import Running.Class.Units (Units (baseFactor))
import Running.Prelude
import Text.Megaparsec qualified as MP
Expand Down Expand Up @@ -82,6 +76,6 @@ instance SingKind DistanceUnit where
fromSing SKilometer = Kilometer
fromSing SMile = Mile

toSing Meter = MkSomeSing SMeter
toSing Kilometer = MkSomeSing SKilometer
toSing Mile = MkSomeSing SMile
toSing Meter = SomeSing SMeter
toSing Kilometer = SomeSing SKilometer
toSing Mile = SomeSing SMile
Loading

0 comments on commit f98060f

Please sign in to comment.