mirror of
https://github.com/yuzu-emu/liftinstall.git
synced 2024-11-22 09:35:38 +01:00
Add support for shortcuts on Windows
This commit is contained in:
parent
94660994ec
commit
82b3681a74
200
Cargo.lock
generated
200
Cargo.lock
generated
@ -27,6 +27,14 @@ dependencies = [
|
||||
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aster"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.11"
|
||||
@ -46,6 +54,31 @@ dependencies = [
|
||||
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "0.9.1"
|
||||
@ -98,6 +131,14 @@ name = "cc"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.4"
|
||||
@ -113,6 +154,17 @@ dependencies = [
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.32.0"
|
||||
@ -204,6 +256,15 @@ dependencies = [
|
||||
"cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fern"
|
||||
version = "0.5.6"
|
||||
@ -282,6 +343,11 @@ name = "gcc"
|
||||
version = "0.3.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.3.2"
|
||||
@ -403,10 +469,22 @@ dependencies = [
|
||||
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liftinstall"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -454,6 +532,14 @@ name = "matches"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.0.1"
|
||||
@ -600,6 +686,14 @@ name = "nodrop"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.39"
|
||||
@ -652,6 +746,11 @@ dependencies = [
|
||||
"vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "1.0.1"
|
||||
@ -710,6 +809,26 @@ dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quasi"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quasi_codegen"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.5"
|
||||
@ -811,6 +930,11 @@ dependencies = [
|
||||
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.2.0"
|
||||
@ -948,6 +1072,48 @@ dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex"
|
||||
version = "0.58.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex_errors"
|
||||
version = "0.58.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex_pos"
|
||||
version = "0.58.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntex_syntax"
|
||||
version = "0.58.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take"
|
||||
version = "0.1.0"
|
||||
@ -973,6 +1139,15 @@ dependencies = [
|
||||
"remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
@ -1233,6 +1408,11 @@ name = "unicode-width"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
@ -1422,8 +1602,11 @@ dependencies = [
|
||||
"checksum aho-corasick 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c6d463cbe7ed28720b5b489e7c083eeb8f90d08be2a0d6bb9e1ffea9ce1afa"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
|
||||
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9"
|
||||
"checksum bindgen 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c57d6c0f6e31f8dcf4d12720a3c2a9ffb70638772a5784976cf4fce52145f22a"
|
||||
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
|
||||
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
||||
"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789"
|
||||
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
|
||||
@ -1432,8 +1615,10 @@ dependencies = [
|
||||
"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
|
||||
"checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b"
|
||||
"checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275"
|
||||
"checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c"
|
||||
"checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e"
|
||||
"checksum chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e48d85528df61dc964aa43c5f6ca681a19cfa74939b2348d204bd08a981f2fb0"
|
||||
"checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a"
|
||||
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
|
||||
"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
|
||||
"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d"
|
||||
@ -1444,6 +1629,7 @@ dependencies = [
|
||||
"checksum dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37a76dd8b997af7107d0bb69d43903cf37153a18266f8b3fdb9911f28efb5444"
|
||||
"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd"
|
||||
"checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d"
|
||||
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
|
||||
"checksum fern 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "57915fe00a83af935983eb2d00b0ecc62419c4741b28c207ecbf98fd4a1b94c8"
|
||||
"checksum filetime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da4b9849e77b13195302c174324b5ba73eec9b236b24c221a61000daefb95c5f"
|
||||
"checksum flate2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37847f133aae7acf82bb9577ccd8bda241df836787642654286e79679826a54b"
|
||||
@ -1455,6 +1641,7 @@ dependencies = [
|
||||
"checksum futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "884dbe32a6ae4cd7da5c6db9b78114449df9953b8d490c9d7e1b51720b922c62"
|
||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b6288d7db100340ca12873fd4d08ad1b8f206a9457798dfb17c018a33fee540"
|
||||
"checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7"
|
||||
"checksum hyper-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffb1bd5e518d3065840ab315dbbf44e4420e5f7d80e2cb93fa6ffffc50522378"
|
||||
@ -1469,9 +1656,11 @@ dependencies = [
|
||||
"checksum libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122"
|
||||
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
|
||||
"checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98"
|
||||
"checksum libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fd38073de8f7965d0c17d30546d4bb6da311ab428d1c7a3fc71dff7f9d4979b9"
|
||||
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
|
||||
"checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2"
|
||||
"checksum matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "835511bab37c34c47da5cb44844bea2cfde0236db0b506f90ea4224482c9774a"
|
||||
"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
|
||||
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
|
||||
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
|
||||
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
|
||||
@ -1487,12 +1676,14 @@ dependencies = [
|
||||
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||
"checksum nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e752e3c216bc8a491c5b59fa46da10f1379ae450b19ac688e07f4bb55042e98"
|
||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
||||
"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"
|
||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
||||
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe"
|
||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
||||
"checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee"
|
||||
"checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985"
|
||||
"checksum openssl-sys 0.9.35 (registry+https://github.com/rust-lang/crates.io-index)" = "912f301a749394e1025d9dcddef6106ddee9252620e6d0a0e5f8d0681de9b129"
|
||||
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
"checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2"
|
||||
"checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b"
|
||||
@ -1501,6 +1692,8 @@ dependencies = [
|
||||
"checksum pkg-config 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "104630aa1c83213cbc76db0703630fcb0421dac3585063be4ce9a8a2feeaa745"
|
||||
"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
|
||||
"checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6"
|
||||
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
|
||||
"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
|
||||
"checksum quote 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3372dc35766b36a99ce2352bd1b6ea0137c38d215cc0c8780bf6de6df7842ba9"
|
||||
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
|
||||
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
|
||||
@ -1511,6 +1704,7 @@ dependencies = [
|
||||
"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a"
|
||||
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
|
||||
"checksum reqwest 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e237e32c3bfa55c95e29af872c8f481471d70b8a5ec15d85f4d274ffd92dd9"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
|
||||
"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
|
||||
"checksum schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1fabf2a7b6483a141426e1afd09ad543520a77ac49bd03c286e7696ccfd77f"
|
||||
@ -1530,9 +1724,14 @@ dependencies = [
|
||||
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e13df71f29f9440b50261a5882c86eac334f1badb3134ec26f0de2f1418e44"
|
||||
"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e"
|
||||
"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c"
|
||||
"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
|
||||
"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
|
||||
"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5"
|
||||
"checksum tar 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "e8f41ca4a5689f06998f0247fcb60da6c760f1950cc9df2a10d71575ad0b062a"
|
||||
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
|
||||
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
|
||||
@ -1559,6 +1758,7 @@ dependencies = [
|
||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||
"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
|
||||
|
@ -51,3 +51,5 @@ nfd = "0.0.4"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
winres = "0.1"
|
||||
bindgen = "0.26.3"
|
||||
cc = "1.0"
|
||||
|
21
build.rs
21
build.rs
@ -2,6 +2,12 @@ extern crate walkdir;
|
||||
#[cfg(windows)]
|
||||
extern crate winres;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate bindgen;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate cc;
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use std::env;
|
||||
@ -24,6 +30,21 @@ fn handle_binary() {
|
||||
let mut res = winres::WindowsResource::new();
|
||||
res.set_icon("static/favicon.ico");
|
||||
res.compile().expect("Failed to generate metadata");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("src/native/interop.h")
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("interop.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.file("src/native/interop.cpp")
|
||||
.compile("interop");
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
|
@ -19,6 +19,14 @@ pub struct PackageSource {
|
||||
pub config: toml::Value,
|
||||
}
|
||||
|
||||
/// Describes if/how a shortcut should be built for a package.
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct PackageShortcut {
|
||||
pub name: String,
|
||||
pub relative_path: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// Describes a overview of a individual package.
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct PackageDescription {
|
||||
@ -26,6 +34,8 @@ pub struct PackageDescription {
|
||||
pub description: String,
|
||||
pub default: Option<bool>,
|
||||
pub source: PackageSource,
|
||||
#[serde(default)]
|
||||
pub shortcuts: Vec<PackageShortcut>,
|
||||
}
|
||||
|
||||
/// Describes the application itself.
|
||||
|
@ -26,6 +26,8 @@ use logging::LoggingErrors;
|
||||
|
||||
use dirs::home_dir;
|
||||
|
||||
use std::fs::remove_file;
|
||||
|
||||
/// A message thrown during the installation of packages.
|
||||
#[derive(Serialize)]
|
||||
pub enum InstallMessage {
|
||||
@ -61,7 +63,10 @@ pub struct InstallationStatus {
|
||||
pub struct LocalInstallation {
|
||||
pub name: String,
|
||||
pub version: Version,
|
||||
/// Relative paths to generated files
|
||||
pub files: Vec<String>,
|
||||
/// Absolute paths to generated shortcut files
|
||||
pub shortcuts: Vec<String>,
|
||||
}
|
||||
|
||||
impl InstallerFramework {
|
||||
@ -148,7 +153,20 @@ impl InstallerFramework {
|
||||
if let Err(v) = messages.send(InstallMessage::Status(msg.to_string(), progress as _)) {
|
||||
error!("Failed to submit queue message: {:?}", v);
|
||||
}
|
||||
}).map(|_x| ())
|
||||
}).map(|_x| ())?;
|
||||
|
||||
// Delete the metadata file
|
||||
let path = self
|
||||
.install_path
|
||||
.as_ref()
|
||||
.log_expect("No install path specified");
|
||||
|
||||
remove_file(path.join("metadata.json"))
|
||||
.map_err(|x| format!("Failed to delete metadata: {:?}", x))?;
|
||||
|
||||
// Logging will have to be done later
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Saves the applications database.
|
||||
|
@ -47,6 +47,7 @@ mod config;
|
||||
mod http;
|
||||
mod installer;
|
||||
mod logging;
|
||||
mod native;
|
||||
mod rest;
|
||||
mod sources;
|
||||
mod tasks;
|
||||
|
89
src/native/interop.cpp
Normal file
89
src/native/interop.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Misc interop helpers.
|
||||
**/
|
||||
|
||||
#include "windows.h"
|
||||
#include "winnls.h"
|
||||
#include "shobjidl.h"
|
||||
#include "objbase.h"
|
||||
#include "objidl.h"
|
||||
#include "shlguid.h"
|
||||
|
||||
extern "C" int saveShortcut(
|
||||
const char *shortcutPath,
|
||||
const char *description,
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *workingDir) {
|
||||
char* errStr = NULL;
|
||||
HRESULT h;
|
||||
IShellLink* shellLink = NULL;
|
||||
IPersistFile* persistFile = NULL;
|
||||
|
||||
#ifdef _WIN64
|
||||
wchar_t wName[MAX_PATH+1];
|
||||
#else
|
||||
WORD wName[MAX_PATH+1];
|
||||
#endif
|
||||
|
||||
int id;
|
||||
|
||||
// Initialize the COM library
|
||||
h = CoInitialize(NULL);
|
||||
if (FAILED(h)) {
|
||||
errStr = "Failed to initialize COM library";
|
||||
goto err;
|
||||
}
|
||||
|
||||
h = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
|
||||
IID_IShellLink, (PVOID*)&shellLink );
|
||||
if (FAILED(h)) {
|
||||
errStr = "Failed to create IShellLink";
|
||||
goto err;
|
||||
}
|
||||
|
||||
h = shellLink->QueryInterface(IID_IPersistFile, (PVOID*)&persistFile);
|
||||
if (FAILED(h)) {
|
||||
errStr = "Failed to get IPersistFile";
|
||||
goto err;
|
||||
}
|
||||
|
||||
//Append the shortcut name to the folder
|
||||
MultiByteToWideChar(CP_ACP,0,shortcutPath,-1,wName,MAX_PATH);
|
||||
|
||||
// Load the file if it exists, to get the values for anything
|
||||
// that we do not set. Ignore errors, such as if it does not exist.
|
||||
h = persistFile->Load(wName, 0);
|
||||
|
||||
// Set the fields for which the application has set a value
|
||||
if (description!=NULL)
|
||||
shellLink->SetDescription(description);
|
||||
if (path!=NULL)
|
||||
shellLink->SetPath(path);
|
||||
if (args!=NULL)
|
||||
shellLink->SetArguments(args);
|
||||
if (workingDir!=NULL)
|
||||
shellLink->SetWorkingDirectory(workingDir);
|
||||
|
||||
//Save the shortcut to disk
|
||||
h = persistFile->Save(wName, TRUE);
|
||||
if (FAILED(h)) {
|
||||
errStr = "Failed to save shortcut";
|
||||
goto err;
|
||||
}
|
||||
|
||||
persistFile->Release();
|
||||
shellLink->Release();
|
||||
CoUninitialize();
|
||||
return h;
|
||||
|
||||
err:
|
||||
if (persistFile != NULL)
|
||||
persistFile->Release();
|
||||
if (shellLink != NULL)
|
||||
shellLink->Release();
|
||||
CoUninitialize();
|
||||
|
||||
return h;
|
||||
}
|
||||
|
6
src/native/interop.h
Normal file
6
src/native/interop.h
Normal file
@ -0,0 +1,6 @@
|
||||
int saveShortcut(
|
||||
const char *shortcutPath,
|
||||
const char *description,
|
||||
const char *path,
|
||||
const char *args,
|
||||
const char *workingDir);
|
80
src/native/mod.rs
Normal file
80
src/native/mod.rs
Normal file
@ -0,0 +1,80 @@
|
||||
//! Natives/platform specific interactions.
|
||||
|
||||
#[cfg(windows)]
|
||||
mod natives {
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use std::ffi::CString;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
|
||||
use std::env;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/interop.rs"));
|
||||
|
||||
// Needed here for Windows interop
|
||||
#[allow(unsafe_code)]
|
||||
pub fn create_shortcut(
|
||||
name: &str,
|
||||
description: &str,
|
||||
target: &str,
|
||||
args: &str,
|
||||
working_dir: &str,
|
||||
) -> Result<String, String> {
|
||||
let source_file = format!(
|
||||
"{}\\Microsoft\\Windows\\Start Menu\\Programs\\{}.lnk",
|
||||
env::var("APPDATA").log_expect("APPDATA is bad, apparently"),
|
||||
name
|
||||
);
|
||||
|
||||
info!("Generating shortcut @ {:?}", source_file);
|
||||
|
||||
let native_target_dir = CString::new(source_file.clone())
|
||||
.log_expect("Error while converting to C-style string");
|
||||
let native_description =
|
||||
CString::new(description).log_expect("Error while converting to C-style string");
|
||||
let native_target =
|
||||
CString::new(target).log_expect("Error while converting to C-style string");
|
||||
let native_args = CString::new(args).log_expect("Error while converting to C-style string");
|
||||
let native_working_dir =
|
||||
CString::new(working_dir).log_expect("Error while converting to C-style string");
|
||||
|
||||
let shortcutResult = unsafe {
|
||||
saveShortcut(
|
||||
native_target_dir.as_ptr(),
|
||||
native_description.as_ptr(),
|
||||
native_target.as_ptr(),
|
||||
native_args.as_ptr(),
|
||||
native_working_dir.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
match shortcutResult {
|
||||
0 => Ok(source_file),
|
||||
_ => Err(format!(
|
||||
"Windows gave bad result while creating shortcut: {:?}",
|
||||
shortcutResult
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
mod natives {
|
||||
pub fn create_shortcut(
|
||||
name: &str,
|
||||
description: &str,
|
||||
target: &str,
|
||||
args: &str,
|
||||
working_dir: &str,
|
||||
) -> Result<String, String> {
|
||||
// TODO: no-op
|
||||
warn!("create_shortcut is stubbed!");
|
||||
|
||||
Ok("".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::natives::*;
|
18
src/rest.rs
18
src/rest.rs
@ -338,9 +338,17 @@ impl Service for WebService {
|
||||
thread::spawn(move || {
|
||||
let mut tx = tx;
|
||||
loop {
|
||||
let response = receiver
|
||||
.recv()
|
||||
.log_expect("Failed to receive message from runner thread");
|
||||
let mut panic_after_finish = false;
|
||||
|
||||
let response = match receiver
|
||||
.recv() {
|
||||
Ok(v) => v,
|
||||
Err(v) => {
|
||||
error!("Queue message failed: {:?}", v);
|
||||
panic_after_finish = true;
|
||||
InstallMessage::Error("Internal error".to_string())
|
||||
}
|
||||
};
|
||||
|
||||
if let InstallMessage::EOF = response {
|
||||
break;
|
||||
@ -353,6 +361,10 @@ impl Service for WebService {
|
||||
.send(Ok(response.into_bytes().into()))
|
||||
.wait()
|
||||
.log_expect("Failed to write JSON response payload to client");
|
||||
|
||||
if panic_after_finish {
|
||||
panic!("Failed to read from queue (flushed error message successfully)");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskOrdering;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use tasks::resolver::ResolvePackageTask;
|
||||
@ -83,10 +85,13 @@ impl Task for DownloadPackageTask {
|
||||
Ok(TaskParamType::FileContents(version, file, data_storage))
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
vec![Box::new(ResolvePackageTask {
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(ResolvePackageTask {
|
||||
name: self.name.clone(),
|
||||
})]
|
||||
}),
|
||||
)]
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
|
@ -4,11 +4,12 @@ use installer::InstallerFramework;
|
||||
|
||||
use tasks::install_dir::VerifyInstallDirTask;
|
||||
use tasks::install_pkg::InstallPackageTask;
|
||||
use tasks::save_database::SaveDatabaseTask;
|
||||
use tasks::save_executable::SaveExecutableTask;
|
||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskOrdering;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
pub struct InstallTask {
|
||||
@ -28,28 +29,38 @@ impl Task for InstallTask {
|
||||
Ok(TaskParamType::None)
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
let mut elements = Vec::<Box<Task>>::new();
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
let mut elements = Vec::new();
|
||||
|
||||
elements.push(Box::new(VerifyInstallDirTask {
|
||||
elements.push(TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(VerifyInstallDirTask {
|
||||
clean_install: self.fresh_install,
|
||||
}));
|
||||
}),
|
||||
));
|
||||
|
||||
for item in &self.items {
|
||||
elements.push(Box::new(InstallPackageTask { name: item.clone() }));
|
||||
elements.push(TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(InstallPackageTask { name: item.clone() }),
|
||||
));
|
||||
}
|
||||
|
||||
for item in &self.uninstall_items {
|
||||
elements.push(Box::new(UninstallPackageTask {
|
||||
elements.push(TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(UninstallPackageTask {
|
||||
name: item.clone(),
|
||||
optional: false,
|
||||
}));
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
elements.push(Box::new(SaveDatabaseTask {}));
|
||||
|
||||
if self.fresh_install {
|
||||
elements.push(Box::new(SaveExecutableTask {}));
|
||||
elements.push(TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(SaveExecutableTask {}),
|
||||
));
|
||||
}
|
||||
|
||||
elements
|
||||
|
@ -3,6 +3,7 @@
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use std::fs::create_dir_all;
|
||||
@ -46,7 +47,7 @@ impl Task for VerifyInstallDirTask {
|
||||
Ok(TaskParamType::None)
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,13 @@
|
||||
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::download_pkg::DownloadPackageTask;
|
||||
use tasks::install_shortcuts::InstallShortcutsTask;
|
||||
use tasks::save_database::SaveDatabaseTask;
|
||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskOrdering;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use config::PackageDescription;
|
||||
@ -11,9 +17,6 @@ use installer::LocalInstallation;
|
||||
use std::fs::create_dir_all;
|
||||
use std::io::copy;
|
||||
|
||||
use tasks::download_pkg::DownloadPackageTask;
|
||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
|
||||
use archives;
|
||||
@ -59,19 +62,23 @@ impl Task for InstallPackageTask {
|
||||
None => return Err(format!("Package {:?} could not be found.", self.name)),
|
||||
};
|
||||
|
||||
// Check to see if no archive was available.
|
||||
if let TaskParamType::Break = input
|
||||
.pop()
|
||||
.log_expect("Should have input from uninstaller!")
|
||||
{
|
||||
// No file to install, but all is good.
|
||||
return Ok(TaskParamType::None);
|
||||
}
|
||||
// Grab data from the shortcut generator
|
||||
let shortcuts = input.pop().log_expect("Should have input from resolver!");
|
||||
let shortcuts = match shortcuts {
|
||||
TaskParamType::GeneratedShortcuts(files) => files,
|
||||
// If the resolver returned early, we need to unwind
|
||||
TaskParamType::Break => return Ok(TaskParamType::None),
|
||||
_ => return Err("Unexpected shortcuts param type to install package".to_string()),
|
||||
};
|
||||
|
||||
// Ignore input from the uninstaller - no useful information passed
|
||||
input.pop();
|
||||
|
||||
// Grab data from the resolver
|
||||
let data = input.pop().log_expect("Should have input from resolver!");
|
||||
let (version, file, data) = match data {
|
||||
TaskParamType::FileContents(version, file, data) => (version, file, data),
|
||||
_ => return Err("Unexpected param type to install package".to_string()),
|
||||
_ => return Err("Unexpected file contents param type to install package".to_string()),
|
||||
};
|
||||
|
||||
let mut archive = archives::read_archive(&file.name, data.as_slice())?;
|
||||
@ -159,21 +166,35 @@ impl Task for InstallPackageTask {
|
||||
context.database.push(LocalInstallation {
|
||||
name: package.name.to_owned(),
|
||||
version,
|
||||
shortcuts,
|
||||
files: installed_files,
|
||||
});
|
||||
|
||||
Ok(TaskParamType::None)
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![
|
||||
TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(DownloadPackageTask {
|
||||
name: self.name.clone(),
|
||||
}),
|
||||
),
|
||||
TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(UninstallPackageTask {
|
||||
name: self.name.clone(),
|
||||
optional: true,
|
||||
}),
|
||||
),
|
||||
TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(InstallShortcutsTask {
|
||||
name: self.name.clone(),
|
||||
}),
|
||||
),
|
||||
TaskDependency::build(TaskOrdering::Post, Box::new(SaveDatabaseTask {})),
|
||||
]
|
||||
}
|
||||
|
||||
|
98
src/tasks/install_shortcuts.rs
Normal file
98
src/tasks/install_shortcuts.rs
Normal file
@ -0,0 +1,98 @@
|
||||
//! Generates shortcuts for a specified file.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use config::PackageDescription;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
|
||||
use native::create_shortcut;
|
||||
|
||||
pub struct InstallShortcutsTask {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Task for InstallShortcutsTask {
|
||||
fn execute(
|
||||
&mut self,
|
||||
_: Vec<TaskParamType>,
|
||||
context: &mut InstallerFramework,
|
||||
messenger: &Fn(&str, f64),
|
||||
) -> Result<TaskParamType, String> {
|
||||
messenger(
|
||||
&format!("Generating shortcuts for package {:?}...", self.name),
|
||||
0.0,
|
||||
);
|
||||
|
||||
let path = context
|
||||
.install_path
|
||||
.as_ref()
|
||||
.log_expect("No install path specified");
|
||||
|
||||
let starting_dir = path
|
||||
.to_str()
|
||||
.log_expect("Unable to build shortcut metadata (startingdir)");
|
||||
|
||||
let mut installed_files = Vec::new();
|
||||
|
||||
let mut metadata: Option<PackageDescription> = None;
|
||||
for description in &context
|
||||
.config
|
||||
.as_ref()
|
||||
.log_expect("Should have packages by now")
|
||||
.packages
|
||||
{
|
||||
if self.name == description.name {
|
||||
metadata = Some(description.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let package = match metadata {
|
||||
Some(v) => v,
|
||||
None => return Err(format!("Package {:?} could not be found.", self.name)),
|
||||
};
|
||||
|
||||
// Generate installer path
|
||||
let platform_extension = if cfg!(windows) {
|
||||
"maintenancetool.exe"
|
||||
} else {
|
||||
"maintenancetool"
|
||||
};
|
||||
|
||||
for shortcut in package.shortcuts {
|
||||
let tool_path = path.join(platform_extension);
|
||||
let tool_path = tool_path
|
||||
.to_str()
|
||||
.log_expect("Unable to build shortcut metadata (tool)");
|
||||
|
||||
let exe_path = path.join(shortcut.relative_path);
|
||||
let exe_path = exe_path
|
||||
.to_str()
|
||||
.log_expect("Unable to build shortcut metadata (exe)");
|
||||
|
||||
installed_files.push(create_shortcut(
|
||||
&shortcut.name,
|
||||
&shortcut.description,
|
||||
tool_path,
|
||||
// TODO: Send by list
|
||||
&format!("--launcher \"{}\"", exe_path),
|
||||
&starting_dir,
|
||||
)?);
|
||||
}
|
||||
|
||||
Ok(TaskParamType::GeneratedShortcuts(installed_files))
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
format!("InstallShortcutsTask (for {:?})", self.name)
|
||||
}
|
||||
}
|
@ -13,11 +13,13 @@ pub mod download_pkg;
|
||||
pub mod install;
|
||||
pub mod install_dir;
|
||||
pub mod install_pkg;
|
||||
pub mod install_shortcuts;
|
||||
pub mod resolver;
|
||||
pub mod save_database;
|
||||
pub mod save_executable;
|
||||
pub mod uninstall;
|
||||
pub mod uninstall_pkg;
|
||||
pub mod uninstall_shortcuts;
|
||||
|
||||
/// An abstraction over the various parameters that can be passed around.
|
||||
pub enum TaskParamType {
|
||||
@ -26,10 +28,34 @@ pub enum TaskParamType {
|
||||
File(Version, File),
|
||||
/// Downloaded contents of a file
|
||||
FileContents(Version, File, Vec<u8>),
|
||||
/// List of shortcuts that have been generated
|
||||
GeneratedShortcuts(Vec<String>),
|
||||
/// Tells the runtime to break parsing other dependencies
|
||||
Break,
|
||||
}
|
||||
|
||||
/// Specifies the relative ordering of a dependency with it's parent.
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum TaskOrdering {
|
||||
/// This task should occur before the main process
|
||||
Pre,
|
||||
/// This task should occur after the main process. These have their results discarded.
|
||||
Post,
|
||||
}
|
||||
|
||||
/// A dependency of a task with various properties.
|
||||
pub struct TaskDependency {
|
||||
ordering: TaskOrdering,
|
||||
task: Box<Task>,
|
||||
}
|
||||
|
||||
impl TaskDependency {
|
||||
/// Builds a new dependency from the specified task.
|
||||
pub fn build(ordering: TaskOrdering, task: Box<Task>) -> TaskDependency {
|
||||
TaskDependency { ordering, task }
|
||||
}
|
||||
}
|
||||
|
||||
/// A Task is a small, async task conforming to a fixed set of inputs/outputs.
|
||||
pub trait Task {
|
||||
/// Executes this individual task, evaluating to the given Output result.
|
||||
@ -44,7 +70,7 @@ pub trait Task {
|
||||
|
||||
/// Returns a vector containing all dependencies that need to be executed
|
||||
/// before this task can function.
|
||||
fn dependencies(&self) -> Vec<Box<Task>>;
|
||||
fn dependencies(&self) -> Vec<TaskDependency>;
|
||||
|
||||
/// Returns a short name used for formatting the dependency tree.
|
||||
fn name(&self) -> String;
|
||||
@ -53,7 +79,7 @@ pub trait Task {
|
||||
/// The dependency tree allows for smart iteration on a Task struct.
|
||||
pub struct DependencyTree {
|
||||
task: Box<Task>,
|
||||
dependencies: Vec<DependencyTree>,
|
||||
dependencies: Vec<(TaskOrdering, DependencyTree)>,
|
||||
}
|
||||
|
||||
impl DependencyTree {
|
||||
@ -64,8 +90,9 @@ impl DependencyTree {
|
||||
buf += "\n";
|
||||
|
||||
for i in 0..self.dependencies.len() {
|
||||
let dependencies = self.dependencies[i].render();
|
||||
let dependencies = dependencies.trim();
|
||||
let (order, dependency) = &(self.dependencies[i]);
|
||||
let dependencies = dependency.render();
|
||||
let dependencies = format!("{:?} {}", order, dependencies.trim());
|
||||
|
||||
if i + 1 == self.dependencies.len() {
|
||||
buf += "└── ";
|
||||
@ -92,7 +119,11 @@ impl DependencyTree {
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
for i in &mut self.dependencies {
|
||||
for (ordering, i) in &mut self.dependencies {
|
||||
if ordering != &TaskOrdering::Pre {
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = i.execute(context, &|msg: &str, progress: f64| {
|
||||
messenger(
|
||||
msg,
|
||||
@ -114,13 +145,46 @@ impl DependencyTree {
|
||||
}
|
||||
}
|
||||
|
||||
self.task
|
||||
let task_result = self
|
||||
.task
|
||||
.execute(inputs, context, &|msg: &str, progress: f64| {
|
||||
messenger(
|
||||
msg,
|
||||
progress / total_tasks + (1.0 / total_tasks) * f64::from(count),
|
||||
)
|
||||
})
|
||||
})?;
|
||||
|
||||
if let TaskParamType::Break = task_result {
|
||||
// We are done here
|
||||
return Ok(TaskParamType::Break);
|
||||
}
|
||||
|
||||
for (ordering, i) in &mut self.dependencies {
|
||||
if ordering != &TaskOrdering::Post {
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = i.execute(context, &|msg: &str, progress: f64| {
|
||||
messenger(
|
||||
msg,
|
||||
progress / total_tasks + (1.0 / total_tasks) * f64::from(count),
|
||||
)
|
||||
})?;
|
||||
|
||||
// Check to see if we skip matching other dependencies
|
||||
let do_break = match &result {
|
||||
TaskParamType::Break => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
count += 1;
|
||||
|
||||
if do_break {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(task_result)
|
||||
}
|
||||
|
||||
/// Builds a new pipeline from the specified task, iterating on dependencies.
|
||||
@ -128,7 +192,7 @@ impl DependencyTree {
|
||||
let dependencies = task
|
||||
.dependencies()
|
||||
.into_iter()
|
||||
.map(|x| DependencyTree::build(x))
|
||||
.map(|x| (x.ordering, DependencyTree::build(x.task)))
|
||||
.collect();
|
||||
|
||||
DependencyTree { task, dependencies }
|
||||
|
@ -5,6 +5,7 @@ use std::env::consts::OS;
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use config::PackageDescription;
|
||||
@ -89,7 +90,7 @@ impl Task for ResolvePackageTask {
|
||||
Ok(TaskParamType::File(latest_version, latest_file))
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
pub struct SaveDatabaseTask {}
|
||||
@ -22,7 +23,7 @@ impl Task for SaveDatabaseTask {
|
||||
Ok(TaskParamType::None)
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use std::fs::File;
|
||||
@ -71,7 +72,7 @@ impl Task for SaveExecutableTask {
|
||||
Ok(TaskParamType::None)
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,9 @@ use installer::InstallerFramework;
|
||||
use tasks::Task;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use tasks::save_database::SaveDatabaseTask;
|
||||
use tasks::uninstall_pkg::UninstallPackageTask;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskOrdering;
|
||||
|
||||
pub struct UninstallTask {
|
||||
pub items: Vec<String>,
|
||||
@ -23,18 +24,19 @@ impl Task for UninstallTask {
|
||||
Ok(TaskParamType::None)
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
let mut elements = Vec::<Box<Task>>::new();
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
let mut elements = Vec::new();
|
||||
|
||||
for item in &self.items {
|
||||
elements.push(Box::new(UninstallPackageTask {
|
||||
elements.push(TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(UninstallPackageTask {
|
||||
name: item.clone(),
|
||||
optional: false,
|
||||
}));
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
elements.push(Box::new(SaveDatabaseTask {}));
|
||||
|
||||
elements
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::save_database::SaveDatabaseTask;
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskOrdering;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use installer::LocalInstallation;
|
||||
@ -11,6 +14,7 @@ use std::fs::remove_dir;
|
||||
use std::fs::remove_file;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
use tasks::uninstall_shortcuts::UninstallShortcutsTask;
|
||||
|
||||
pub struct UninstallPackageTask {
|
||||
pub name: String,
|
||||
@ -24,7 +28,7 @@ impl Task for UninstallPackageTask {
|
||||
context: &mut InstallerFramework,
|
||||
messenger: &Fn(&str, f64),
|
||||
) -> Result<TaskParamType, String> {
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(input.len(), 1);
|
||||
|
||||
let path = context
|
||||
.install_path
|
||||
@ -83,8 +87,17 @@ impl Task for UninstallPackageTask {
|
||||
Ok(TaskParamType::None)
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<Box<Task>> {
|
||||
vec![]
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![
|
||||
TaskDependency::build(
|
||||
TaskOrdering::Pre,
|
||||
Box::new(UninstallShortcutsTask {
|
||||
name: self.name.clone(),
|
||||
optional: self.optional,
|
||||
}),
|
||||
),
|
||||
TaskDependency::build(TaskOrdering::Post, Box::new(SaveDatabaseTask {})),
|
||||
]
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
|
100
src/tasks/uninstall_shortcuts.rs
Normal file
100
src/tasks/uninstall_shortcuts.rs
Normal file
@ -0,0 +1,100 @@
|
||||
//! Uninstalls a specific package.
|
||||
|
||||
use installer::InstallerFramework;
|
||||
|
||||
use tasks::Task;
|
||||
use tasks::TaskDependency;
|
||||
use tasks::TaskParamType;
|
||||
|
||||
use installer::LocalInstallation;
|
||||
|
||||
use std::fs::remove_dir;
|
||||
use std::fs::remove_file;
|
||||
|
||||
use logging::LoggingErrors;
|
||||
|
||||
pub struct UninstallShortcutsTask {
|
||||
pub name: String,
|
||||
pub optional: bool,
|
||||
}
|
||||
|
||||
impl Task for UninstallShortcutsTask {
|
||||
fn execute(
|
||||
&mut self,
|
||||
input: Vec<TaskParamType>,
|
||||
context: &mut InstallerFramework,
|
||||
messenger: &Fn(&str, f64),
|
||||
) -> Result<TaskParamType, String> {
|
||||
assert_eq!(input.len(), 0);
|
||||
|
||||
let path = context
|
||||
.install_path
|
||||
.as_ref()
|
||||
.log_expect("No install path specified");
|
||||
|
||||
let mut metadata: Option<LocalInstallation> = None;
|
||||
for i in 0..context.database.len() {
|
||||
if self.name == context.database[i].name {
|
||||
metadata = Some(context.database[i].clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut package = match metadata {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
if self.optional {
|
||||
return Ok(TaskParamType::None);
|
||||
}
|
||||
|
||||
return Err(format!(
|
||||
"Package {:?} could not be found for uninstall.",
|
||||
self.name
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
messenger(
|
||||
&format!("Uninstalling shortcuts for package {:?}...", self.name),
|
||||
0.0,
|
||||
);
|
||||
|
||||
// Reverse, as to delete directories last
|
||||
package.files.reverse();
|
||||
|
||||
let max = package.files.len();
|
||||
for (i, file) in package.shortcuts.iter().enumerate() {
|
||||
let name = file.clone();
|
||||
let file = path.join(file);
|
||||
info!("Deleting {:?}", file);
|
||||
|
||||
messenger(
|
||||
&format!("Deleting {} ({} of {})", name, i + 1, max),
|
||||
(i as f64) / (max as f64),
|
||||
);
|
||||
|
||||
let result = if file.is_dir() {
|
||||
remove_dir(file)
|
||||
} else {
|
||||
remove_file(file)
|
||||
};
|
||||
|
||||
if let Err(v) = result {
|
||||
error!("Failed to delete file: {:?}", v);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TaskParamType::None)
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<TaskDependency> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
format!(
|
||||
"UninstallShortcutsTask (for {:?}, optional = {})",
|
||||
self.name, self.optional
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user