-
Notifications
You must be signed in to change notification settings - Fork 0
/
Configuration.fs
188 lines (161 loc) · 5.8 KB
/
Configuration.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
namespace fitz
open System
open System.IO
open System.Text
module Configuration =
open FSharp.Json
[<Literal>]
let ConfigVersion = "1.0.0"
/// Module-wide encoding
let Enc = Encoding.UTF8
/// Represents the configurable symbols in the plotted time bars.
type Symbols =
| [<JsonField("rectangles")>] Rectangles
| [<JsonField("mono")>] Mono
| [<JsonField("sun-moon")>] SunMoon
/// A structure containing the plot colors.
[<Struct>]
type PlotColors =
{ [<JsonField("color_morning")>]
ColorMorning : string
[<JsonField("color_day")>]
ColorDay : string
[<JsonField("color_evening")>]
ColorEvening : string
[<JsonField("color_night")>]
ColorNight : string
[<JsonField("foreground")>]
Foreground : string
[<JsonField("background")>]
Background : string }
/// A structure that contains a tagged TimeZone that can be easily mapped to a Location.
[<Struct>]
type ConfigLocation =
{ [<JsonField("name")>]
Name : string
[<JsonField("timezone")>]
TimeZone : string }
/// A structure to configure how the day is broken up by hours.
[<Struct>]
type DaySegmentation =
{ [<JsonField("morning")>]
HourMorning : int
[<JsonField("day")>]
HourDay : int
[<JsonField("evening")>]
HourEvening : int
[<JsonField("night")>]
HourNight : int }
type Style =
{ [<JsonField("symbols")>]
Symbols : Symbols
[<JsonField("colorize")>]
Colorize : bool
[<JsonField("day_segments")>]
DaySegmentation : DaySegmentation
[<JsonField("colors")>]
Colors : PlotColors }
type Config =
{ [<JsonField("config_version")>]
ConfigVersion : string
[<JsonField("timezones")>]
TimeZones : ConfigLocation []
[<JsonField("style")>]
Style : Style
[<JsonField("tics")>]
Tics : bool
[<JsonField("stretch")>]
Stretch : bool
[<JsonField("hours12")>]
Hours12 : bool
[<JsonField("live")>]
Live : bool }
let private (|Contents|AccessDenied|) s =
try
Contents(File.ReadAllText(s, Enc))
with
| _ -> AccessDenied
let private (|Config|ConfigFail|) s =
try
Config(Json.deserialize<Config> s)
with
| ex -> ConfigFail ex
let private (|Json|JsonFail|) data =
try
Json(Json.serialize data)
with
| ex -> JsonFail ex
let defaultConfig =
{ ConfigVersion = ConfigVersion
TimeZones =
[| { Name = "New York"; TimeZone = "America/New_York" }
{ Name = "Berlin"; TimeZone = "Europe/Berlin" }
{ Name = "Shanghai"; TimeZone = "Asia/Shanghai" }
{ Name = "Sydney"; TimeZone = "Australia/Sydney" } |]
Style =
{ Symbols = Rectangles
Colorize = false
DaySegmentation = { HourMorning = 6; HourDay = 8; HourEvening = 18; HourNight = 22 }
Colors =
{ ColorMorning = "red"
ColorDay = "yellow"
ColorEvening = "red"
ColorNight = "blue"
Foreground = ""
Background = "" } }
Tics = false
Stretch = false
Hours12 = false
Live = false }
let defaultConfigFile =
try
// Note on the difference between known folder calls:
// in go, the xdg package adds the config to LocalAppData for Win and xdg_config_home for unix
// in .net core, to link to xdg_config_home, use ApplicationData
// ApplicationData also links to AppData/Roaming, which is the correct heirarchy for
// a small, portable config like this, according to Microsoft
// in short: ardrg/xdg gets it **wrong**
let fAppData = Environment.SpecialFolder.ApplicationData
let appDataDir = Environment.GetFolderPath(fAppData)
Path.Combine(appDataDir, "fitz\config.json")
with
| _ as ex -> failwith ex.Message
let save (c : Config) =
match c with
| Json s ->
try
File.WriteAllText(defaultConfigFile, s, Enc)
with
| :? DirectoryNotFoundException ->
let fi = FileInfo defaultConfigFile
let di = DirectoryInfo fi.DirectoryName
if not di.Exists then
try
Directory.CreateDirectory(di.FullName) |> ignore
File.WriteAllText(defaultConfigFile, s, Enc)
with
| _ as ex -> failwith ex.Message
| _ as ex -> failwith ex.Message
| JsonFail ex -> failwith ex.Message
let saveDefault () = save defaultConfig
let load =
// If no configuration file exists, create one
if not (FileInfo defaultConfigFile).Exists then
saveDefault()
// Read configuration file
match defaultConfigFile with
| Contents v ->
// Deserialize (unmarshal)
match v with
| Config data ->
// Check version and validate in place
if data.ConfigVersion = ConfigVersion then
data
else
defaultConfig
| ConfigFail _ ->
eprintfn "ERROR: Could not deserialize config -- fallback to default config"
defaultConfig
| AccessDenied ->
eprintfn "ERROR: Could not load config file -- fallback to default config"
defaultConfig