module Ecluse.Core.Text (
nonBlank,
stripTrailingSlash,
joinUrlPath,
renderIso8601Utc,
) where
import Data.Text qualified as T
import Data.Text.Lazy qualified as TL
import Data.Text.Lazy.Builder qualified as TB
import Data.Text.Lazy.Builder.Int qualified as TBI
import Data.Time (UTCTime (UTCTime), diffTimeToPicoseconds, toGregorian)
import Data.Time.Format.ISO8601 (iso8601Show)
nonBlank :: Text -> Maybe Text
nonBlank :: Text -> Maybe Text
nonBlank Text
t =
let trimmed :: Text
trimmed = Text -> Text
T.strip Text
t
in if Text -> Bool
T.null Text
trimmed then Maybe Text
forall a. Maybe a
Nothing else Text -> Maybe Text
forall a. a -> Maybe a
Just Text
trimmed
stripTrailingSlash :: Text -> Text
stripTrailingSlash :: Text -> Text
stripTrailingSlash Text
b = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
b (Text -> Text -> Maybe Text
T.stripSuffix Text
"/" Text
b)
joinUrlPath :: Text -> Text -> Text
joinUrlPath :: Text -> Text -> Text
joinUrlPath Text
b Text
path = Text -> Text
stripTrailingSlash Text
b Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"/" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
path
renderIso8601Utc :: UTCTime -> Text
renderIso8601Utc :: UTCTime -> Text
renderIso8601Utc t :: UTCTime
t@(UTCTime Day
day DiffTime
dt)
| Integer
year Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
0 Bool -> Bool -> Bool
|| Integer
year Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
9999 Bool -> Bool -> Bool
|| Integer
picos Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
86_400_000_000_000_000 = String -> Text
forall a. ToText a => a -> Text
toText (UTCTime -> String
forall t. ISO8601 t => t -> String
iso8601Show UTCTime
t)
| Bool
otherwise =
LazyText -> Text
TL.toStrict (LazyText -> Text) -> (Builder -> LazyText) -> Builder -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyText
TB.toLazyText (Builder -> Text) -> Builder -> Text
forall a b. (a -> b) -> a -> b
$
MonthOfYear -> Integer -> Builder
digits MonthOfYear
4 Integer
year
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"-"
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> MonthOfYear -> Integer -> Builder
digits MonthOfYear
2 (MonthOfYear -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral MonthOfYear
month)
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"-"
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> MonthOfYear -> Integer -> Builder
digits MonthOfYear
2 (MonthOfYear -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral MonthOfYear
dayOfMonth)
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"T"
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> MonthOfYear -> Integer -> Builder
digits MonthOfYear
2 Integer
hh
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
":"
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> MonthOfYear -> Integer -> Builder
digits MonthOfYear
2 Integer
mm
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
":"
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> MonthOfYear -> Integer -> Builder
digits MonthOfYear
2 Integer
ss
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
fraction
Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"Z"
where
(Integer
year, MonthOfYear
month, MonthOfYear
dayOfMonth) = Day -> (Integer, MonthOfYear, MonthOfYear)
toGregorian Day
day
picos :: Integer
picos = DiffTime -> Integer
diffTimeToPicoseconds DiffTime
dt
(Integer
secondsOfDay, Integer
frac) = Integer
picos Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`divMod` Integer
1_000_000_000_000
(Integer
hh, Integer
rem') = Integer
secondsOfDay Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`divMod` Integer
3600
(Integer
mm, Integer
ss) = Integer
rem' Integer -> Integer -> (Integer, Integer)
forall a. Integral a => a -> a -> (a, a)
`divMod` Integer
60
digits :: Int -> Integer -> TB.Builder
digits :: MonthOfYear -> Integer -> Builder
digits MonthOfYear
width Integer
n =
let body :: String
body = Integer -> String
forall b a. (Show a, IsString b) => a -> b
show Integer
n :: String
pad :: MonthOfYear
pad = MonthOfYear
width MonthOfYear -> MonthOfYear -> MonthOfYear
forall a. Num a => a -> a -> a
- String -> MonthOfYear
forall a. [a] -> MonthOfYear
forall (t :: * -> *) a. Foldable t => t a -> MonthOfYear
length String
body
in String -> Builder
TB.fromString (MonthOfYear -> Char -> String
forall a. MonthOfYear -> a -> [a]
replicate MonthOfYear
pad Char
'0') Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Integer -> Builder
forall a. Integral a => a -> Builder
TBI.decimal Integer
n
fraction :: TB.Builder
fraction :: Builder
fraction
| Integer
frac Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
0 = Builder
forall a. Monoid a => a
mempty
| Bool
otherwise =
Text -> Builder
TB.fromText (Text
"." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (Char -> Bool) -> Text -> Text
T.dropWhileEnd (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'0') (MonthOfYear -> Char -> Text -> Text
T.justifyRight MonthOfYear
12 Char
'0' (Integer -> Text
forall b a. (Show a, IsString b) => a -> b
show Integer
frac)))