0 | module IotaTime.Tzdb.Windows.Platform
2 | import IotaTime.TimeZone.Core
3 | import IotaTime.TimeZone.Error
4 | import IotaTime.Tzdb.Metadata
5 | import IotaTime.Tzdb.Provider
6 | import IotaTime.Tzdb.Windows
7 | import IotaTime.Tzdb.Windows.Types
13 | %foreign "C:iotatime_windows_registry_snapshot, libiotatime_windows"
14 | prim__windowsRegistrySnapshot : PrimIO AnyPtr
16 | %foreign "C:iotatime_windows_snapshot_string, libiotatime_windows"
17 | prim__windowsSnapshotString : AnyPtr -> String
19 | %foreign "C:iotatime_windows_snapshot_free, libiotatime_windows"
20 | prim__windowsSnapshotFree : AnyPtr -> PrimIO ()
22 | %foreign "C:iotatime_windows_iana_to_windows, libiotatime_windows"
23 | prim__windowsIanaToWindows : String -> PrimIO AnyPtr
25 | %foreign "C:iotatime_windows_windows_to_iana, libiotatime_windows"
26 | prim__windowsWindowsToIana : String -> PrimIO AnyPtr
28 | convertZoneId : (String -> PrimIO AnyPtr) -> String -> IO (Maybe String)
29 | convertZoneId convert value = do
30 | pointer <- primIO (convert value)
31 | if prim__nullAnyPtr pointer /= 0
34 | let converted = prim__windowsSnapshotString pointer
35 | primIO (prim__windowsSnapshotFree pointer)
36 | pure (Just converted)
38 | ianaToWindowsZone : String -> IO (Maybe String)
39 | ianaToWindowsZone = convertZoneId prim__windowsIanaToWindows
41 | windowsToIanaZone : String -> IO (Maybe String)
42 | windowsToIanaZone = convertZoneId prim__windowsWindowsToIana
47 | record WindowsRegistrySource where
48 | constructor MkWindowsRegistrySource
49 | sourceRegistrySnapshot : IO (Either String WindowsRegistrySnapshot)
52 | windowsRegistrySource : IO (Either String WindowsRegistrySnapshot) ->
53 | WindowsRegistrySource
54 | windowsRegistrySource = MkWindowsRegistrySource
56 | windowsRegistrySnapshot : WindowsRegistrySource ->
57 | IO (Either TzdbError WindowsRegistrySnapshot)
58 | windowsRegistrySnapshot source = do
59 | loaded <- source.sourceRegistrySnapshot
60 | pure (mapFst WindowsRegistrySourceError loaded)
62 | windowsRegistryZones : WindowsRegistrySource ->
63 | IO (Either TzdbError (List WindowsRegistryZone))
64 | windowsRegistryZones source = map (map snapshotZones)
65 | (windowsRegistrySnapshot source)
67 | findWindowsZone : String -> List WindowsRegistryZone ->
68 | Maybe WindowsRegistryZone
69 | findWindowsZone name [] = Nothing
70 | findWindowsZone name (zone :: rest) =
71 | if zone.registryZoneId == name then Just zone else findWindowsZone name rest
73 | windowsRegistryNamedZone : WindowsRegistrySource -> String ->
74 | IO (Either TzdbError TimeZone)
75 | windowsRegistryNamedZone source name = do
76 | converted <- ianaToWindowsZone name
77 | loaded <- windowsRegistryZones source
78 | let registryName = case converted of
83 | registry <- case findWindowsZone registryName zones of
84 | Nothing => Left (WindowsZoneNotFound name)
85 | Just value => Right value
86 | mapFst TzdbWindowsError (windowsRegistryTimeZoneAs name registry)
88 | windowsRegistryLocalZone : WindowsRegistrySource ->
89 | IO (Either TzdbError TimeZone)
90 | windowsRegistryLocalZone source = do
91 | loaded <- windowsRegistrySnapshot source
93 | Left error => pure (Left error)
94 | Right snapshot => do
95 | canonical <- windowsToIanaZone snapshot.snapshotLocalZoneId
96 | let valueId = case canonical of
97 | Nothing => snapshot.snapshotLocalZoneId
100 | registry <- case findWindowsZone snapshot.snapshotLocalZoneId
101 | snapshot.snapshotZones of
102 | Nothing => Left (WindowsZoneNotFound snapshot.snapshotLocalZoneId)
103 | Just value => Right value
104 | mapFst TzdbWindowsError (windowsRegistryTimeZoneAs valueId registry)
106 | windowsRegistryAvailableZones : WindowsRegistrySource ->
107 | IO (Either TzdbError (List String))
108 | windowsRegistryAvailableZones source = do
109 | loaded <- windowsRegistryZones source
110 | pure (map (sort . map registryZoneId) loaded)
114 | windowsRegistryTimeZoneProvider : WindowsRegistrySource -> TimeZoneProviderRep
115 | windowsRegistryTimeZoneProvider source = timeZoneProvider
116 | (pure (Right (fixedTimeZone "UTC" empty)))
117 | (windowsRegistryNamedZone source)
118 | (windowsRegistryLocalZone source)
119 | (windowsRegistryAvailableZones source)
120 | (pure (Right (MkTzdbMetadata Nothing [])))
125 | windowsRegistrySnapshotProvider : WindowsRegistrySource ->
126 | IO (Either TzdbError TimeZoneProviderRep)
127 | windowsRegistrySnapshotProvider source = do
128 | loaded <- windowsRegistrySnapshot source
130 | (\snapshot => windowsRegistryTimeZoneProvider
131 | (MkWindowsRegistrySource (pure (Right snapshot))))
134 | protocolErrorMessage : WindowsRegistryProtocolError -> String
135 | protocolErrorMessage MissingLocalZoneId = "missing local Windows zone identifier"
136 | protocolErrorMessage (UnexpectedRegistryLine line) =
137 | "unexpected Windows registry output: " ++ line
138 | protocolErrorMessage (InvalidRegistryHex value) =
139 | "invalid Windows registry hex value: " ++ value
140 | protocolErrorMessage (InvalidDynamicRegistryLine line) =
141 | "invalid Windows Dynamic DST output: " ++ line
142 | protocolErrorMessage IncompleteRegistryZone = "incomplete Windows registry zone"
144 | nativeError : String -> Maybe String
145 | nativeError source = map pack (strip (unpack "ERROR\t") (unpack source))
147 | strip : List Char -> List Char -> Maybe (List Char)
148 | strip [] remaining = Just remaining
149 | strip (expected :: rest) (actual :: remaining) =
150 | if expected == actual then strip rest remaining else Nothing
151 | strip _ _ = Nothing
153 | runWindowsNativeRegistry : IO (Either String WindowsRegistrySnapshot)
154 | runWindowsNativeRegistry = do
155 | pointer <- primIO prim__windowsRegistrySnapshot
156 | if prim__nullAnyPtr pointer /= 0
157 | then pure (Left "native Windows registry snapshot allocation failed")
159 | let output = prim__windowsSnapshotString pointer
160 | primIO (prim__windowsSnapshotFree pointer)
161 | pure $
case nativeError output of
162 | Just error => Left error
163 | Nothing => case parseWindowsRegistrySnapshot output of
164 | Left error => Left (protocolErrorMessage error)
165 | Right snapshot => Right snapshot
169 | windowsNativeRegistrySource : WindowsRegistrySource
170 | windowsNativeRegistrySource = MkWindowsRegistrySource
171 | runWindowsNativeRegistry