├── .gitattributes ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── dark_sky.rs ├── errors.rs ├── forecast.rs ├── geocode.rs ├── location.rs ├── main.rs ├── precipitation.rs ├── sparkline.rs └── theme.rs └── workflow ├── icon.png ├── icons ├── Dark-Cloud-Fog.png ├── Dark-Cloud-Moon.png ├── Dark-Cloud-Rain.png ├── Dark-Cloud-Snow-Alt.png ├── Dark-Cloud-Snow.png ├── Dark-Cloud-Sun.png ├── Dark-Cloud.png ├── Dark-Moon.png ├── Dark-Sun.png ├── Dark-Wind.png ├── Light-Cloud-Fog.png ├── Light-Cloud-Moon.png ├── Light-Cloud-Rain.png ├── Light-Cloud-Snow-Alt.png ├── Light-Cloud-Snow.png ├── Light-Cloud-Sun.png ├── Light-Cloud.png ├── Light-Moon.png ├── Light-Sun.png ├── Light-Wind.png └── dark_sky.png └── info.plist /.gitattributes: -------------------------------------------------------------------------------- 1 | info.plist filter=alfredworkflow 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | workflow/dark-sky 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | language: rust 3 | rust: 4 | - nightly 5 | before_deploy: 6 | - cargo build --release 7 | - cp target/release/dark-sky workflow 8 | - (cd workflow && zip -rv - *) > dark-sky.alfredworkflow 9 | deploy: 10 | provider: releases 11 | api_key: 12 | secure: OuF79z87p7jfdKfh5zJPf7rEwu3AjFsr2hIqVZ8vndfoGKfaQs9DzWOI7zLJNksKqGUaADufr4IAW846AlJNWiXku1kX2iV+VKkAsb4gdT0MCUB5wTBrIADAjSbYn5o61nqy80uGiF2qgIMl4A6iocAgw1dU/1fy0xeVTrCxhd8= 13 | file: dark-sky.alfredworkflow 14 | skip_cleanup: true 15 | on: 16 | tags: true 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [3.0.3] - 2018-04-29 2 | 3 | - Update OneUpdator support since the repo structure changed. 4 | ([@vitorvalvao](https://github.com/kejadlen/dark-sky.alfredworkflow/pull/25)) 5 | 6 | ## [3.0.2] - 2017-10-17 7 | 8 | - Fix the release to include the icons in the right location. 9 | 10 | ## [3.0.1] - 2017-10-17 11 | 12 | - Release via Travis 13 | 14 | ## [3.0.0] - 2017-10-17 15 | 16 | - Rewrote it in Rust 17 | 18 | ## [2.1.2] - 2017-10-11 19 | 20 | - Vendor 2.3 gems for High Sierra. 21 | 22 | ### Fixed 23 | - Typo (Thanks, @jgarber623!) 24 | 25 | ## [2.1.1] - 2017-02-23 26 | 27 | ### Added 28 | 29 | - Do not export variable for light icons (it’s still there, just set by default 30 | to an empty value instead of false, since it works the same). 31 | - Corrected the main workflow icon (it was not using the hybrid version, which 32 | was the whole point; there was also a node that had a repeated icon). 33 | - Updated to latest OneUpdater which has some niceties concerned specifically 34 | with workflows like this one, that require Workflow Environment Variables. 35 | 36 | ## [2.1.0] - 2016-12-04 37 | 38 | Many thanks to [vitorgalvao](https://github.com/vitorgalvao) for this release! 39 | 40 | ### Added 41 | - Support for using a dark theme in Alfred 42 | - Better Alfred support (environment variables, description, and version) 43 | - [OneUpdater](oneupdater) support 44 | 45 | [oneupdater]: http://www.alfredforum.com/topic/9224-oneupdater-%E2%80%94-update-workflows-with-a-single-node/#comment-45902 46 | 47 | ### Changed 48 | - Workflow doesn't need to be scrolled to view all the nodes 49 | 50 | ### Removed 51 | - Unused weather icons 52 | 53 | ## [2.0.0] - 2016-10-15 54 | ### Added 55 | - `darksky` keyword trigger 56 | 57 | ### Changed 58 | - Switched to [Dark Sky from Forecast.io][forecast-to-dark-sky] 59 | - Renamed workflow to `dark-sky.alfredworkflow` 60 | - Use `DARK_SKY_API_KEY` instead of `FORECAST_API_KEY` 61 | 62 | [forecast-to-dark-sky]: http://blog.darksky.net/introducing-darksky-net/ 63 | 64 | ## [1.0.8] - 2016-08-04 65 | ### Added 66 | - Selecting a daily item now goes to the day on Forecast.io. 67 | 68 | ## [1.0.7] - 2016-07-28 69 | ### Fixed 70 | - Re-release to fix vendoring. 71 | 72 | ## [1.0.6] - 2016-07-27 73 | ### Fixed 74 | - Off by one error on which days to display. 75 | - Re-enable going to the Forecast.io page on all items. 76 | 77 | ## [1.0.5] - 2016-07-20 78 | ### Changed 79 | - Update to Alfred 3: use built-in configuration for API keys. 80 | - Modified the data display for the current day. 81 | 82 | ### Removed 83 | - Don't allow sorting of the items. 84 | 85 | ## [1.0.4] - 2016-04-17 86 | ### Fixed 87 | - Update dependencies. 88 | 89 | ## [1.0.3] - 2016-04-05 90 | ### Added 91 | - Delay fetching results until after the last character is typed. 92 | 93 | ### Fixed 94 | - Actually handle when there isn't a config file. 95 | 96 | ## [1.0.2] - 2015-11-01 97 | ### Changed 98 | - Update Alphred to handle a crash when there isn't a config file. 99 | 100 | ## [1.0.0] - 2015-11-01 101 | ### Changed 102 | - Use [Alphred](https://github.com/kejadlen/alphred). 103 | 104 | ## [0.0.8] - 2015-05-31 105 | ### Added 106 | - Re-add support for `DEFAULT_LAT_LONG` and `DEFAULT_LOCATION`. 107 | 108 | ## [0.0.7] - 2015-01-05 109 | ### Added 110 | - The current location is retrieved via IP geolocation rather than set in the 111 | workflow configuration. 112 | - Fixed opening the forecast in the browser. 113 | 114 | ### Removed 115 | - Support for `DEFAULT_LAT_LONG` and `DEFAULT_LOCATION` has been deprecated in favor 116 | of getting the current location from the IP. 117 | 118 | ## [0.0.6] - 2014-12-13 119 | ### Added 120 | - Add option to force Celsius/Fahrenheit using `FORECAST_UNITS`. 121 | 122 | ## [0.0.5] - 2014-11-22 123 | ### Changed 124 | - Use `forecast-config` for managing API keys. 125 | - Fixed bug when precipitation intensity/probability was all 0's. 126 | 127 | ## [0.0.4] - 2014-11-21 128 | ### Added 129 | - Sparklines for precipitation intensity and probability for the next hour 130 | (where applicable) and day. 131 | 132 | ### Changed 133 | - Bugfix for when `DEFAULT_LAT_LONG` is set and `DEFAULT_LOCATION` is not. 134 | 135 | ## [0.0.3] - 2014-11-19 136 | ### Added 137 | - Forecast now uses units appropriate to the location. 138 | 139 | ### Changed 140 | - Fix `DEFAULT_LAT_LONG`. 141 | 142 | ## [0.0.2] - 2014-11-19 143 | ### Changed 144 | - Remove minutely result for non-US locations since Forecast doesn't have this 145 | data. 146 | 147 | ## [0.0.1] - 2014-11-18 148 | ### Added 149 | - Initial release 150 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "alphred" 5 | version = "0.1.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "d68589d97a14ab93d18b9cb5144e541377fbb3b9695cea9e3f732db056bf1244" 8 | dependencies = [ 9 | "serde", 10 | "serde_derive", 11 | ] 12 | 13 | [[package]] 14 | name = "autocfg" 15 | version = "0.1.2" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" 18 | 19 | [[package]] 20 | name = "autocfg" 21 | version = "1.0.1" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 24 | 25 | [[package]] 26 | name = "backtrace" 27 | version = "0.3.15" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "f106c02a3604afcdc0df5d36cc47b44b55917dbaf3d808f71c163a0ddba64637" 30 | dependencies = [ 31 | "autocfg 0.1.2", 32 | "backtrace-sys", 33 | "cfg-if 0.1.7", 34 | "libc", 35 | "rustc-demangle", 36 | "winapi", 37 | ] 38 | 39 | [[package]] 40 | name = "backtrace-sys" 41 | version = "0.1.28" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" 44 | dependencies = [ 45 | "cc", 46 | "libc", 47 | ] 48 | 49 | [[package]] 50 | name = "base64" 51 | version = "0.13.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 54 | 55 | [[package]] 56 | name = "bitflags" 57 | version = "1.2.1" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 60 | 61 | [[package]] 62 | name = "bumpalo" 63 | version = "3.6.1" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" 66 | 67 | [[package]] 68 | name = "bytes" 69 | version = "1.0.1" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 72 | 73 | [[package]] 74 | name = "cc" 75 | version = "1.0.35" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83" 78 | 79 | [[package]] 80 | name = "cfg-if" 81 | version = "0.1.7" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" 84 | 85 | [[package]] 86 | name = "cfg-if" 87 | version = "1.0.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 90 | 91 | [[package]] 92 | name = "chrono" 93 | version = "0.4.6" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" 96 | dependencies = [ 97 | "num-integer", 98 | "num-traits", 99 | "time", 100 | ] 101 | 102 | [[package]] 103 | name = "core-foundation" 104 | version = "0.9.1" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 107 | dependencies = [ 108 | "core-foundation-sys", 109 | "libc", 110 | ] 111 | 112 | [[package]] 113 | name = "core-foundation-sys" 114 | version = "0.8.2" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 117 | 118 | [[package]] 119 | name = "dark-sky" 120 | version = "4.0.0" 121 | dependencies = [ 122 | "alphred", 123 | "chrono", 124 | "error-chain", 125 | "reqwest", 126 | "serde", 127 | "url", 128 | ] 129 | 130 | [[package]] 131 | name = "encoding_rs" 132 | version = "0.8.17" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed" 135 | dependencies = [ 136 | "cfg-if 0.1.7", 137 | ] 138 | 139 | [[package]] 140 | name = "error-chain" 141 | version = "0.11.0" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" 144 | dependencies = [ 145 | "backtrace", 146 | ] 147 | 148 | [[package]] 149 | name = "fnv" 150 | version = "1.0.6" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 153 | 154 | [[package]] 155 | name = "foreign-types" 156 | version = "0.3.2" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 159 | dependencies = [ 160 | "foreign-types-shared", 161 | ] 162 | 163 | [[package]] 164 | name = "foreign-types-shared" 165 | version = "0.1.1" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 168 | 169 | [[package]] 170 | name = "form_urlencoded" 171 | version = "1.0.1" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 174 | dependencies = [ 175 | "matches", 176 | "percent-encoding", 177 | ] 178 | 179 | [[package]] 180 | name = "futures-channel" 181 | version = "0.3.15" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" 184 | dependencies = [ 185 | "futures-core", 186 | ] 187 | 188 | [[package]] 189 | name = "futures-core" 190 | version = "0.3.15" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" 193 | 194 | [[package]] 195 | name = "futures-io" 196 | version = "0.3.15" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" 199 | 200 | [[package]] 201 | name = "futures-sink" 202 | version = "0.3.15" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" 205 | 206 | [[package]] 207 | name = "futures-task" 208 | version = "0.3.15" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" 211 | 212 | [[package]] 213 | name = "futures-util" 214 | version = "0.3.15" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" 217 | dependencies = [ 218 | "autocfg 1.0.1", 219 | "futures-core", 220 | "futures-io", 221 | "futures-task", 222 | "memchr", 223 | "pin-project-lite", 224 | "pin-utils", 225 | "slab", 226 | ] 227 | 228 | [[package]] 229 | name = "getrandom" 230 | version = "0.2.3" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 233 | dependencies = [ 234 | "cfg-if 1.0.0", 235 | "libc", 236 | "wasi", 237 | ] 238 | 239 | [[package]] 240 | name = "h2" 241 | version = "0.3.3" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" 244 | dependencies = [ 245 | "bytes", 246 | "fnv", 247 | "futures-core", 248 | "futures-sink", 249 | "futures-util", 250 | "http", 251 | "indexmap", 252 | "slab", 253 | "tokio", 254 | "tokio-util", 255 | "tracing", 256 | ] 257 | 258 | [[package]] 259 | name = "hashbrown" 260 | version = "0.9.1" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 263 | 264 | [[package]] 265 | name = "http" 266 | version = "0.2.4" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" 269 | dependencies = [ 270 | "bytes", 271 | "fnv", 272 | "itoa", 273 | ] 274 | 275 | [[package]] 276 | name = "http-body" 277 | version = "0.4.2" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9" 280 | dependencies = [ 281 | "bytes", 282 | "http", 283 | "pin-project-lite", 284 | ] 285 | 286 | [[package]] 287 | name = "httparse" 288 | version = "1.4.1" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" 291 | 292 | [[package]] 293 | name = "httpdate" 294 | version = "1.0.1" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" 297 | 298 | [[package]] 299 | name = "hyper" 300 | version = "0.14.7" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "1e5f105c494081baa3bf9e200b279e27ec1623895cd504c7dbef8d0b080fcf54" 303 | dependencies = [ 304 | "bytes", 305 | "futures-channel", 306 | "futures-core", 307 | "futures-util", 308 | "h2", 309 | "http", 310 | "http-body", 311 | "httparse", 312 | "httpdate", 313 | "itoa", 314 | "pin-project", 315 | "socket2", 316 | "tokio", 317 | "tower-service", 318 | "tracing", 319 | "want", 320 | ] 321 | 322 | [[package]] 323 | name = "hyper-tls" 324 | version = "0.5.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 327 | dependencies = [ 328 | "bytes", 329 | "hyper", 330 | "native-tls", 331 | "tokio", 332 | "tokio-native-tls", 333 | ] 334 | 335 | [[package]] 336 | name = "idna" 337 | version = "0.2.3" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 340 | dependencies = [ 341 | "matches", 342 | "unicode-bidi", 343 | "unicode-normalization", 344 | ] 345 | 346 | [[package]] 347 | name = "indexmap" 348 | version = "1.6.2" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" 351 | dependencies = [ 352 | "autocfg 1.0.1", 353 | "hashbrown", 354 | ] 355 | 356 | [[package]] 357 | name = "ipnet" 358 | version = "2.3.0" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" 361 | 362 | [[package]] 363 | name = "itoa" 364 | version = "0.4.3" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" 367 | 368 | [[package]] 369 | name = "js-sys" 370 | version = "0.3.51" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" 373 | dependencies = [ 374 | "wasm-bindgen", 375 | ] 376 | 377 | [[package]] 378 | name = "lazy_static" 379 | version = "1.4.0" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 382 | 383 | [[package]] 384 | name = "libc" 385 | version = "0.2.94" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" 388 | 389 | [[package]] 390 | name = "log" 391 | version = "0.4.14" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 394 | dependencies = [ 395 | "cfg-if 1.0.0", 396 | ] 397 | 398 | [[package]] 399 | name = "matches" 400 | version = "0.1.8" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 403 | 404 | [[package]] 405 | name = "memchr" 406 | version = "2.4.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" 409 | 410 | [[package]] 411 | name = "mime" 412 | version = "0.3.16" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 415 | 416 | [[package]] 417 | name = "mio" 418 | version = "0.7.11" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" 421 | dependencies = [ 422 | "libc", 423 | "log", 424 | "miow", 425 | "ntapi", 426 | "winapi", 427 | ] 428 | 429 | [[package]] 430 | name = "miow" 431 | version = "0.3.7" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 434 | dependencies = [ 435 | "winapi", 436 | ] 437 | 438 | [[package]] 439 | name = "native-tls" 440 | version = "0.2.7" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" 443 | dependencies = [ 444 | "lazy_static", 445 | "libc", 446 | "log", 447 | "openssl", 448 | "openssl-probe", 449 | "openssl-sys", 450 | "schannel", 451 | "security-framework", 452 | "security-framework-sys", 453 | "tempfile", 454 | ] 455 | 456 | [[package]] 457 | name = "ntapi" 458 | version = "0.3.6" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 461 | dependencies = [ 462 | "winapi", 463 | ] 464 | 465 | [[package]] 466 | name = "num-integer" 467 | version = "0.1.39" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" 470 | dependencies = [ 471 | "num-traits", 472 | ] 473 | 474 | [[package]] 475 | name = "num-traits" 476 | version = "0.2.6" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" 479 | 480 | [[package]] 481 | name = "num_cpus" 482 | version = "1.10.0" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" 485 | dependencies = [ 486 | "libc", 487 | ] 488 | 489 | [[package]] 490 | name = "once_cell" 491 | version = "1.7.2" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 494 | 495 | [[package]] 496 | name = "openssl" 497 | version = "0.10.34" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" 500 | dependencies = [ 501 | "bitflags", 502 | "cfg-if 1.0.0", 503 | "foreign-types", 504 | "libc", 505 | "once_cell", 506 | "openssl-sys", 507 | ] 508 | 509 | [[package]] 510 | name = "openssl-probe" 511 | version = "0.1.4" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" 514 | 515 | [[package]] 516 | name = "openssl-sys" 517 | version = "0.9.63" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" 520 | dependencies = [ 521 | "autocfg 1.0.1", 522 | "cc", 523 | "libc", 524 | "pkg-config", 525 | "vcpkg", 526 | ] 527 | 528 | [[package]] 529 | name = "percent-encoding" 530 | version = "2.1.0" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 533 | 534 | [[package]] 535 | name = "pin-project" 536 | version = "1.0.7" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" 539 | dependencies = [ 540 | "pin-project-internal", 541 | ] 542 | 543 | [[package]] 544 | name = "pin-project-internal" 545 | version = "1.0.7" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" 548 | dependencies = [ 549 | "proc-macro2 1.0.27", 550 | "quote 1.0.9", 551 | "syn 1.0.72", 552 | ] 553 | 554 | [[package]] 555 | name = "pin-project-lite" 556 | version = "0.2.6" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" 559 | 560 | [[package]] 561 | name = "pin-utils" 562 | version = "0.1.0" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 565 | 566 | [[package]] 567 | name = "pkg-config" 568 | version = "0.3.14" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" 571 | 572 | [[package]] 573 | name = "ppv-lite86" 574 | version = "0.2.10" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 577 | 578 | [[package]] 579 | name = "proc-macro2" 580 | version = "0.4.28" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "ba92c84f814b3f9a44c5cfca7d2ad77fa10710867d2bbb1b3d175ab5f47daa12" 583 | dependencies = [ 584 | "unicode-xid 0.1.0", 585 | ] 586 | 587 | [[package]] 588 | name = "proc-macro2" 589 | version = "1.0.27" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" 592 | dependencies = [ 593 | "unicode-xid 0.2.2", 594 | ] 595 | 596 | [[package]] 597 | name = "quote" 598 | version = "0.6.12" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" 601 | dependencies = [ 602 | "proc-macro2 0.4.28", 603 | ] 604 | 605 | [[package]] 606 | name = "quote" 607 | version = "1.0.9" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 610 | dependencies = [ 611 | "proc-macro2 1.0.27", 612 | ] 613 | 614 | [[package]] 615 | name = "rand" 616 | version = "0.8.3" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" 619 | dependencies = [ 620 | "libc", 621 | "rand_chacha", 622 | "rand_core", 623 | "rand_hc", 624 | ] 625 | 626 | [[package]] 627 | name = "rand_chacha" 628 | version = "0.3.0" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" 631 | dependencies = [ 632 | "ppv-lite86", 633 | "rand_core", 634 | ] 635 | 636 | [[package]] 637 | name = "rand_core" 638 | version = "0.6.2" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" 641 | dependencies = [ 642 | "getrandom", 643 | ] 644 | 645 | [[package]] 646 | name = "rand_hc" 647 | version = "0.3.0" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" 650 | dependencies = [ 651 | "rand_core", 652 | ] 653 | 654 | [[package]] 655 | name = "redox_syscall" 656 | version = "0.1.54" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" 659 | 660 | [[package]] 661 | name = "redox_syscall" 662 | version = "0.2.8" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" 665 | dependencies = [ 666 | "bitflags", 667 | ] 668 | 669 | [[package]] 670 | name = "remove_dir_all" 671 | version = "0.5.1" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | checksum = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" 674 | dependencies = [ 675 | "winapi", 676 | ] 677 | 678 | [[package]] 679 | name = "reqwest" 680 | version = "0.11.3" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "2296f2fac53979e8ccbc4a1136b25dcefd37be9ed7e4a1f6b05a6029c84ff124" 683 | dependencies = [ 684 | "base64", 685 | "bytes", 686 | "encoding_rs", 687 | "futures-core", 688 | "futures-util", 689 | "http", 690 | "http-body", 691 | "hyper", 692 | "hyper-tls", 693 | "ipnet", 694 | "js-sys", 695 | "lazy_static", 696 | "log", 697 | "mime", 698 | "native-tls", 699 | "percent-encoding", 700 | "pin-project-lite", 701 | "serde", 702 | "serde_json", 703 | "serde_urlencoded", 704 | "tokio", 705 | "tokio-native-tls", 706 | "url", 707 | "wasm-bindgen", 708 | "wasm-bindgen-futures", 709 | "web-sys", 710 | "winreg", 711 | ] 712 | 713 | [[package]] 714 | name = "rustc-demangle" 715 | version = "0.1.14" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "ccc78bfd5acd7bf3e89cffcf899e5cb1a52d6fafa8dec2739ad70c9577a57288" 718 | 719 | [[package]] 720 | name = "ryu" 721 | version = "0.2.7" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" 724 | 725 | [[package]] 726 | name = "ryu" 727 | version = "1.0.5" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 730 | 731 | [[package]] 732 | name = "schannel" 733 | version = "0.1.19" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 736 | dependencies = [ 737 | "lazy_static", 738 | "winapi", 739 | ] 740 | 741 | [[package]] 742 | name = "security-framework" 743 | version = "2.2.0" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" 746 | dependencies = [ 747 | "bitflags", 748 | "core-foundation", 749 | "core-foundation-sys", 750 | "libc", 751 | "security-framework-sys", 752 | ] 753 | 754 | [[package]] 755 | name = "security-framework-sys" 756 | version = "2.2.0" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" 759 | dependencies = [ 760 | "core-foundation-sys", 761 | "libc", 762 | ] 763 | 764 | [[package]] 765 | name = "serde" 766 | version = "1.0.90" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4" 769 | dependencies = [ 770 | "serde_derive", 771 | ] 772 | 773 | [[package]] 774 | name = "serde_derive" 775 | version = "1.0.90" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "58fc82bec244f168b23d1963b45c8bf5726e9a15a9d146a067f9081aeed2de79" 778 | dependencies = [ 779 | "proc-macro2 0.4.28", 780 | "quote 0.6.12", 781 | "syn 0.15.32", 782 | ] 783 | 784 | [[package]] 785 | name = "serde_json" 786 | version = "1.0.39" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" 789 | dependencies = [ 790 | "itoa", 791 | "ryu 0.2.7", 792 | "serde", 793 | ] 794 | 795 | [[package]] 796 | name = "serde_urlencoded" 797 | version = "0.7.0" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 800 | dependencies = [ 801 | "form_urlencoded", 802 | "itoa", 803 | "ryu 1.0.5", 804 | "serde", 805 | ] 806 | 807 | [[package]] 808 | name = "slab" 809 | version = "0.4.2" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 812 | 813 | [[package]] 814 | name = "socket2" 815 | version = "0.4.0" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" 818 | dependencies = [ 819 | "libc", 820 | "winapi", 821 | ] 822 | 823 | [[package]] 824 | name = "syn" 825 | version = "0.15.32" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "846620ec526c1599c070eff393bfeeeb88a93afa2513fc3b49f1fea84cf7b0ed" 828 | dependencies = [ 829 | "proc-macro2 0.4.28", 830 | "quote 0.6.12", 831 | "unicode-xid 0.1.0", 832 | ] 833 | 834 | [[package]] 835 | name = "syn" 836 | version = "1.0.72" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" 839 | dependencies = [ 840 | "proc-macro2 1.0.27", 841 | "quote 1.0.9", 842 | "unicode-xid 0.2.2", 843 | ] 844 | 845 | [[package]] 846 | name = "tempfile" 847 | version = "3.2.0" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 850 | dependencies = [ 851 | "cfg-if 1.0.0", 852 | "libc", 853 | "rand", 854 | "redox_syscall 0.2.8", 855 | "remove_dir_all", 856 | "winapi", 857 | ] 858 | 859 | [[package]] 860 | name = "time" 861 | version = "0.1.42" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" 864 | dependencies = [ 865 | "libc", 866 | "redox_syscall 0.1.54", 867 | "winapi", 868 | ] 869 | 870 | [[package]] 871 | name = "tinyvec" 872 | version = "1.2.0" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" 875 | dependencies = [ 876 | "tinyvec_macros", 877 | ] 878 | 879 | [[package]] 880 | name = "tinyvec_macros" 881 | version = "0.1.0" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 884 | 885 | [[package]] 886 | name = "tokio" 887 | version = "1.6.0" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "bd3076b5c8cc18138b8f8814895c11eb4de37114a5d127bafdc5e55798ceef37" 890 | dependencies = [ 891 | "autocfg 1.0.1", 892 | "bytes", 893 | "libc", 894 | "memchr", 895 | "mio", 896 | "num_cpus", 897 | "pin-project-lite", 898 | ] 899 | 900 | [[package]] 901 | name = "tokio-native-tls" 902 | version = "0.3.0" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 905 | dependencies = [ 906 | "native-tls", 907 | "tokio", 908 | ] 909 | 910 | [[package]] 911 | name = "tokio-util" 912 | version = "0.6.7" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" 915 | dependencies = [ 916 | "bytes", 917 | "futures-core", 918 | "futures-sink", 919 | "log", 920 | "pin-project-lite", 921 | "tokio", 922 | ] 923 | 924 | [[package]] 925 | name = "tower-service" 926 | version = "0.3.1" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 929 | 930 | [[package]] 931 | name = "tracing" 932 | version = "0.1.26" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" 935 | dependencies = [ 936 | "cfg-if 1.0.0", 937 | "pin-project-lite", 938 | "tracing-core", 939 | ] 940 | 941 | [[package]] 942 | name = "tracing-core" 943 | version = "0.1.18" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" 946 | dependencies = [ 947 | "lazy_static", 948 | ] 949 | 950 | [[package]] 951 | name = "try-lock" 952 | version = "0.2.3" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 955 | 956 | [[package]] 957 | name = "unicode-bidi" 958 | version = "0.3.4" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 961 | dependencies = [ 962 | "matches", 963 | ] 964 | 965 | [[package]] 966 | name = "unicode-normalization" 967 | version = "0.1.17" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" 970 | dependencies = [ 971 | "tinyvec", 972 | ] 973 | 974 | [[package]] 975 | name = "unicode-xid" 976 | version = "0.1.0" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 979 | 980 | [[package]] 981 | name = "unicode-xid" 982 | version = "0.2.2" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 985 | 986 | [[package]] 987 | name = "url" 988 | version = "2.2.2" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 991 | dependencies = [ 992 | "form_urlencoded", 993 | "idna", 994 | "matches", 995 | "percent-encoding", 996 | "serde", 997 | ] 998 | 999 | [[package]] 1000 | name = "vcpkg" 1001 | version = "0.2.13" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa" 1004 | 1005 | [[package]] 1006 | name = "want" 1007 | version = "0.3.0" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1010 | dependencies = [ 1011 | "log", 1012 | "try-lock", 1013 | ] 1014 | 1015 | [[package]] 1016 | name = "wasi" 1017 | version = "0.10.2+wasi-snapshot-preview1" 1018 | source = "registry+https://github.com/rust-lang/crates.io-index" 1019 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1020 | 1021 | [[package]] 1022 | name = "wasm-bindgen" 1023 | version = "0.2.74" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" 1026 | dependencies = [ 1027 | "cfg-if 1.0.0", 1028 | "serde", 1029 | "serde_json", 1030 | "wasm-bindgen-macro", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "wasm-bindgen-backend" 1035 | version = "0.2.74" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" 1038 | dependencies = [ 1039 | "bumpalo", 1040 | "lazy_static", 1041 | "log", 1042 | "proc-macro2 1.0.27", 1043 | "quote 1.0.9", 1044 | "syn 1.0.72", 1045 | "wasm-bindgen-shared", 1046 | ] 1047 | 1048 | [[package]] 1049 | name = "wasm-bindgen-futures" 1050 | version = "0.4.24" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" 1053 | dependencies = [ 1054 | "cfg-if 1.0.0", 1055 | "js-sys", 1056 | "wasm-bindgen", 1057 | "web-sys", 1058 | ] 1059 | 1060 | [[package]] 1061 | name = "wasm-bindgen-macro" 1062 | version = "0.2.74" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" 1065 | dependencies = [ 1066 | "quote 1.0.9", 1067 | "wasm-bindgen-macro-support", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "wasm-bindgen-macro-support" 1072 | version = "0.2.74" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" 1075 | dependencies = [ 1076 | "proc-macro2 1.0.27", 1077 | "quote 1.0.9", 1078 | "syn 1.0.72", 1079 | "wasm-bindgen-backend", 1080 | "wasm-bindgen-shared", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "wasm-bindgen-shared" 1085 | version = "0.2.74" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" 1088 | 1089 | [[package]] 1090 | name = "web-sys" 1091 | version = "0.3.51" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" 1094 | dependencies = [ 1095 | "js-sys", 1096 | "wasm-bindgen", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "winapi" 1101 | version = "0.3.9" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1104 | dependencies = [ 1105 | "winapi-i686-pc-windows-gnu", 1106 | "winapi-x86_64-pc-windows-gnu", 1107 | ] 1108 | 1109 | [[package]] 1110 | name = "winapi-i686-pc-windows-gnu" 1111 | version = "0.4.0" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1114 | 1115 | [[package]] 1116 | name = "winapi-x86_64-pc-windows-gnu" 1117 | version = "0.4.0" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1120 | 1121 | [[package]] 1122 | name = "winreg" 1123 | version = "0.7.0" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" 1126 | dependencies = [ 1127 | "winapi", 1128 | ] 1129 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dark-sky" 3 | version = "4.0.0" 4 | authors = ["Alpha Chen "] 5 | 6 | [dependencies] 7 | alphred = "0.1" 8 | chrono = "0.4" 9 | error-chain = "0.11" 10 | reqwest = { version = "0.11", features = ["blocking", "json"] } 11 | serde = { version = "1.0", features = ["derive"] } 12 | serde_json = "1.0" 13 | url = { version = "2.2", features = ["serde"] } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Alpha Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dark Sky Workflow for Alfred 2 | 3 | With [Dark Sky shutting down their API][dark-sky-incredible-journey], this has 4 | been replaced by a [Pirate Weather workflow][pirate-weather-workflow]. 5 | 6 | [dark-sky-incredible-journey]: https://blog.darksky.net/ 7 | [pirate-weather-workflow]: https://github.com/kejadlen/pirate-weather.alfredworkflow/ 8 | 9 | ## Migration 10 | 11 | Download and install the the latest [pirate-weather.alfred-workflow 12 | release][pirate-weather-workflow-releases]. [Obtain a Pirate Weather API 13 | key][pirate-weather-getting-started] and set `PIRATE_WEATHER_API_KEY` in the 14 | workflow environment variables. 15 | 16 | [pirate-weather-workflow-releases]: https://github.com/kejadlen/pirate-weather.alfredworkflow/releases 17 | [pirate-weather-getting-started]: https://pirateweather.net/getting-started 18 | 19 | ## Notes 20 | 21 | You can still use Dark Sky as a data source until the API is actually turned 22 | off by setting the `PIRATE_WEATHER_ENDPOINT` environment variable to 23 | `api.darksky.net`. 24 | -------------------------------------------------------------------------------- /src/dark_sky.rs: -------------------------------------------------------------------------------- 1 | use alphred::Item; 2 | use chrono::prelude::*; 3 | use reqwest; 4 | 5 | use errors::*; 6 | use forecast; 7 | use location; 8 | use sparkline; 9 | use theme::Theme; 10 | 11 | #[derive(Debug)] 12 | pub struct DarkSky { 13 | pub dark_sky_endpoint: String, 14 | pub dark_sky_api_key: String, 15 | pub location: location::Location, 16 | pub theme: Theme, 17 | pub units: forecast::Units, 18 | pub lang: forecast::Lang, 19 | } 20 | 21 | impl DarkSky { 22 | pub fn run(&self) -> Result<()> { 23 | let forecast = self.forecast(&self.location.coord)?; 24 | 25 | let mut items = Vec::new(); 26 | 27 | let item = Item::new(self.location.description.clone()) 28 | .subtitle("• Powered by Dark Sky") 29 | .arg(&self.arg()) 30 | .icon("icons/dark_sky.png"); 31 | items.push(item); 32 | 33 | if let Some(item) = forecast.currently.and_then(|point| self.currently(&point)) { 34 | items.push(item); 35 | } 36 | 37 | if let Some(item) = forecast.minutely.and_then(|block| self.minutely(&block)) { 38 | items.push(item); 39 | } 40 | 41 | if let Some(daily) = forecast.daily { 42 | daily 43 | .data 44 | .iter() 45 | .take(5) 46 | .flat_map(|point| self.daily(point)) 47 | .for_each(|x| items.push(x)); 48 | } 49 | 50 | let json = json!({ "items": items }); 51 | println!("{}", json); 52 | 53 | Ok(()) 54 | } 55 | 56 | fn forecast(&self, coord: &location::Coordinate) -> Result { 57 | let &location::Coordinate(lat, long) = coord; 58 | let units = match self.units { 59 | forecast::Units::Auto => "auto", 60 | forecast::Units::Ca => "ca", 61 | forecast::Units::Uk2 => "uk2", 62 | forecast::Units::Us => "us", 63 | forecast::Units::Si => "si", 64 | }; 65 | let lang = match self.lang { 66 | forecast::Lang::Arabic => "ar", 67 | forecast::Lang::Azerbaijani => "az", 68 | forecast::Lang::Belarusian => "be", 69 | forecast::Lang::Bulgarian => "bg", 70 | forecast::Lang::Bengali => "bn", 71 | forecast::Lang::Bosnian => "bs", 72 | forecast::Lang::Catalan => "ca", 73 | forecast::Lang::Czech => "cs", 74 | forecast::Lang::Danish => "da", 75 | forecast::Lang::German => "de", 76 | forecast::Lang::Greek => "el", 77 | forecast::Lang::English => "en", 78 | forecast::Lang::Esperanto => "eo", 79 | forecast::Lang::Spanish => "es", 80 | forecast::Lang::Estonian => "et", 81 | forecast::Lang::Finnish => "fi", 82 | forecast::Lang::French => "fr", 83 | forecast::Lang::Hebrew => "he", 84 | forecast::Lang::Hindi => "hi", 85 | forecast::Lang::Croatian => "hr", 86 | forecast::Lang::Hungarian => "hu", 87 | forecast::Lang::Indonesian => "id", 88 | forecast::Lang::Icelandic => "is", 89 | forecast::Lang::Italian => "it", 90 | forecast::Lang::Japanese => "ja", 91 | forecast::Lang::Georgian => "ka", 92 | forecast::Lang::Kannada => "kn", 93 | forecast::Lang::Korean => "ko", 94 | forecast::Lang::Cornish => "kw", 95 | forecast::Lang::Latvian => "lv", 96 | forecast::Lang::Malayam => "ml", 97 | forecast::Lang::Marathi => "mr", 98 | forecast::Lang::NorwegianBokmal => "nb", 99 | forecast::Lang::Dutch => "nl", 100 | forecast::Lang::Punjabi => "pa", 101 | forecast::Lang::Polish => "pl", 102 | forecast::Lang::Portuguese => "pt", 103 | forecast::Lang::Romanian => "ro", 104 | forecast::Lang::Russian => "ru", 105 | forecast::Lang::Slovak => "sk", 106 | forecast::Lang::Slovenian => "sl", 107 | forecast::Lang::Serbian => "sr", 108 | forecast::Lang::Swedish => "sv", 109 | forecast::Lang::Tamil => "ta", 110 | forecast::Lang::Telugu => "te", 111 | forecast::Lang::Tetum => "tet", 112 | forecast::Lang::Turkish => "tr", 113 | forecast::Lang::Ukrainian => "uk", 114 | forecast::Lang::Urdu => "ur", 115 | forecast::Lang::IgpayAtinlay => "x-pig-latin", 116 | forecast::Lang::SimplifiedChinese => "zh", 117 | forecast::Lang::TraditionalChinese => "zh-tw", 118 | }; 119 | let url = format!( 120 | "https://{}/forecast/{}/{},{}?units={}&lang={}", 121 | self.dark_sky_endpoint, self.dark_sky_api_key, lat, long, units, lang 122 | ); 123 | Ok(reqwest::blocking::get(&url)?.json()?) 124 | } 125 | 126 | fn arg(&self) -> String { 127 | let location::Coordinate(lat, long) = self.location.coord; 128 | format!("{:.4},{:.4}", lat, long) 129 | } 130 | 131 | fn currently(&self, point: &forecast::Point) -> Option { 132 | let title = point.summary.clone()?; 133 | let temp = point.temp?; 134 | let apparent_temp = point.apparent_temperature.clone()?; 135 | let icon = point.icon.clone()?; 136 | 137 | let mut subtitle = vec![ 138 | format!("{}°", temp.round()), 139 | format!("Feels like {}", apparent_temp), 140 | ]; 141 | if let Some(human_precip) = point.human_precipitation() { 142 | subtitle.push(human_precip); 143 | } 144 | let subtitle = subtitle.join(" · "); 145 | 146 | let mut item = Item::new(title); 147 | item = item.subtitle(&subtitle); 148 | item = item.arg(&self.arg()); 149 | if let Some(path) = self.theme.icon_path(&icon) { 150 | item = item.icon(path.as_path()); 151 | } 152 | Some(item) 153 | } 154 | 155 | fn minutely(&self, block: &forecast::Block) -> Option { 156 | let title = block.summary.clone()?; 157 | let icon = block.icon.clone()?; 158 | 159 | let mut item = Item::new(title); 160 | 161 | let mut subtitle = Vec::new(); 162 | let intensities = block.precip_intensities(); 163 | if let (Some(min), Some(max)) = (intensities.iter().min(), intensities.iter().max()) { 164 | let sparkline = sparkline::Ascii::new( 165 | min.0, 166 | max.0, 167 | intensities.clone().iter().map(|x| x.0).collect(), 168 | 5, 169 | ); 170 | subtitle.push(format!("{} {} {}", min, sparkline, max)); 171 | } 172 | let probabilities = block.precip_probabilities(); 173 | if let (Some(min), Some(max)) = (probabilities.iter().min(), probabilities.iter().max()) { 174 | let sparkline = sparkline::Ascii::new( 175 | min.0, 176 | 1., 177 | probabilities.clone().iter().map(|x| x.0).collect(), 178 | 5, 179 | ); 180 | subtitle.push(format!("{} {} {}", min, sparkline, max)); 181 | } 182 | let subtitle = subtitle.join(" · "); 183 | 184 | item = item.subtitle(&subtitle); 185 | item = item.arg(&self.arg()); 186 | if let Some(path) = self.theme.icon_path(&icon) { 187 | item = item.icon(path.as_path()); 188 | } 189 | 190 | Some(item) 191 | } 192 | 193 | fn daily(&self, point: &forecast::Point) -> Option { 194 | let summary = point.summary.clone()?; 195 | let min = point.apparent_temperature_min.clone()?; 196 | let max = point.apparent_temperature_max.clone()?; 197 | let icon = point.icon.clone()?; 198 | 199 | let weekday = if point.time.date() == Local::today() { 200 | "Today".into() 201 | } else { 202 | point.time.format("%A").to_string() 203 | }; 204 | let title = format!("{} - {}", weekday, summary); 205 | let subtitle = format!("Low: {} · High: {}", min, max); 206 | let arg = format!("{}/{}", self.arg(), point.time.timestamp()); 207 | 208 | let mut item = Item::new(title); 209 | item = item.subtitle(&subtitle); 210 | item = item.arg(&arg); 211 | if let Some(path) = self.theme.icon_path(&icon) { 212 | item = item.icon(path.as_path()); 213 | } 214 | Some(item) 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | error_chain! { 3 | foreign_links { 4 | Http(::reqwest::Error); 5 | Env(::std::env::VarError); 6 | Url(::url::ParseError); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/forecast.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::result; 3 | 4 | use chrono::prelude::*; 5 | use serde::Deserialize; 6 | 7 | use precipitation::{Intensity, Probability}; 8 | 9 | #[derive(Clone, Debug, Deserialize)] 10 | pub struct Temperature(f64); 11 | 12 | impl fmt::Display for Temperature { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | write!(f, "{}°", self.0.round()) 15 | } 16 | } 17 | 18 | #[derive(Debug)] 19 | pub enum Units { 20 | Auto, 21 | Ca, 22 | Uk2, 23 | Us, 24 | Si, 25 | } 26 | 27 | #[derive(Debug)] 28 | pub enum Lang { 29 | Arabic, 30 | Azerbaijani, 31 | Belarusian, 32 | Bulgarian, 33 | Bengali, 34 | Bosnian, 35 | Catalan, 36 | Czech, 37 | Danish, 38 | German, 39 | Greek, 40 | English, 41 | Esperanto, 42 | Spanish, 43 | Estonian, 44 | Finnish, 45 | French, 46 | Hebrew, 47 | Hindi, 48 | Croatian, 49 | Hungarian, 50 | Indonesian, 51 | Icelandic, 52 | Italian, 53 | Japanese, 54 | Georgian, 55 | Kannada, 56 | Korean, 57 | Cornish, 58 | Latvian, 59 | Malayam, 60 | Marathi, 61 | NorwegianBokmal, 62 | Dutch, 63 | Punjabi, 64 | Polish, 65 | Portuguese, 66 | Romanian, 67 | Russian, 68 | Slovak, 69 | Slovenian, 70 | Serbian, 71 | Swedish, 72 | Tamil, 73 | Telugu, 74 | Tetum, 75 | Turkish, 76 | Ukrainian, 77 | Urdu, 78 | IgpayAtinlay, 79 | SimplifiedChinese, 80 | TraditionalChinese, 81 | } 82 | 83 | #[derive(Clone, Debug)] 84 | pub enum Icon { 85 | ClearDay, 86 | ClearNight, 87 | Rain, 88 | Snow, 89 | Sleet, 90 | Wind, 91 | Fog, 92 | Cloudy, 93 | PartlyCloudyDay, 94 | PartlyCloudyNight, 95 | Unknown(String), 96 | } 97 | 98 | impl<'de> Deserialize<'de> for Icon { 99 | fn deserialize(deserializer: D) -> result::Result 100 | where 101 | D: ::serde::Deserializer<'de>, 102 | { 103 | Ok(match String::deserialize(deserializer)?.as_str() { 104 | "clear-day" => Icon::ClearDay, 105 | "clear-night" => Icon::ClearNight, 106 | "rain" => Icon::Rain, 107 | "snow" => Icon::Snow, 108 | "sleet" => Icon::Sleet, 109 | "wind" => Icon::Wind, 110 | "fog" => Icon::Fog, 111 | "cloudy" => Icon::Cloudy, 112 | "partly-cloudy-day" => Icon::PartlyCloudyDay, 113 | "partly-cloudy-night" => Icon::PartlyCloudyNight, 114 | s => Icon::Unknown(s.into()), 115 | }) 116 | } 117 | } 118 | 119 | #[derive(Debug, Deserialize)] 120 | pub struct Forecast { 121 | pub currently: Option, 122 | pub minutely: Option, 123 | pub daily: Option, 124 | } 125 | 126 | #[derive(Debug, Deserialize)] 127 | #[serde(rename_all = "camelCase")] 128 | pub struct Point { 129 | #[serde(rename = "temperature")] 130 | pub temp: Option, 131 | pub apparent_temperature: Option, 132 | pub apparent_temperature_min: Option, 133 | pub apparent_temperature_max: Option, 134 | pub icon: Option, 135 | pub precip_intensity: Option, 136 | pub precip_probability: Option, 137 | pub summary: Option, 138 | #[serde(deserialize_with = "deserialize_timestamp")] 139 | pub time: DateTime, 140 | } 141 | 142 | fn deserialize_timestamp<'de, D>(deserializer: D) -> result::Result, D::Error> 143 | where 144 | D: ::serde::Deserializer<'de>, 145 | { 146 | let unix_time = f64::deserialize(deserializer)? as i64; 147 | Ok(Local.timestamp(unix_time, 0)) 148 | } 149 | 150 | impl Point { 151 | pub fn human_precipitation(&self) -> Option { 152 | let intensity = self.precip_intensity.clone()?; 153 | let probability = self.precip_probability.clone()?; 154 | if probability.0 > 0. { 155 | Some(format!( 156 | "{} chance of {} rain.", 157 | probability, 158 | intensity.humanized() 159 | )) 160 | } else { 161 | None 162 | } 163 | } 164 | } 165 | 166 | #[derive(Debug, Deserialize)] 167 | pub struct Block { 168 | pub data: Vec, 169 | pub summary: Option, 170 | pub icon: Option, 171 | } 172 | 173 | impl Block { 174 | // These aren't quite right since I'm just dropping precipitation values that aren't there... 175 | 176 | pub fn precip_intensities(&self) -> Vec { 177 | self.data 178 | .iter() 179 | .flat_map(|x| x.precip_intensity.clone()) 180 | .collect() 181 | } 182 | 183 | pub fn precip_probabilities(&self) -> Vec { 184 | self.data 185 | .iter() 186 | .flat_map(|x| x.precip_probability.clone()) 187 | .collect() 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/geocode.rs: -------------------------------------------------------------------------------- 1 | use reqwest; 2 | use serde::Deserialize; 3 | use url; 4 | 5 | use errors; 6 | use location; 7 | 8 | pub struct Geocoder { 9 | api_key: String, 10 | } 11 | 12 | impl Geocoder { 13 | pub fn new(api_key: &str) -> Self { 14 | let api_key = api_key.into(); 15 | Self { api_key } 16 | } 17 | 18 | pub fn geocode(&self, address: &str) -> errors::Result { 19 | let client = reqwest::blocking::Client::new(); 20 | 21 | let mut url = url::Url::parse("https://maps.googleapis.com/maps/api/geocode/json")?; 22 | url.query_pairs_mut().append_pair("address", address); 23 | url.query_pairs_mut().append_pair("key", &self.api_key); 24 | let response: Response = client.get(url).send()?.json()?; 25 | 26 | response 27 | .location() 28 | .ok_or_else(|| format!("unable to geocode address '{}'", address).into()) 29 | } 30 | } 31 | 32 | #[derive(Debug, Deserialize)] 33 | struct Response { 34 | results: Vec, 35 | } 36 | 37 | impl Response { 38 | fn location(&self) -> Option { 39 | let result = self.results.first()?; 40 | let description = result.formatted_address.clone(); 41 | let location = &result.geometry.location; 42 | let coord = location::Coordinate(location.lat, location.lng); 43 | Some(location::Location { description, coord }) 44 | } 45 | } 46 | 47 | #[derive(Debug, Deserialize)] 48 | struct Result { 49 | formatted_address: String, 50 | geometry: Geometry, 51 | } 52 | 53 | #[derive(Debug, Deserialize)] 54 | struct Geometry { 55 | location: Location, 56 | } 57 | 58 | #[derive(Debug, Deserialize)] 59 | struct Location { 60 | lat: f64, 61 | lng: f64, 62 | } 63 | -------------------------------------------------------------------------------- /src/location.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | use std::result; 3 | 4 | use reqwest; 5 | use serde::Deserialize; 6 | 7 | use errors::*; 8 | 9 | #[derive(Clone, Debug)] 10 | pub struct Location { 11 | pub description: String, 12 | pub coord: Coordinate, 13 | } 14 | 15 | #[derive(Debug, Deserialize)] 16 | struct IPInfo { 17 | #[serde(rename = "loc")] 18 | coord: Coordinate, 19 | city: String, 20 | region: String, 21 | } 22 | 23 | impl Location { 24 | pub fn from_ip() -> Result { 25 | let ip_info: IPInfo = reqwest::blocking::get("https://ipinfo.io/json")?.json()?; 26 | let description = format!("{}, {}", ip_info.city, ip_info.region); 27 | let coord = ip_info.coord; 28 | Ok(Self { description, coord }) 29 | } 30 | } 31 | 32 | #[derive(Clone, Debug)] 33 | pub struct Coordinate(pub f64, pub f64); 34 | 35 | impl<'a> TryFrom<&'a str> for Coordinate { 36 | type Error = (); 37 | 38 | fn try_from(s: &str) -> result::Result { 39 | let mut split = s.split(',').flat_map(|s| s.parse::()); 40 | match (split.next(), split.next()) { 41 | (Some(lat), Some(long)) => Ok(Coordinate(lat, long)), 42 | _ => Err(()), 43 | } 44 | } 45 | } 46 | 47 | #[test] 48 | fn test_coordinate_try_from() { 49 | assert!(Coordinate::try_from("1.1,-2.3").is_ok()); 50 | 51 | assert!(Coordinate::try_from("").is_err()); 52 | assert!(Coordinate::try_from("1").is_err()); 53 | assert!(Coordinate::try_from("a").is_err()); 54 | assert!(Coordinate::try_from("1,").is_err()); 55 | } 56 | 57 | impl<'de> Deserialize<'de> for Coordinate { 58 | fn deserialize(deserializer: D) -> result::Result 59 | where 60 | D: ::serde::Deserializer<'de>, 61 | { 62 | let s = String::deserialize(deserializer)?; 63 | let mut split = s.split(','); 64 | let lat = split 65 | .next() 66 | .and_then(|lat| lat.parse().ok()) 67 | .ok_or_else(|| ::serde::de::Error::custom(""))?; 68 | let long = split 69 | .next() 70 | .and_then(|long| long.parse().ok()) 71 | .ok_or_else(|| ::serde::de::Error::custom(""))?; 72 | Ok(Coordinate(lat, long)) 73 | } 74 | } 75 | 76 | #[test] 77 | fn test_deserializing_coordinate() { 78 | let c: result::Result = ::serde_json::from_str("\"\""); 79 | assert!(c.is_err()); 80 | 81 | let c: result::Result = ::serde_json::from_str("\"123\""); 82 | assert!(c.is_err()); 83 | 84 | let c: result::Result = ::serde_json::from_str("\"123,\""); 85 | assert!(c.is_err()); 86 | 87 | let Coordinate(lat, long) = ::serde_json::from_str("\"123,-123\"").unwrap(); 88 | assert_eq!(lat, 123.0); 89 | assert_eq!(long, -123.0); 90 | } 91 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "1024"] 2 | 3 | extern crate alphred; 4 | extern crate chrono; 5 | #[macro_use] 6 | extern crate error_chain; 7 | extern crate reqwest; 8 | extern crate serde; 9 | #[macro_use] 10 | extern crate serde_json; 11 | extern crate url; 12 | 13 | mod dark_sky; 14 | mod errors; 15 | mod forecast; 16 | mod geocode; 17 | mod location; 18 | mod precipitation; 19 | mod sparkline; 20 | mod theme; 21 | 22 | use std::convert::TryFrom; 23 | use std::env; 24 | 25 | use errors::*; 26 | use theme::Theme; 27 | 28 | quick_main!(|| { 29 | let dark_sky_endpoint = 30 | env::var("DARK_SKY_ENDPOINT").unwrap_or_else(|_| "api.darksky.net".into()); 31 | let dark_sky_api_key = env::var("DARK_SKY_API_KEY")?; 32 | let location = location()?; 33 | let theme = if env::var("LIGHT_ICONS") == Ok("true".into()) { 34 | Theme::Light 35 | } else { 36 | Theme::Dark 37 | }; 38 | let units = env::var("FORECAST_UNITS").unwrap_or_else(|_| "auto".into()); 39 | let units = match units.as_str() { 40 | "auto" => forecast::Units::Auto, 41 | "ca" => forecast::Units::Ca, 42 | "uk2" => forecast::Units::Uk2, 43 | "us" => forecast::Units::Us, 44 | "si" => forecast::Units::Si, 45 | units => bail!("invalid `FORECAST_UNITS`: '{}'", units), 46 | }; 47 | let lang = env::var("FORECAST_LANG").unwrap_or_else(|_| "en".into()); 48 | let lang = match lang.as_str() { 49 | "ar" => forecast::Lang::Arabic, 50 | "az" => forecast::Lang::Azerbaijani, 51 | "be" => forecast::Lang::Belarusian, 52 | "bg" => forecast::Lang::Bulgarian, 53 | "bn" => forecast::Lang::Bengali, 54 | "bs" => forecast::Lang::Bosnian, 55 | "ca" => forecast::Lang::Catalan, 56 | "cs" => forecast::Lang::Czech, 57 | "da" => forecast::Lang::Danish, 58 | "de" => forecast::Lang::German, 59 | "el" => forecast::Lang::Greek, 60 | "en" => forecast::Lang::English, 61 | "eo" => forecast::Lang::Esperanto, 62 | "es" => forecast::Lang::Spanish, 63 | "et" => forecast::Lang::Estonian, 64 | "fi" => forecast::Lang::Finnish, 65 | "fr" => forecast::Lang::French, 66 | "he" => forecast::Lang::Hebrew, 67 | "hi" => forecast::Lang::Hindi, 68 | "hr" => forecast::Lang::Croatian, 69 | "hu" => forecast::Lang::Hungarian, 70 | "id" => forecast::Lang::Indonesian, 71 | "is" => forecast::Lang::Icelandic, 72 | "it" => forecast::Lang::Italian, 73 | "ja" => forecast::Lang::Japanese, 74 | "ka" => forecast::Lang::Georgian, 75 | "kn" => forecast::Lang::Kannada, 76 | "ko" => forecast::Lang::Korean, 77 | "kw" => forecast::Lang::Cornish, 78 | "lv" => forecast::Lang::Latvian, 79 | "ml" => forecast::Lang::Malayam, 80 | "mr" => forecast::Lang::Marathi, 81 | "nb" => forecast::Lang::NorwegianBokmal, 82 | "nl" => forecast::Lang::Dutch, 83 | "no" => forecast::Lang::NorwegianBokmal, 84 | "pa" => forecast::Lang::Punjabi, 85 | "pl" => forecast::Lang::Polish, 86 | "pt" => forecast::Lang::Portuguese, 87 | "ro" => forecast::Lang::Romanian, 88 | "ru" => forecast::Lang::Russian, 89 | "sk" => forecast::Lang::Slovak, 90 | "sl" => forecast::Lang::Slovenian, 91 | "sr" => forecast::Lang::Serbian, 92 | "sv" => forecast::Lang::Swedish, 93 | "ta" => forecast::Lang::Tamil, 94 | "te" => forecast::Lang::Telugu, 95 | "tet" => forecast::Lang::Tetum, 96 | "tr" => forecast::Lang::Turkish, 97 | "uk" => forecast::Lang::Ukrainian, 98 | "ur" => forecast::Lang::Urdu, 99 | "x-pig-latin" => forecast::Lang::IgpayAtinlay, 100 | "zh" => forecast::Lang::SimplifiedChinese, 101 | "zh-tw" => forecast::Lang::TraditionalChinese, 102 | lang => bail!("invalid `FORECAST_LANG`: '{}'", lang), 103 | }; 104 | 105 | let dark_sky = dark_sky::DarkSky { 106 | dark_sky_endpoint, 107 | dark_sky_api_key, 108 | location, 109 | theme, 110 | units, 111 | lang, 112 | }; 113 | 114 | dark_sky.run() 115 | }); 116 | 117 | fn location() -> Result { 118 | let args: Vec<_> = env::args().skip(1).collect(); 119 | let query = args.join(" "); 120 | let location = parse_default_location()?; 121 | match (query, location) { 122 | (ref query, _) if !query.is_empty() => { 123 | let api_key = env::var("GOOGLE_API_KEY")?; 124 | let geocoder = geocode::Geocoder::new(&api_key); 125 | geocoder.geocode(query) 126 | } 127 | (_, Some(ref location)) => Ok(location.clone()), 128 | _ => location::Location::from_ip(), 129 | } 130 | } 131 | 132 | fn parse_default_location() -> Result> { 133 | let location = match env::var("DEFAULT_LAT_LONG") { 134 | Ok(ref lat_long) if !lat_long.is_empty() => { 135 | let coord = location::Coordinate::try_from(lat_long.as_str()) 136 | .map_err(|_| Error::from(format!("invalid `DEFAULT_LAT_LONG`: {}", lat_long)))?; 137 | let description = env::var("DEFAULT_LOCATION").unwrap_or_else(|_| "".into()); 138 | let location = location::Location { description, coord }; 139 | Some(location) 140 | } 141 | _ => None, 142 | }; 143 | Ok(location) 144 | } 145 | -------------------------------------------------------------------------------- /src/precipitation.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::fmt; 3 | 4 | use serde::Deserialize; 5 | 6 | #[derive(Clone, Debug, Deserialize)] 7 | pub struct Intensity(pub f64); 8 | 9 | impl Intensity { 10 | pub fn humanized(&self) -> String { 11 | let intensity = if (0.0..=0.002).contains(&self.0) { 12 | "no" 13 | } else if (0.002..=0.017).contains(&self.0) { 14 | "very light" 15 | } else if (0.017..=0.1).contains(&self.0) { 16 | "light" 17 | } else if (0.1..=0.4).contains(&self.0) { 18 | "moderate" 19 | } else { 20 | "heavy" 21 | }; 22 | intensity.into() 23 | } 24 | } 25 | 26 | impl Eq for Intensity {} 27 | impl PartialEq for Intensity { 28 | fn eq(&self, other: &Self) -> bool { 29 | self.0 == other.0 30 | } 31 | } 32 | 33 | impl PartialOrd for Intensity { 34 | fn partial_cmp(&self, other: &Self) -> Option { 35 | self.0.partial_cmp(&other.0) 36 | } 37 | } 38 | 39 | impl Ord for Intensity { 40 | fn cmp(&self, other: &Self) -> Ordering { 41 | self.partial_cmp(other).unwrap() 42 | } 43 | } 44 | 45 | impl Eq for Probability {} 46 | impl PartialEq for Probability { 47 | fn eq(&self, other: &Self) -> bool { 48 | self.0 == other.0 49 | } 50 | } 51 | 52 | impl PartialOrd for Probability { 53 | fn partial_cmp(&self, other: &Self) -> Option { 54 | self.0.partial_cmp(&other.0) 55 | } 56 | } 57 | 58 | impl Ord for Probability { 59 | fn cmp(&self, other: &Self) -> Ordering { 60 | self.partial_cmp(other).unwrap() 61 | } 62 | } 63 | 64 | impl fmt::Display for Intensity { 65 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 66 | let intensity = format!("{:.3}", (self.0 * 1000.).round() / 1000.); 67 | let mut intensity = String::from(intensity.trim_end_matches('0')); 68 | if intensity.ends_with('.') { 69 | intensity.push('0'); 70 | } 71 | write!(f, "{}\"", intensity) 72 | } 73 | } 74 | 75 | #[derive(Clone, Debug, Deserialize)] 76 | pub struct Probability(pub f64); 77 | 78 | impl fmt::Display for Probability { 79 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 80 | write!(f, "{}%", (self.0 * 100.).round()) 81 | } 82 | } 83 | 84 | #[test] 85 | fn test_fmt() { 86 | let i = Intensity(0.); 87 | assert_eq!(format!("{}", i), "0.0\""); 88 | 89 | let i = Intensity(0.1); 90 | assert_eq!(format!("{}", i), "0.1\""); 91 | 92 | let i = Intensity(0.12); 93 | assert_eq!(format!("{}", i), "0.12\""); 94 | 95 | let i = Intensity(0.1234); 96 | assert_eq!(format!("{}", i), "0.123\""); 97 | 98 | let i = Intensity(0.1235); 99 | assert_eq!(format!("{}", i), "0.124\""); 100 | } 101 | -------------------------------------------------------------------------------- /src/sparkline.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | pub struct Ascii { 4 | min: f64, 5 | max: f64, 6 | data: Vec, 7 | step: usize, 8 | } 9 | 10 | impl Ascii { 11 | pub fn new(min: f64, max: f64, data: Vec, step: usize) -> Self { 12 | Self { 13 | min, 14 | max, 15 | data, 16 | step, 17 | } 18 | } 19 | } 20 | 21 | impl fmt::Display for Ascii { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | let ticks = vec!['▁', '▂', '▃', '▄', '▅', '▆', '▇']; 24 | let ticks_len = (ticks.len() - 1) as f64; 25 | let sparkline: String = self 26 | .data 27 | .iter() 28 | .step_by(self.step) 29 | .map(|x| { 30 | let mut i = ticks_len * (x - self.min); 31 | if i > 0. { 32 | i = (i / self.max).round(); 33 | } 34 | ticks[i as usize] 35 | }) 36 | .collect(); 37 | write!(f, "{}", sparkline) 38 | } 39 | } 40 | 41 | #[test] 42 | fn test_display() { 43 | let s = Ascii { 44 | min: 0., 45 | max: 1., 46 | data: vec![0.], 47 | step: 2, 48 | }; 49 | assert_eq!(format!("{}", s), "▁"); 50 | let s = Ascii { 51 | min: 0., 52 | max: 1., 53 | data: vec![0., 10.], 54 | step: 2, 55 | }; 56 | assert_eq!(format!("{}", s), "▁"); 57 | 58 | let s = Ascii { 59 | min: 0., 60 | max: 1., 61 | data: vec![0., 0., 1.], 62 | step: 2, 63 | }; 64 | assert_eq!(format!("{}", s), "▁▇"); 65 | } 66 | -------------------------------------------------------------------------------- /src/theme.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use forecast::Icon; 4 | 5 | #[derive(Debug)] 6 | pub enum Theme { 7 | Light, 8 | Dark, 9 | } 10 | 11 | impl Theme { 12 | pub fn icon_path(&self, icon: &Icon) -> Option { 13 | match *icon { 14 | Icon::ClearDay => Some("Sun"), 15 | Icon::ClearNight => Some("Moon"), 16 | Icon::Rain => Some("Cloud-Rain"), 17 | Icon::Snow => Some("Cloud-Snow"), 18 | Icon::Sleet => Some("Cloud-Snow-Alt"), 19 | Icon::Wind => Some("Wind"), 20 | Icon::Fog => Some("Cloud-Fog"), 21 | Icon::Cloudy => Some("Cloud"), 22 | Icon::PartlyCloudyDay => Some("Cloud-Sun"), 23 | Icon::PartlyCloudyNight => Some("Cloud-Moon"), 24 | Icon::Unknown(_) => None, 25 | } 26 | .map(|x| { 27 | let theme = match *self { 28 | Theme::Light => "Light", 29 | Theme::Dark => "Dark", 30 | }; 31 | format!("icons/{}-{}.png", theme, x).into() 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /workflow/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icon.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Cloud-Fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Cloud-Fog.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Cloud-Moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Cloud-Moon.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Cloud-Rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Cloud-Rain.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Cloud-Snow-Alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Cloud-Snow-Alt.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Cloud-Snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Cloud-Snow.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Cloud-Sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Cloud-Sun.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Cloud.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Moon.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Sun.png -------------------------------------------------------------------------------- /workflow/icons/Dark-Wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Dark-Wind.png -------------------------------------------------------------------------------- /workflow/icons/Light-Cloud-Fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Cloud-Fog.png -------------------------------------------------------------------------------- /workflow/icons/Light-Cloud-Moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Cloud-Moon.png -------------------------------------------------------------------------------- /workflow/icons/Light-Cloud-Rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Cloud-Rain.png -------------------------------------------------------------------------------- /workflow/icons/Light-Cloud-Snow-Alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Cloud-Snow-Alt.png -------------------------------------------------------------------------------- /workflow/icons/Light-Cloud-Snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Cloud-Snow.png -------------------------------------------------------------------------------- /workflow/icons/Light-Cloud-Sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Cloud-Sun.png -------------------------------------------------------------------------------- /workflow/icons/Light-Cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Cloud.png -------------------------------------------------------------------------------- /workflow/icons/Light-Moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Moon.png -------------------------------------------------------------------------------- /workflow/icons/Light-Sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Sun.png -------------------------------------------------------------------------------- /workflow/icons/Light-Wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/Light-Wind.png -------------------------------------------------------------------------------- /workflow/icons/dark_sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/b6e5e04a7950bddb08b211781f0e736671e0f495/workflow/icons/dark_sky.png -------------------------------------------------------------------------------- /workflow/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | com.kejadlen.darksky 7 | category 8 | Internet 9 | connections 10 | 11 | 2A5C0A87-204E-49EA-94A7-8E62BB4EFD8A 12 | 13 | 14 | destinationuid 15 | E4BEE57E-E97D-4B2B-ADC1-988BF006A8CC 16 | modifiers 17 | 0 18 | modifiersubtext 19 | 20 | vitoclose 21 | 22 | 23 | 24 | 3331E3E1-4FAE-41FD-8133-DAB62076AB91 25 | 26 | 27 | destinationuid 28 | E4BEE57E-E97D-4B2B-ADC1-988BF006A8CC 29 | modifiers 30 | 0 31 | modifiersubtext 32 | 33 | vitoclose 34 | 35 | 36 | 37 | E4BEE57E-E97D-4B2B-ADC1-988BF006A8CC 38 | 39 | 40 | destinationuid 41 | F496E087-4042-44E7-B07E-71986ED6CDBA 42 | modifiers 43 | 0 44 | modifiersubtext 45 | 46 | vitoclose 47 | 48 | 49 | 50 | 51 | createdby 52 | Alpha Chen 53 | description 54 | 55 | disabled 56 | 57 | name 58 | Dark Sky 59 | objects 60 | 61 | 62 | config 63 | 64 | concurrently 65 | 66 | escaping 67 | 0 68 | script 69 | open "https://darksky.net/{query}" 70 | scriptargtype 71 | 0 72 | scriptfile 73 | 74 | type 75 | 0 76 | 77 | type 78 | alfred.workflow.action.script 79 | uid 80 | F496E087-4042-44E7-B07E-71986ED6CDBA 81 | version 82 | 2 83 | 84 | 85 | config 86 | 87 | alfredfiltersresults 88 | 89 | alfredfiltersresultsmatchmode 90 | 0 91 | argumenttreatemptyqueryasnil 92 | 93 | argumenttrimmode 94 | 0 95 | argumenttype 96 | 1 97 | escaping 98 | 127 99 | keyword 100 | darksky 101 | queuedelaycustom 102 | 3 103 | queuedelayimmediatelyinitially 104 | 105 | queuedelaymode 106 | 1 107 | queuemode 108 | 2 109 | runningsubtext 110 | Retrieving location/weather... 111 | script 112 | ./dark-sky {query} 113 | scriptargtype 114 | 1 115 | scriptfile 116 | dark-sky 117 | subtext 118 | 119 | title 120 | Dark Sky 121 | type 122 | 8 123 | withspace 124 | 125 | 126 | type 127 | alfred.workflow.input.scriptfilter 128 | uid 129 | 2A5C0A87-204E-49EA-94A7-8E62BB4EFD8A 130 | version 131 | 3 132 | 133 | 134 | config 135 | 136 | inputstring 137 | {query} 138 | matchcasesensitive 139 | 140 | matchmode 141 | 1 142 | matchstring 143 | 144 | 145 | type 146 | alfred.workflow.utility.filter 147 | uid 148 | E4BEE57E-E97D-4B2B-ADC1-988BF006A8CC 149 | version 150 | 1 151 | 152 | 153 | config 154 | 155 | alfredfiltersresults 156 | 157 | alfredfiltersresultsmatchmode 158 | 0 159 | argumenttreatemptyqueryasnil 160 | 161 | argumenttrimmode 162 | 0 163 | argumenttype 164 | 1 165 | escaping 166 | 127 167 | keyword 168 | forecast 169 | queuedelaycustom 170 | 3 171 | queuedelayimmediatelyinitially 172 | 173 | queuedelaymode 174 | 1 175 | queuemode 176 | 2 177 | runningsubtext 178 | Retrieving location/weather... 179 | script 180 | ./dark-sky {query} 181 | scriptargtype 182 | 1 183 | scriptfile 184 | dark-sky 185 | subtext 186 | 187 | title 188 | Dark Sky 189 | type 190 | 8 191 | withspace 192 | 193 | 194 | type 195 | alfred.workflow.input.scriptfilter 196 | uid 197 | 3331E3E1-4FAE-41FD-8133-DAB62076AB91 198 | version 199 | 3 200 | 201 | 202 | config 203 | 204 | alfredfiltersresults 205 | 206 | alfredfiltersresultsmatchmode 207 | 0 208 | argumenttreatemptyqueryasnil 209 | 210 | argumenttrimmode 211 | 0 212 | argumenttype 213 | 2 214 | escaping 215 | 102 216 | keyword 217 | darksky 218 | queuedelaycustom 219 | 3 220 | queuedelayimmediatelyinitially 221 | 222 | queuedelaymode 223 | 0 224 | queuemode 225 | 1 226 | runningsubtext 227 | 228 | script 229 | # THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH. 230 | readonly remote_info_plist='https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/master/workflow/info.plist' 231 | readonly workflow_url='kejadlen/dark-sky.alfredworkflow' 232 | readonly download_type='github_release' 233 | readonly frequency_check='4' 234 | 235 | # FROM HERE ON, CODE SHOULD BE LEFT UNTOUCHED! 236 | function abort { 237 | echo "${1}" >&2 238 | exit 1 239 | } 240 | 241 | function url_exists { 242 | curl --silent --location --output /dev/null --fail --range 0-0 "${1}" 243 | } 244 | 245 | function notification { 246 | readonly local notificator="$(find . -type d -name 'Notificator.app')" 247 | if [[ -n "${notificator}" ]]; then 248 | "${notificator}/Contents/Resources/Scripts/notificator" --message "${1}" --title "${alfred_workflow_name}" --subtitle 'A new version is available' 249 | return 250 | fi 251 | 252 | readonly local terminal_notifier="$(find . -type f -name 'terminal-notifier')" 253 | if [[ -n "${terminal_notifier}" ]]; then 254 | "${terminal_notifier}" -title "${alfred_workflow_name}" -subtitle 'A new version is available' -message "${1}" 255 | return 256 | fi 257 | 258 | osascript -e "display notification \"${1}\" with title \"${alfred_workflow_name}\" subtitle \"A new version is available\"" 259 | } 260 | 261 | # Local sanity checks 262 | readonly local_info_plist='info.plist' 263 | readonly local_version="$(/usr/libexec/PlistBuddy -c 'print version' "${local_info_plist}")" 264 | 265 | [[ -n "${local_version}" ]] || abort 'You need to set a workflow version in the configuration sheet.' 266 | [[ "${download_type}" =~ ^(direct|page|github_release)$ ]] || abort "'download_type' (${download_type}) needs to be one of 'direct', 'page', or 'github_release'." 267 | [[ "${frequency_check}" =~ ^[0-9]+$ ]] || abort "'frequency_check' (${frequency_check}) needs to be a number." 268 | 269 | # Check for updates 270 | if [[ $(find "${local_info_plist}" -mtime +"${frequency_check}"d) ]]; then 271 | if ! url_exists "${remote_info_plist}"; then abort "'remote_info_plist' (${remote_info_plist}) appears to not be reachable."; fi # Remote sanity check 272 | 273 | readonly tmp_file="$(mktemp)" 274 | curl --silent --location --output "${tmp_file}" "${remote_info_plist}" 275 | readonly remote_version="$(/usr/libexec/PlistBuddy -c 'print version' "${tmp_file}")" 276 | 277 | if [[ "${local_version}" == "${remote_version}" ]]; then 278 | touch "${local_info_plist}" # Reset timer by touching local file 279 | exit 0 280 | fi 281 | 282 | if [[ "${download_type}" == 'page' ]]; then 283 | notification 'Opening download page…' 284 | open "${workflow_url}" 285 | exit 0 286 | fi 287 | 288 | download_url="$([[ "${download_type}" == 'github_release' ]] && curl --silent "https://api.github.com/repos/${workflow_url}/releases/latest" | grep 'browser_download_url' | head -1 | sed -E 's/.*browser_download_url": "(.*)"/\1/' || echo "${workflow_url}")" 289 | 290 | if url_exists "${download_url}"; then 291 | notification 'Downloading and installing…' 292 | curl --silent --location --output "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" "${download_url}" 293 | open "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" 294 | else 295 | abort "'workflow_url' (${download_url}) appears to not be reachable." 296 | fi 297 | fi 298 | scriptargtype 299 | 1 300 | scriptfile 301 | 302 | subtext 303 | 304 | title 305 | 306 | type 307 | 0 308 | withspace 309 | 310 | 311 | type 312 | alfred.workflow.input.scriptfilter 313 | uid 314 | B9B6DB3D-FF10-4E82-B5F3-AC80EF058E0D 315 | version 316 | 3 317 | 318 | 319 | config 320 | 321 | alfredfiltersresults 322 | 323 | alfredfiltersresultsmatchmode 324 | 0 325 | argumenttreatemptyqueryasnil 326 | 327 | argumenttrimmode 328 | 0 329 | argumenttype 330 | 2 331 | escaping 332 | 102 333 | keyword 334 | fo 335 | queuedelaycustom 336 | 3 337 | queuedelayimmediatelyinitially 338 | 339 | queuedelaymode 340 | 0 341 | queuemode 342 | 1 343 | runningsubtext 344 | 345 | script 346 | # THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH. 347 | readonly remote_info_plist='https://raw.githubusercontent.com/kejadlen/dark-sky.alfredworkflow/master/workflow/info.plist' 348 | readonly workflow_url='kejadlen/dark-sky.alfredworkflow' 349 | readonly download_type='github_release' 350 | readonly frequency_check='4' 351 | 352 | # FROM HERE ON, CODE SHOULD BE LEFT UNTOUCHED! 353 | function abort { 354 | echo "${1}" >&2 355 | exit 1 356 | } 357 | 358 | function url_exists { 359 | curl --silent --location --output /dev/null --fail --range 0-0 "${1}" 360 | } 361 | 362 | function notification { 363 | readonly local notificator="$(find . -type d -name 'Notificator.app')" 364 | if [[ -n "${notificator}" ]]; then 365 | "${notificator}/Contents/Resources/Scripts/notificator" --message "${1}" --title "${alfred_workflow_name}" --subtitle 'A new version is available' 366 | return 367 | fi 368 | 369 | readonly local terminal_notifier="$(find . -type f -name 'terminal-notifier')" 370 | if [[ -n "${terminal_notifier}" ]]; then 371 | "${terminal_notifier}" -title "${alfred_workflow_name}" -subtitle 'A new version is available' -message "${1}" 372 | return 373 | fi 374 | 375 | osascript -e "display notification \"${1}\" with title \"${alfred_workflow_name}\" subtitle \"A new version is available\"" 376 | } 377 | 378 | # Local sanity checks 379 | readonly local_info_plist='info.plist' 380 | readonly local_version="$(/usr/libexec/PlistBuddy -c 'print version' "${local_info_plist}")" 381 | 382 | [[ -n "${local_version}" ]] || abort 'You need to set a workflow version in the configuration sheet.' 383 | [[ "${download_type}" =~ ^(direct|page|github_release)$ ]] || abort "'download_type' (${download_type}) needs to be one of 'direct', 'page', or 'github_release'." 384 | [[ "${frequency_check}" =~ ^[0-9]+$ ]] || abort "'frequency_check' (${frequency_check}) needs to be a number." 385 | 386 | # Check for updates 387 | if [[ $(find "${local_info_plist}" -mtime +"${frequency_check}"d) ]]; then 388 | if ! url_exists "${remote_info_plist}"; then abort "'remote_info_plist' (${remote_info_plist}) appears to not be reachable."; fi # Remote sanity check 389 | 390 | readonly tmp_file="$(mktemp)" 391 | curl --silent --location --output "${tmp_file}" "${remote_info_plist}" 392 | readonly remote_version="$(/usr/libexec/PlistBuddy -c 'print version' "${tmp_file}")" 393 | 394 | if [[ "${local_version}" == "${remote_version}" ]]; then 395 | touch "${local_info_plist}" # Reset timer by touching local file 396 | exit 0 397 | fi 398 | 399 | if [[ "${download_type}" == 'page' ]]; then 400 | notification 'Opening download page…' 401 | open "${workflow_url}" 402 | exit 0 403 | fi 404 | 405 | download_url="$([[ "${download_type}" == 'github_release' ]] && curl --silent "https://api.github.com/repos/${workflow_url}/releases/latest" | grep 'browser_download_url' | head -1 | sed -E 's/.*browser_download_url": "(.*)"/\1/' || echo "${workflow_url}")" 406 | 407 | if url_exists "${download_url}"; then 408 | notification 'Downloading and installing…' 409 | curl --silent --location --output "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" "${download_url}" 410 | open "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" 411 | else 412 | abort "'workflow_url' (${download_url}) appears to not be reachable." 413 | fi 414 | fi 415 | scriptargtype 416 | 1 417 | scriptfile 418 | 419 | subtext 420 | 421 | title 422 | 423 | type 424 | 0 425 | withspace 426 | 427 | 428 | type 429 | alfred.workflow.input.scriptfilter 430 | uid 431 | E8F22EA9-54D4-4F7D-8AAB-5875A39AE683 432 | version 433 | 3 434 | 435 | 436 | readme 437 | These environment variables can be configured: 438 | 439 | - `DARK_SKY_API_KEY`: Get an API key [here][dark-sky-api-key]. 440 | - `GOOGLE_API_KEY`: Get an API key [here][google-api-key]. (Used for geocoding queries. *This can be omitted if you only want the forecast for the current location*.) 441 | - `FORECAST_UNITS`: Defaults to `auto`, which sets the units based on the location. Use `si` for Celsius and `us` for Fahrenheit. 442 | - `FORECAST_LANG`: Defaults to `en`. See [Dark Sky documentation][dark-sky-lang] for full list of language options. 443 | - `DEFAULT_LAT_LONG`: Set this to override IP geolocation. Ex: `47.7396,-122.3426` for Seattle. 444 | - `DEFAULT_LOCATION`: Used for displaying the location name when using `DEFAULT_LAT_LONG`. 445 | - `LIGHT_ICONS`: `true` gives white icons, `false` gives black icons. 446 | 447 | [dark-sky-api-key]: https://darksky.net/dev/register 448 | [google-api-key]: https://developers.google.com/maps/documentation/geocoding/#api_key 449 | [dark-sky-lang]: https://darksky.net/dev/docs#forecast-request 450 | uidata 451 | 452 | 2A5C0A87-204E-49EA-94A7-8E62BB4EFD8A 453 | 454 | xpos 455 | 190 456 | ypos 457 | 30 458 | 459 | 3331E3E1-4FAE-41FD-8133-DAB62076AB91 460 | 461 | xpos 462 | 190 463 | ypos 464 | 140 465 | 466 | B9B6DB3D-FF10-4E82-B5F3-AC80EF058E0D 467 | 468 | colorindex 469 | 12 470 | note 471 | OneUpdater 472 | xpos 473 | 190 474 | ypos 475 | 260 476 | 477 | E4BEE57E-E97D-4B2B-ADC1-988BF006A8CC 478 | 479 | xpos 480 | 430 481 | ypos 482 | 60 483 | 484 | E8F22EA9-54D4-4F7D-8AAB-5875A39AE683 485 | 486 | colorindex 487 | 12 488 | note 489 | OneUpdater 490 | xpos 491 | 190 492 | ypos 493 | 400 494 | 495 | F496E087-4042-44E7-B07E-71986ED6CDBA 496 | 497 | xpos 498 | 590 499 | ypos 500 | 30 501 | 502 | 503 | variables 504 | 505 | DARK_SKY_API_KEY 506 | 507 | DARK_SKY_ENDPOINT 508 | api.pirateweather.net 509 | DEFAULT_LAT_LONG 510 | 511 | DEFAULT_LOCATION 512 | 513 | FORECAST_LANG 514 | en 515 | FORECAST_UNITS 516 | auto 517 | GOOGLE_API_KEY 518 | 519 | LIGHT_ICONS 520 | 521 | 522 | variablesdontexport 523 | 524 | DARK_SKY_API_KEY 525 | DEFAULT_LOCATION 526 | DEFAULT_LAT_LONG 527 | LIGHT_ICONS 528 | GOOGLE_API_KEY 529 | 530 | version 531 | 3.1.0 532 | webaddress 533 | http://github.com/kejadlen/dark-sky.alfredworkflow 534 | 535 | 536 | --------------------------------------------------------------------------------