├── .aliases ├── .dockerignore ├── .gitignore ├── .rspec ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── README.md ├── config └── cli.yml ├── docker-compose.yml ├── packagers └── debian │ ├── Dockerfile │ ├── build.sh │ └── templates │ └── DEBIAN │ └── control ├── releases ├── 0.1.0 │ └── mac │ │ └── aliases.tar.gz ├── 0.1.1 │ └── mac │ │ └── aliases.tar.gz ├── 0.2.0 │ └── mac │ │ └── aliases.tar.gz ├── 0.3.0 │ └── mac │ │ └── aliases.tar.gz ├── 0.4.0 │ └── mac │ │ └── aliases.tar.gz ├── 0.5.0 │ └── mac │ │ └── aliases.tar.gz ├── 0.5.1 │ └── mac │ │ └── aliases.tar.gz ├── 0.6.0 │ └── mac │ │ └── aliases.tar.gz ├── 0.7.0 │ └── mac │ │ └── aliases.tar.gz ├── 0.7.1 │ └── mac │ │ └── aliases.tar.gz ├── 0.7.2 │ └── mac │ │ └── aliases.tar.gz ├── 0.7.3 │ └── mac │ │ └── aliases.tar.gz ├── 0.7.4 │ └── mac │ │ └── aliases.tar.gz ├── 0.7.5 │ └── mac │ │ └── aliases.tar.gz ├── 0.7.6 │ └── mac │ │ └── aliases.tar.gz ├── 0.7.7 │ └── mac │ │ └── aliases.tar.gz ├── 0.8.0-devel+001 │ └── debian │ │ └── aliases_0.8.0-devel+001.deb ├── 0.8.0-devel │ ├── debian │ │ └── aliases_0.8.0-devel.deb │ └── mac │ │ └── aliases.tar.gz ├── 0.8.0 │ ├── debian │ │ └── aliases_0.8.0.deb │ └── mac │ │ └── aliases.tar.gz └── 0.8.1 │ ├── debian │ └── aliases_0.8.1.deb │ └── mac │ └── aliases.tar.gz ├── scripts ├── build-release-debian.sh ├── build-release-osx.sh └── test-runner.sh ├── spec ├── Dockerfile ├── add_spec.rb ├── directories_spec.rb ├── dockerfiles │ └── initialized ├── exec_spec.rb ├── init_spec.rb ├── list_spec.rb ├── rehash_spec.rb ├── remove_spec.rb ├── spec_helper.rb ├── support │ ├── docker_command.rb │ ├── dockerfile_repository.rb │ └── logger.rb └── users_spec.rb ├── src ├── aliases │ ├── app.rs │ ├── builders │ │ ├── alias_builder.rs │ │ ├── command_builder.rs │ │ └── mod.rs │ ├── collections │ │ └── mod.rs │ ├── commands │ │ ├── add.rs │ │ ├── clone_repo.rs │ │ ├── directories.rs │ │ ├── disable_user.rs │ │ ├── enable_user.rs │ │ ├── exec │ │ │ └── mod.rs │ │ ├── init.rs │ │ ├── list.rs │ │ ├── mod.rs │ │ ├── move_user.rs │ │ ├── pull_repo.rs │ │ ├── rehash │ │ │ └── mod.rs │ │ ├── remove.rs │ │ ├── test.rs │ │ └── users.rs │ ├── config.rs │ ├── execution_workflow.rs │ ├── factories │ │ ├── alias_factory.rs │ │ ├── mod.rs │ │ └── shim_file_factory.rs │ ├── git.rs │ ├── mod.rs │ ├── models │ │ ├── alias.rs │ │ ├── alias_file.rs │ │ ├── conditional.rs │ │ ├── mod.rs │ │ ├── user.rs │ │ └── user_confirmation.rs │ ├── repositories │ │ ├── alias_file_repository.rs │ │ ├── alias_repository.rs │ │ ├── mod.rs │ │ └── user_repository.rs │ └── views │ │ ├── aliases.rs │ │ └── mod.rs ├── lib.rs └── main.rs └── tests ├── alias_builder.rs ├── alias_factory.rs ├── aliases_collection.rs ├── fixtures ├── aliases_files │ ├── invalid │ └── valid ├── dir_with_local_aliases │ └── .aliases ├── initialized_dir │ └── .aliases └── uninitialized_dir │ └── .aliases ├── lib.rs └── rehash_command.rs /.aliases: -------------------------------------------------------------------------------- 1 | # This file is autogenerated by the aliases tool. 2 | # For more info about aliases type `aliases --help` 3 | # or visit https://github.com/sebglazebrook/aliases 4 | 5 | --- 6 | "docker-machine-name": 7 | command: "docker-machine ls | tail -n 1 | awk '{ print $1 }'" 8 | release: 9 | command: "cargo clean && ./scripts/build-release-osx.sh $0 && ./scripts/build-release-debian.sh $0" 10 | "run-tests": 11 | command: "./scripts/test-runner.sh" 12 | tester: 13 | command: "arg=$(FZF) && $0 $arg && echo $0 $arg >> ~/.bash_history" 14 | workspace: 15 | command: "eval \"$(docker-machine env $(docker-machine-name))\" && docker-compose run rust" 16 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | tmp 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | tmp/ 3 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | --format documentation 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | script: 7 | - ./scripts/test-runner.sh 8 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "aho-corasick" 3 | version = "0.6.3" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "aliases" 11 | version = "0.8.1" 12 | dependencies = [ 13 | "clap 2.23.3 (registry+https://github.com/rust-lang/crates.io-index)", 14 | "countdown 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 15 | "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", 16 | "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "gag 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 19 | "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 20 | "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", 21 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 22 | "scoped-pool 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 23 | "stainless 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", 24 | "tabwriter 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 26 | ] 27 | 28 | [[package]] 29 | name = "ansi_term" 30 | version = "0.9.0" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | 33 | [[package]] 34 | name = "atty" 35 | version = "0.2.2" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | dependencies = [ 38 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 39 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 41 | ] 42 | 43 | [[package]] 44 | name = "bitflags" 45 | version = "0.8.2" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | 48 | [[package]] 49 | name = "clap" 50 | version = "2.23.3" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | dependencies = [ 53 | "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", 56 | "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 57 | "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 59 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 61 | "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 62 | ] 63 | 64 | [[package]] 65 | name = "countdown" 66 | version = "0.1.2" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | 69 | [[package]] 70 | name = "crossbeam" 71 | version = "0.2.10" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | 74 | [[package]] 75 | name = "env_logger" 76 | version = "0.4.2" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | dependencies = [ 79 | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 81 | ] 82 | 83 | [[package]] 84 | name = "gag" 85 | version = "0.1.9" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | dependencies = [ 88 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 89 | "tempfile 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 90 | ] 91 | 92 | [[package]] 93 | name = "gcc" 94 | version = "0.3.45" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | 97 | [[package]] 98 | name = "kernel32-sys" 99 | version = "0.2.2" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | dependencies = [ 102 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 104 | ] 105 | 106 | [[package]] 107 | name = "libc" 108 | version = "0.2.21" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | 111 | [[package]] 112 | name = "log" 113 | version = "0.3.7" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | 116 | [[package]] 117 | name = "memchr" 118 | version = "1.0.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | dependencies = [ 121 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 122 | ] 123 | 124 | [[package]] 125 | name = "rand" 126 | version = "0.3.15" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | dependencies = [ 129 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 130 | ] 131 | 132 | [[package]] 133 | name = "redox_syscall" 134 | version = "0.1.17" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | 137 | [[package]] 138 | name = "regex" 139 | version = "0.2.1" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | dependencies = [ 142 | "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", 143 | "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 144 | "regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 146 | "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 147 | ] 148 | 149 | [[package]] 150 | name = "regex-syntax" 151 | version = "0.4.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | 154 | [[package]] 155 | name = "rust-crypto" 156 | version = "0.2.36" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | dependencies = [ 159 | "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", 160 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 161 | "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 164 | ] 165 | 166 | [[package]] 167 | name = "rustc-serialize" 168 | version = "0.3.24" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | 171 | [[package]] 172 | name = "rustc_version" 173 | version = "0.1.7" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | dependencies = [ 176 | "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", 177 | ] 178 | 179 | [[package]] 180 | name = "scoped-pool" 181 | version = "1.0.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | dependencies = [ 184 | "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", 185 | "scopeguard 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 186 | "variance 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 187 | ] 188 | 189 | [[package]] 190 | name = "scopeguard" 191 | version = "0.1.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | 194 | [[package]] 195 | name = "semver" 196 | version = "0.1.20" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | 199 | [[package]] 200 | name = "stainless" 201 | version = "0.1.11" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | 204 | [[package]] 205 | name = "strsim" 206 | version = "0.6.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | 209 | [[package]] 210 | name = "tabwriter" 211 | version = "0.1.25" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | dependencies = [ 214 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 215 | ] 216 | 217 | [[package]] 218 | name = "tempfile" 219 | version = "2.1.5" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | dependencies = [ 222 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 223 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 224 | "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 225 | "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 226 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 227 | ] 228 | 229 | [[package]] 230 | name = "term_size" 231 | version = "0.3.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | dependencies = [ 234 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 235 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 236 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 237 | ] 238 | 239 | [[package]] 240 | name = "thread-id" 241 | version = "3.0.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | dependencies = [ 244 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 245 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 246 | ] 247 | 248 | [[package]] 249 | name = "thread_local" 250 | version = "0.3.3" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | dependencies = [ 253 | "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 254 | "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 255 | ] 256 | 257 | [[package]] 258 | name = "time" 259 | version = "0.1.37" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | dependencies = [ 262 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 263 | "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 264 | "redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", 265 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 266 | ] 267 | 268 | [[package]] 269 | name = "unicode-segmentation" 270 | version = "1.1.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | 273 | [[package]] 274 | name = "unicode-width" 275 | version = "0.1.4" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | 278 | [[package]] 279 | name = "unreachable" 280 | version = "0.1.1" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | dependencies = [ 283 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 284 | ] 285 | 286 | [[package]] 287 | name = "utf8-ranges" 288 | version = "1.0.0" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | 291 | [[package]] 292 | name = "variance" 293 | version = "0.1.3" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | 296 | [[package]] 297 | name = "vec_map" 298 | version = "0.7.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | 301 | [[package]] 302 | name = "void" 303 | version = "1.0.2" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | 306 | [[package]] 307 | name = "winapi" 308 | version = "0.2.8" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | 311 | [[package]] 312 | name = "winapi-build" 313 | version = "0.1.1" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | 316 | [[package]] 317 | name = "yaml-rust" 318 | version = "0.3.5" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | 321 | [metadata] 322 | "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" 323 | "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" 324 | "checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" 325 | "checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" 326 | "checksum clap 2.23.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f57e9b63057a545ad2ecd773ea61e49422ed1b1d63d74d5da5ecaee55b3396cd" 327 | "checksum countdown 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ed00fdaf891f9f1c63ac1c5a388b5bb4220547dfbf97377acc64c22c84c1a44b" 328 | "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" 329 | "checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83" 330 | "checksum gag 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c606f5c0da18075916377e73de8aec7c140e1b6110a3cf26ae1f47873529b47e" 331 | "checksum gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "40899336fb50db0c78710f53e87afc54d8c7266fb76262fecc78ca1a7f09deae" 332 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 333 | "checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135" 334 | "checksum log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5141eca02775a762cc6cd564d8d2c50f67c0ea3a372cbf1c51592b3e029e10ad" 335 | "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" 336 | "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" 337 | "checksum redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "29dbdfd4b9df8ab31dec47c6087b7b13cbf4a776f335e4de8efba8288dda075b" 338 | "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" 339 | "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" 340 | "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" 341 | "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 342 | "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" 343 | "checksum scoped-pool 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "817a3a15e704545ce59ed2b5c60a5d32bda4d7869befb8b36667b658a6c00b43" 344 | "checksum scopeguard 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59a076157c1e2dc561d8de585151ee6965d910dd4dcb5dabb7ae3e83981a6c57" 345 | "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" 346 | "checksum stainless 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "68dc99ce35b968242ca77766006b1005661465568181d4919e06bbcd8852b43e" 347 | "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" 348 | "checksum tabwriter 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "85dbf563da2891d55ef4b00ef08c5b5f160143f67691ff1f97ad89e77824ed3b" 349 | "checksum tempfile 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3213fd2b7ed87e39306737ccfac04b1233b57a33ca64cfbf52f2ffaa2b765e2f" 350 | "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" 351 | "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" 352 | "checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" 353 | "checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3" 354 | "checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3" 355 | "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" 356 | "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" 357 | "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" 358 | "checksum variance 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3abfc2be1fb59663871379ea884fd81de80c496f2274e021c01d6fe56cd77b05" 359 | "checksum vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8cdc8b93bd0198ed872357fb2e667f7125646b1762f16d60b2c96350d361897" 360 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 361 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 362 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 363 | "checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" 364 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aliases" 3 | version = "0.8.1" 4 | authors = ["Seb Glazebrook "] 5 | 6 | [lib] 7 | name = "aliases" 8 | path = "src/lib.rs" 9 | 10 | [[bin]] 11 | name = "aliases" 12 | path = "src/main.rs" 13 | 14 | [dependencies] 15 | yaml-rust = "0.3.0" 16 | regex = "*" 17 | rust-crypto = "0.2.34" 18 | rustc-serialize = "0.3" 19 | tabwriter = "0.1" 20 | countdown = "*" 21 | crossbeam = "*" 22 | log = "*" 23 | env_logger = "*" 24 | scoped-pool = "*" 25 | 26 | [dependencies.clap] 27 | features = ["yaml"] 28 | version = "2.16.4" 29 | 30 | [dev-dependencies] 31 | gag = "*" 32 | stainless = "*" 33 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM sebglazebrook/rust-nightly:latest 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source "https://rubygems.org" 3 | 4 | gem "rspec" 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | diff-lcs (1.2.5) 5 | rspec (3.5.0) 6 | rspec-core (~> 3.5.0) 7 | rspec-expectations (~> 3.5.0) 8 | rspec-mocks (~> 3.5.0) 9 | rspec-core (3.5.4) 10 | rspec-support (~> 3.5.0) 11 | rspec-expectations (3.5.0) 12 | diff-lcs (>= 1.2.0, < 2.0) 13 | rspec-support (~> 3.5.0) 14 | rspec-mocks (3.5.0) 15 | diff-lcs (>= 1.2.0, < 2.0) 16 | rspec-support (~> 3.5.0) 17 | rspec-support (3.5.0) 18 | 19 | PLATFORMS 20 | ruby 21 | 22 | DEPENDENCIES 23 | rspec 24 | 25 | BUNDLED WITH 26 | 1.13.7 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Dynamic aliases based on the directory you are currently in. 4 | 5 | Ever wanted to type something like `server` in whole bunch of different directories and your computer just knows what you're thinking? 6 | 7 | Now you can! 8 | 9 | ## Why you want this 10 | 11 | Already know why you want this? Jump to [Installation](#installation) section. 12 | 13 | Bash aliases are cool but limited, they are globals and have limited configurability. 14 | 15 | One downside of standard bash aliases is that they don't take arguments, to counter this many people (myself included) do things like create bash functions like this: 16 | 17 | ``` 18 | function ll { 19 | ls -la "$@" 20 | } 21 | ``` 22 | 23 | The downside to above function is that it's hard to tell where these functions are coming from, you can't just type `which ll` and find the function. 24 | 25 | You also end up writing a whole lot of functions that are really similar, plus none of these are dynamic or contextual 26 | 27 | So I created `aliases` to make my life easier and more fun. 28 | 29 | ## Usage 30 | 31 | To create aliases for the current directory run: 32 | 33 | ``` 34 | aliases init 35 | ``` 36 | 37 | This creates a `.aliases` file in the current directory with a commented out alias structure that looks like below: 38 | 39 | ```yaml 40 | # alias_name: 41 | # command: ./super_command.sh # required 42 | # confirm: true # optional - You will be asked to confirm before execution 43 | # confirmation_message: Are you sure you are sure?? # optional - If confirm is set to true then you this is your confirmation message 44 | # conditional: /bin/true # optional - A bash command that needs to be successful for the alias to run 45 | # backout_seconds: 3 # optional - Give's you a backout option (ctrl + c) before the alias is executed 46 | # unit_test: '[ true = true ]' # optional - A bash command that tells whether the alias is doing what you want 47 | # quiet: false # optional - default 'false', when set to false evaluated command is printed to stderr before running 48 | ``` 49 | 50 | Edit the file and then run `aliases rehash` to make the alias available. 51 | 52 | Here's an example of some of the aliases: 53 | 54 | ```yaml 55 | l: 56 | command: ls 57 | gc: 58 | command: git commit 59 | deploy_production: 60 | command: bundle exec cap production deploy 61 | backout_seconds: 3 62 | conditional: test git rev-parse --abbrev-ref HEAD` == "master" 63 | deploy_staging: 64 | command: bundle exec cap staging deploy 65 | ``` 66 | 67 | To list all aliases available type: 68 | 69 | ``` 70 | aliases 71 | ``` 72 | 73 | The `.aliases` file should be checked in to your repo, spread the alias love with the people you are working with. 74 | 75 | ### Global Aliases 76 | 77 | Global aliases are created by running `aliases init` in your home directory. 78 | 79 | These aliases are overridden by local aliases if they exist. 80 | 81 | 82 | ## Installation 83 | 84 | ### macOS 85 | 86 | ``` 87 | brew install sebglazebrook/aliases/aliases 88 | ``` 89 | 90 | ### Linux 91 | 92 | There are some debian packages in the released directory so you can clone the repo and do something like this: 93 | 94 | ``` 95 | apt install ./releases/0.8.1/debian/aliases_0.8.1.deb 96 | ``` 97 | 98 | You'll need to `sudo` that though of course. 99 | 100 | Next you'll need to add the aliases initialization config to you .bashrc or any file that's loaded when your user logs in. 101 | 102 | ``` 103 | aliases init --global >> ~/.bashrc 104 | ``` 105 | 106 | ### Compile from source 107 | 108 | TODO 109 | 110 | ## Features in development 111 | 112 | To test these either compile from source or `brew install aliases --devel` 113 | 114 | ### Positional arguments 115 | 116 | Example: 117 | 118 | ```yaml 119 | vim-replace: 120 | command: ag -l "$0" && vim -c "bufdo %s/$0/$1/gc" $(ag -l "$0") 121 | enable_positional_arguments: true 122 | `````` 123 | 124 | The above alias allows you to do the following: 125 | 126 | ``` 127 | vim-replace old_text new_text 128 | ``` 129 | 130 | This replaces `$0`, `$1` etc with the arguments you send to your alias. 131 | 132 | This currently assumes that your position keys are continuous. For example if you have `$0`, `$1`, `$5` then it will not work. 133 | 134 | ### User Aliases 135 | 136 | Maybe you want to import a friends aliases or use aliases relevant to your work, aliases makes that easy: 137 | 138 | ``` 139 | # create a new aliases file for a user 140 | 141 | aliases init --user superman 142 | ``` 143 | 144 | This will create a new empty `.aliases-superman` file in the current directory and add the user `superman` to the list of alias users. 145 | 146 | If your repo already has a `.aliases-superman` the file will be left untouched and the user `superman` will be added to your list of alias users. 147 | 148 | The next time you run `aliases rehash`, all `superman` aliases in all initialized directories will be updated. 149 | 150 | User aliases are merged together to create a list of available aliases. 151 | 152 | If there are clashing aliases the alias user prioritized highest wins. 153 | 154 | You can see all users and their prioritization using: 155 | 156 | ``` 157 | aliases users 158 | ``` 159 | 160 | To change the prioritization order of the user, you currently need to edit the user list in the config file. 161 | 162 | ``` 163 | cat ~/.aliases_cfg 164 | ``` 165 | 166 | And to temporally bump a user to the top with env var, like when you are pairing and sharing your shell: 167 | ``` 168 | export ALIASES_USER=superman 169 | ``` 170 | 171 | You can also pull and sync user aliases with github as: 172 | ``` 173 | aliases clone sebglazebrook # this could pull from github.com/sebglazebrook/.aliases repo 174 | aliases clone sebglazebrook https://some-other-address # pulls from the given repo 175 | aliases pull sebglazberook # pull and show the diff 176 | ``` 177 | 178 | ## Contributing 179 | 180 | Do the normal things, fork the code and make a PR. 181 | 182 | We use docker containers so we can share the same development environment. Some aliases are in this repository to help you to get up to speed. Here the list: 183 | - ``release`` - generates a new release given the version-number 184 | - ``run-tests`` - run all the tests 185 | - ``workspace`` - connect to docker environment (uses the alias ``docker-machine-name`` to get it) 186 | 187 | ## Bugs to fix 188 | 189 | - Handle different process signals 190 | - Check user's config is out of whack, like they are missing a key, it blows up 191 | - Being able to actually run the unit tests :-) 192 | - When listing, aliases alert the user if the dir hasn't been initialized 193 | 194 | ## Small improvements to come 195 | 196 | - Handle when numbers args are not in order 197 | - Add description of alias that can be seen in list view 198 | - Sort aliases lists better and make it more obvious which ones are local 199 | - Colors for user interactions 200 | - Use a thread pool when running rehash command to avoid too many threads 201 | - In list view add more data about the aliases, user etc 202 | - Allow multi-line command that display is list view well 203 | 204 | ## Possible future features 205 | 206 | - Clean uninstall, removing shims etc 207 | - Allow user to set a default shell or override the default shell. Currently all aliases are hardcoded to run inside a bash shell, could be sh or zsh 208 | -------------------------------------------------------------------------------- /config/cli.yml: -------------------------------------------------------------------------------- 1 | name: aliases 2 | version: 0.8.1 3 | about: dynamic alias functions for bash 4 | author: Seb Glazebrook 5 | 6 | settings: 7 | - Hidden 8 | 9 | subcommands: 10 | - init: 11 | about: initialize a directory for aliases 12 | args: 13 | - global: 14 | help: returns the global initialization for the app 15 | long: global 16 | short: g 17 | takes_value: false 18 | - user: 19 | help: initialize aliases for a specific user 20 | long: user 21 | short: u 22 | takes_value: true 23 | - list: 24 | about: list the aliases available 25 | args: 26 | - local: 27 | help: list only local aliases 28 | long: local 29 | short: l 30 | takes_value: false 31 | conflicts_with: 32 | - global 33 | - directory 34 | - global: 35 | help: list only global aliases 36 | long: global 37 | short: g 38 | takes_value: false 39 | conflicts_with: 40 | - local 41 | - directory 42 | - directory: 43 | help: list aliases for a specific directory 44 | long: directory 45 | short: d 46 | takes_value: true 47 | conflicts_with: 48 | - local 49 | - global 50 | - name: 51 | help: list aliases for with a specific name 52 | long: name 53 | short: n 54 | takes_value: true 55 | - users: 56 | about: list the users 57 | args: [] 58 | subcommands: 59 | - move: 60 | about: move a user up or down the prioritization list 61 | args: 62 | - username: 63 | help: user name you want to move 64 | required: true 65 | - prioritization: 66 | help: the priotity order you want for the user 67 | required: true 68 | - use: 69 | about: Assign a user to the top of the priority list 70 | args: 71 | - username: 72 | help: user name you want to move 73 | required: true 74 | - enable: 75 | about: "Enable a user's aliases" 76 | args: 77 | - username: 78 | help: user name you want to disable 79 | required: true 80 | - disable: 81 | about: "Disable a user's aliases" 82 | args: 83 | - username: 84 | help: user name you want to disable 85 | required: true 86 | - exec: 87 | about: execute an alias for a given directory 88 | args: 89 | - directory: 90 | help: directory where the alias is defined 91 | required: true 92 | - name: 93 | help: name of alias 94 | required: true 95 | multiple: true 96 | - add: 97 | about: add an alias via the cli 98 | args: 99 | - name: 100 | help: the name of the alias 101 | required: true 102 | - command: 103 | help: the command you want to run 104 | required: true 105 | - remove: 106 | about: remove an alias via the cli 107 | args: 108 | - name: 109 | help: the name of the alias 110 | required: true 111 | - directories: 112 | about: list all directories initialized with aliases 113 | - rehash: 114 | about: update the aliases 115 | args: [] 116 | - clone: 117 | about: clone external aliases 118 | args: 119 | - username: 120 | help: the username of the aliases you want to clone 121 | required: true 122 | - repo_url: 123 | help: the git repo url of the aliases (defaults to github//dot-aliases) 124 | required: false 125 | - enable: 126 | help: whether to enable the user if they are not currently enabled 127 | required: false 128 | long: enable 129 | short: E 130 | - pull: 131 | about: "pull a cloned user's aliases" 132 | args: 133 | - username: 134 | help: the username of the aliases you want to pull, leave blank to pull all user aliases 135 | required: false 136 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | data: 2 | image: sebglazebrook/rust-nightly:latest 3 | volumes: 4 | - /root/.cargo/ 5 | - /root/.bash_histoy 6 | command: ["true"] 7 | rust: 8 | build: . 9 | volumes: 10 | - .:/code 11 | volumes_from: 12 | - data 13 | command: ["bash"] 14 | -------------------------------------------------------------------------------- /packagers/debian/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | RUN apt-get update && apt-get install --yes gcc git curl file 4 | 5 | RUN curl -sSf https://static.rust-lang.org/rustup.sh | sh -s -- -y --channel=nightly --disable-sudo 6 | 7 | COPY . /code 8 | 9 | WORKDIR /code 10 | 11 | ENV CODE_DIR=/code 12 | 13 | ENV WORK_DIR=/tmp 14 | 15 | ENV PKG_NAME=aliases 16 | 17 | CMD ./packagers/debian/build.sh 18 | -------------------------------------------------------------------------------- /packagers/debian/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | echo "Building package for version number ${VERSION_NUMBER}" 4 | 5 | PKG_DIR="${WORK_DIR}/${PKG_NAME}_${VERSION_NUMBER}" 6 | 7 | cd ${WORK_DIR} 8 | 9 | mkdir ${PKG_DIR} && cd ${PKG_DIR} \ 10 | && mkdir -p usr/local/bin \ 11 | && mkdir -p DEBIAN \ 12 | && cat ${CODE_DIR}/packagers/debian/templates/DEBIAN/control | sed "s/<>/${VERSION_NUMBER}/" > DEBIAN/control 13 | 14 | cd ${CODE_DIR} && cargo build --release 15 | 16 | cp ${CODE_DIR}/target/release/aliases ${PKG_DIR}/usr/local/bin/aliases 17 | 18 | cd ${WORK_DIR} && dpkg-deb --build ${PKG_NAME}_${VERSION_NUMBER} 19 | 20 | ls ${PKG_NAME}_${VERSION_NUMBER}.deb 21 | -------------------------------------------------------------------------------- /packagers/debian/templates/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: aliases 2 | Version: <> 3 | Section: base 4 | Priority: optional 5 | Architecture: amd64 6 | Maintainer: Seb Glazebrook 7 | Description: Aliases 8 | This package contains a shell tool for managing shell aliases more conveniently. 9 | It installs aliases binary to be access from shell. 10 | For further documentation see 11 | -------------------------------------------------------------------------------- /releases/0.1.0/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.1.0/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.1.1/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.1.1/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.2.0/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.2.0/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.3.0/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.3.0/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.4.0/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.4.0/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.5.0/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.5.0/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.5.1/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.5.1/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.6.0/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.6.0/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.7.0/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.7.0/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.7.1/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.7.1/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.7.2/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.7.2/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.7.3/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.7.3/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.7.4/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.7.4/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.7.5/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.7.5/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.7.6/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.7.6/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.7.7/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.7.7/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.8.0-devel+001/debian/aliases_0.8.0-devel+001.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.8.0-devel+001/debian/aliases_0.8.0-devel+001.deb -------------------------------------------------------------------------------- /releases/0.8.0-devel/debian/aliases_0.8.0-devel.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.8.0-devel/debian/aliases_0.8.0-devel.deb -------------------------------------------------------------------------------- /releases/0.8.0-devel/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.8.0-devel/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.8.0/debian/aliases_0.8.0.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.8.0/debian/aliases_0.8.0.deb -------------------------------------------------------------------------------- /releases/0.8.0/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.8.0/mac/aliases.tar.gz -------------------------------------------------------------------------------- /releases/0.8.1/debian/aliases_0.8.1.deb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.8.1/debian/aliases_0.8.1.deb -------------------------------------------------------------------------------- /releases/0.8.1/mac/aliases.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/releases/0.8.1/mac/aliases.tar.gz -------------------------------------------------------------------------------- /scripts/build-release-debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | VERSION_NUMBER=${1} 4 | 5 | docker build -t alias-debian-pkg-builder -f packagers/debian/Dockerfile . 6 | 7 | CONTAINER_ID=$(docker create -e VERSION_NUMBER="$VERSION_NUMBER" alias-debian-pkg-builder) 8 | 9 | docker start -ai ${CONTAINER_ID} 10 | 11 | docker cp ${CONTAINER_ID}:/tmp/aliases_${VERSION_NUMBER}.deb . 12 | 13 | mkdir -p releases/$VERSION_NUMBER/debian && mv aliases_${VERSION_NUMBER}.deb releases/$VERSION_NUMBER/debian/ 14 | -------------------------------------------------------------------------------- /scripts/build-release-osx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -u 4 | 5 | targz() { 6 | local tmpFile="${@%/}.tar" 7 | tar -cvf "${tmpFile}" --exclude=".DS_Store" "${@}" || return 1 8 | size=$( 9 | stat -f"%z" "${tmpFile}" 2> /dev/null; # OS X `stat` 10 | stat -c"%s" "${tmpFile}" 2> /dev/null # GNU `stat` 11 | ) 12 | local cmd="" 13 | if (( size < 52428800 )) && hash zopfli 2> /dev/null; then 14 | # the .tar file is smaller than 50 MB and Zopfli is available; use it 15 | cmd="zopfli" 16 | else 17 | if hash pigz 2> /dev/null; then 18 | cmd="pigz" 19 | else 20 | cmd="gzip" 21 | fi 22 | fi 23 | echo "Compressing .tar using \`${cmd}\`…" 24 | "${cmd}" -v "${tmpFile}" || return 1 25 | [ -f "${tmpFile}" ] && rm "${tmpFile}" 26 | echo "${tmpFile}.gz created successfully." 27 | } 28 | 29 | VERSION_NUMBER="$1" 30 | 31 | if [ `basename "${PWD}" != "aliases"` ]; then 32 | echo "Looks like you're not in the app root, can't continue." 33 | exit 1 34 | fi 35 | 36 | # update the version number here? 37 | 38 | cargo build --release 39 | 40 | cd target/release 41 | 42 | targz aliases 43 | 44 | cd ../.. 45 | 46 | mkdir -p releases/$VERSION_NUMBER/mac && mv target/release/aliases.tar.gz releases/$VERSION_NUMBER/mac/ 47 | -------------------------------------------------------------------------------- /scripts/test-runner.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" 5 | 6 | # make sure image is built 7 | cd $dir && docker build -t alias-test-runner -f spec/Dockerfile . 8 | 9 | docker-compose run rust bash -c 'cd /code && cargo build' 10 | 11 | container_command="${1:-bundle exec rspec spec}" 12 | docker run -ti -e "APP_ROOT=${dir}" -v ${dir}:/code -v /var/run/docker.sock:/var/run/docker.sock alias-test-runner ${container_command} 13 | -------------------------------------------------------------------------------- /spec/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.3 2 | 3 | RUN apt-get update && apt-get install --yes apt-transport-https ca-certificates gnupg2 4 | 5 | RUN apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D 6 | 7 | RUN echo "deb https://apt.dockerproject.org/repo debian-jessie main" > /etc/apt/sources.list.d/docker.list 8 | 9 | RUN apt-get update && apt-get install --yes docker-engine 10 | 11 | RUN gem install bundler 12 | 13 | WORKDIR /code 14 | 15 | COPY Gemfile* /code/ 16 | 17 | RUN bundle install 18 | 19 | COPY * /code/ 20 | -------------------------------------------------------------------------------- /spec/add_spec.rb: -------------------------------------------------------------------------------- 1 | describe "`add` command" do 2 | 3 | let(:docker_command) { DockerCommand.new(command, args, dockerfile) } 4 | subject { docker_command.invoke } 5 | 6 | after { docker_command.kill } 7 | 8 | context "on an initialized file system" do 9 | 10 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 11 | 12 | context "without optional args" do 13 | 14 | let(:args) { [] } 15 | 16 | let(:command ) { "bash -c 'cd /tmp && /code/target/debug/aliases add foo \"hello world\"'" } 17 | 18 | it "creates the alias in the current directory" do 19 | subject 20 | expect(docker_command.query("bash -c 'cd /tmp && /code/target/debug/aliases list --local'")).to match("foo") 21 | end 22 | 23 | context "when the current directory is not initialized" do 24 | 25 | let(:command ) { "bash -c 'cd /tmp && mkdir new-uninitialized-dir && cd /tmp/new-uninitialized-dir && /code/target/debug/aliases add foo bar'" } 26 | 27 | it "initializes the directory" do 28 | subject 29 | expect(docker_command.query("bash -c 'aliases directories'")).to match("/tmp/new-uninitialized-dir") 30 | end 31 | end 32 | 33 | it "makes the alias available for use" do 34 | subject 35 | expect(docker_command.query("bash -c 'source ~/.bashrc && cd /tmp && foo'")).to match(/hello world/) #TODO shouldn't have to source the rc file here 36 | end 37 | end 38 | 39 | context "with optional args" do 40 | 41 | describe "--global" do 42 | end 43 | 44 | describe "--directory" do 45 | end 46 | 47 | describe "--user" do 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/directories_spec.rb: -------------------------------------------------------------------------------- 1 | describe "`directories` command" do 2 | 3 | let(:docker_command) { DockerCommand.new(command, args, dockerfile) } 4 | subject { docker_command.invoke } 5 | 6 | after { docker_command.kill } 7 | 8 | context "on an initialized file system" do 9 | 10 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 11 | 12 | let(:args) { [] } 13 | 14 | context "when there are initialized directories" do 15 | let(:command ) { "bash -c 'cd /tmp && /code/target/debug/aliases init'" } 16 | 17 | it "lists the directory" do 18 | subject 19 | expect(docker_command.query("bash -c '/code/target/debug/aliases directories'").include?("/tmp")).to be true 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/dockerfiles/initialized: -------------------------------------------------------------------------------- 1 | FROM ruby:2.3 2 | 3 | RUN apt-get update && apt-get install --yes apt-transport-https ca-certificates gnupg2 4 | 5 | RUN apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D 6 | 7 | RUN echo "deb https://apt.dockerproject.org/repo debian-jessie main" > /etc/apt/sources.list.d/docker.list 8 | 9 | RUN apt-get update && apt-get install --yes docker-engine 10 | 11 | RUN gem install bundler 12 | 13 | WORKDIR /code 14 | 15 | COPY Gemfile* /code/ 16 | 17 | RUN bundle install 18 | 19 | COPY * /code/ 20 | 21 | COPY target/debug/aliases /usr/local/bin/ 22 | 23 | RUN aliases init 24 | 25 | RUN echo "source <(aliases init --global)" >> ~/.bashrc 26 | -------------------------------------------------------------------------------- /spec/exec_spec.rb: -------------------------------------------------------------------------------- 1 | describe "`exec` command" do 2 | 3 | let(:docker_command) { DockerCommand.new(command, args, dockerfile) } 4 | subject { docker_command.invoke } 5 | 6 | after { docker_command.kill } 7 | let(:args) { [] } 8 | 9 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 10 | 11 | let(:command) { "bash -c 'cd #{directory} && aliases exec #{directory} #{alias_name}'" } 12 | 13 | context "when given a directory" do 14 | 15 | let(:directory) { "/tmp/target-dir" } 16 | 17 | context "and an alias name" do 18 | 19 | let(:alias_name) { "awesome" } 20 | 21 | context "and an alias exists" do 22 | 23 | before do 24 | docker_command.start_container 25 | docker_command.query("bash -c 'cd /tmp && mkdir -p target-dir && cd target-dir && aliases add awesome pwd'") 26 | end 27 | 28 | describe "default behavior" do 29 | 30 | it "prints out the command that will be executed" do 31 | expect(subject.output).to match(/Executing: pwd/) 32 | end 33 | 34 | it "executes the command" do 35 | expect(subject.output).to match(/\/tmp\/target-dir/) 36 | end 37 | end 38 | 39 | context "when there are forwarding arguments" do 40 | 41 | let(:alias_name) { "great" } 42 | 43 | let(:command) { "bash -c 'cd #{directory} && aliases exec #{directory} #{alias_name} -- -la'" } 44 | 45 | before do 46 | docker_command.start_container 47 | docker_command.query("bash -c 'cd /tmp && mkdir -p target-dir && cd target-dir && aliases add #{alias_name} ls'") 48 | end 49 | 50 | it "prints out the command and the arguments that will be executed" do 51 | expect(subject.output).to match(/Executing: ls -la/) 52 | end 53 | 54 | it "executes the command" do 55 | expect(subject.output).to match(/\.aliases/) 56 | end 57 | end 58 | end 59 | 60 | context "and an alias does NOT exist" do 61 | 62 | it "does something" 63 | end 64 | end 65 | end 66 | 67 | context "when given a symlinked directory" do 68 | let(:directory) { "/tmp/linked" } 69 | 70 | context "and an alias name" do 71 | 72 | let(:alias_name) { "awesome" } 73 | 74 | context "and an alias exists" do 75 | 76 | before do 77 | docker_command.start_container 78 | docker_command.query("bash -c 'cd /tmp && mkdir -p original && ln -s original linked && cd linked && aliases add awesome pwd'") 79 | end 80 | 81 | describe "default behavior" do 82 | 83 | it "prints out the command that will be executed" do 84 | expect(subject.output).to match(/Executing: pwd/) 85 | end 86 | 87 | #it "executes the command" do 88 | #expect(subject.output).to match(/\/tmp\/linked/) 89 | #end 90 | end 91 | end 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /spec/init_spec.rb: -------------------------------------------------------------------------------- 1 | describe "`init` command" do 2 | 3 | let(:dockerfile) { DockerfileRepository.find(:empty) } 4 | let(:docker_command) { DockerCommand.new(command, args, dockerfile) } 5 | subject { docker_command.invoke } 6 | 7 | before { docker_command.kill } 8 | 9 | context "without any args" do 10 | 11 | let(:args) { [] } 12 | 13 | context "given the directory is uninitialized" do 14 | 15 | let(:command) { "bash -c 'cd /tmp && /code/target/debug/aliases init'" } 16 | 17 | it "creates an aliases file" do 18 | expect(subject.diff.include?("A /tmp/.aliases")).to be true 19 | end 20 | end 21 | 22 | context "given the directory is initialized" do 23 | 24 | let(:command) { "./target/debug/aliases init" } 25 | 26 | it "leaves the file system untouched" do 27 | expect(subject.diff.include?("A /code/.aliases")).to be false 28 | end 29 | end 30 | 31 | context "given the file system isn't initialized" do 32 | 33 | let(:command) { "./target/debug/aliases init" } 34 | 35 | it "creates a aliases config file" do 36 | expect(subject.diff.include?("A /root/.aliases_cfg")).to be true 37 | end 38 | end 39 | 40 | context "given the file system IS already initialized" do 41 | 42 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 43 | 44 | let(:command) { "./target/debug/aliases init" } 45 | 46 | it "leaves the file system untouched" do 47 | expect(subject.diff.include?("A /root/.aliases_cfg")).to be false 48 | end 49 | end 50 | 51 | context "given the directory is a symlink" do 52 | 53 | before do 54 | docker_command.start_container 55 | docker_command.query("bash -c 'mkdir -p /tmp/original'") 56 | docker_command.query("bash -c 'cd /tmp/ && ln -s original linked'") 57 | end 58 | 59 | let(:command ) { "bash -c 'cd /tmp/original && /code/target/debug/aliases init'" } 60 | 61 | it "adds the sourced dir not the linked dir" do 62 | subject 63 | expect(docker_command.query('/code/target/debug/aliases directories')).to match("/tmp/original") 64 | expect(docker_command.query('/code/target/debug/aliases directories')).to_not match("/tmp/linked") 65 | end 66 | end 67 | end 68 | 69 | describe "--global" do 70 | 71 | let(:args) { ["--global"] } 72 | let(:command) { "bash -c 'cd /tmp && /code/target/debug/aliases init --global'" } 73 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 74 | 75 | it "outputs the global system config users need to run to enable aliases" do 76 | expect(subject.output).to eq "export PATH=\"${HOME}/.aliases.d/shims:${PATH}\"\r\naliases rehash\r\n" 77 | end 78 | 79 | it "doesn't change the filesystem" do 80 | expect(subject.diff.empty?).to be true 81 | end 82 | end 83 | 84 | describe "--user" do 85 | 86 | context "when given a username" do 87 | 88 | let(:args) { ["--user", "superman"] } 89 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 90 | let(:command) { "bash -c 'cd /tmp && /code/target/debug/aliases init --user superman'" } 91 | 92 | it "creates an alias file for that user" do 93 | expect(subject.diff.include?("A /tmp/.aliases-superman")).to eq true 94 | end 95 | end 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /spec/list_spec.rb: -------------------------------------------------------------------------------- 1 | describe "`list` command" do 2 | 3 | context "when aliases has been initialized" do 4 | 5 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 6 | let(:docker_command) { DockerCommand.new(command, args, dockerfile) } 7 | let(:args) { [] } 8 | subject { docker_command.invoke } 9 | 10 | after { docker_command.kill } 11 | 12 | context "and there are aliases in the home and current directory" do 13 | 14 | before do 15 | docker_command.start_container 16 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases add c cat'") 17 | docker_command.query("bash -c 'cd /tmp && /code/target/debug/aliases add l ls'") 18 | end 19 | 20 | context "with NO args" do 21 | 22 | let(:command) { "bash -c 'cd /tmp && aliases list'" } 23 | 24 | it "lists aliases in the home directory" do 25 | expect(docker_command.query("bash -c 'cd /tmp && /code/target/debug/aliases list'")).to match(/c\s+cat/) 26 | end 27 | 28 | it "lists aliases in the local directory" do 29 | expect(docker_command.query("bash -c 'cd /tmp && /code/target/debug/aliases list'")).to match(/l\s+ls/) 30 | end 31 | 32 | context "when there are matching aliases in both directories" do 33 | 34 | before do 35 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases add foo home-bar'") 36 | docker_command.query("bash -c 'cd /tmp && /code/target/debug/aliases add foo local-bar'") 37 | end 38 | 39 | it "lists the one in the local directory" do 40 | expect(docker_command.query("bash -c 'cd /tmp && /code/target/debug/aliases list'")).to match(/foo\s+local-bar/) 41 | expect(docker_command.query("bash -c 'cd /tmp && /code/target/debug/aliases list'")).to_not match(/foo\s+home-bar/) 42 | end 43 | end 44 | end 45 | 46 | context "with `--global` arg" do 47 | 48 | let(:command) { "bash -c 'cd /tmp && aliases list --global'" } 49 | 50 | it "only lists the aliases in the home dir" do 51 | expect(subject.output).to match(/c\s+cat/) 52 | expect(subject.output).to_not match(/l\s+ls/) 53 | end 54 | end 55 | 56 | context "with `--local` arg" do 57 | 58 | let(:command) { "bash -c 'cd /tmp && aliases list --local'" } 59 | 60 | it "only list the aliases in the local directory" do 61 | expect(subject.output).to match(/l\s+ls/) 62 | expect(subject.output).to_not match(/c\s+cat/) 63 | end 64 | end 65 | 66 | context "with `--directory` arg" do 67 | 68 | let(:command) { "bash -c 'cd /tmp && aliases list --directory \"$PWD\"'" } 69 | 70 | it "only lists the aliases in the given directory" do 71 | expect(subject.output).to match(/l\s+ls/) 72 | expect(subject.output).to_not match(/c\s+cat/) 73 | end 74 | end 75 | 76 | context "with `--name` arg" do 77 | 78 | let(:command) { "bash -c 'cd /tmp && aliases list --name c'" } 79 | 80 | it "only lists the aliases that matches the given name exactly" do 81 | expect(subject.output).to match(/c\s+cat/) 82 | expect(subject.output).to_not match(/l\s+ls/) 83 | end 84 | end 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /spec/rehash_spec.rb: -------------------------------------------------------------------------------- 1 | describe "`rehash` command" do 2 | 3 | context "when there are pending aliases" do 4 | 5 | it "creates shims for them" do 6 | end 7 | 8 | context "when the shim dir is in the user's path" do 9 | 10 | it "makes the alias available for use" do 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/remove_spec.rb: -------------------------------------------------------------------------------- 1 | describe "`remove` command" do 2 | 3 | let(:docker_command) { DockerCommand.new(command, args, dockerfile) } 4 | subject { docker_command.invoke } 5 | 6 | after { docker_command.kill } 7 | 8 | context "on an initialized file system" do 9 | 10 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 11 | 12 | let(:args) { [] } 13 | 14 | 15 | context 'when the alias exist' do 16 | before :each do 17 | DockerCommand.new(add_command, args, dockerfile) 18 | end 19 | 20 | let(:add_command ) { "bash -c 'cd /tmp && /code/target/debug/aliases add foo \"hello world\"'" } 21 | 22 | let(:command) { "bash -c 'cd /tmp && /code/target/debug/aliases remove foo \"hello world\"'" } 23 | 24 | it "removes the alias from the current directory" do 25 | subject 26 | expect(docker_command.query("bash -c 'cd /tmp && /code/target/debug/aliases list --local'")).to_not match("foo") 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require_relative "support/dockerfile_repository" 2 | require_relative "support/logger" 3 | require_relative "support/docker_command" 4 | # This file was generated by the `rspec --init` command. Conventionally, all 5 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 6 | # The generated `.rspec` file contains `--require spec_helper` which will cause 7 | # this file to always be loaded, without a need to explicitly require it in any 8 | # files. 9 | # 10 | # Given that it is always loaded, you are encouraged to keep this file as 11 | # light-weight as possible. Requiring heavyweight dependencies from this file 12 | # will add to the boot time of your test suite on EVERY test run, even for an 13 | # individual file that may not need all of that loaded. Instead, consider making 14 | # a separate helper file that requires the additional dependencies and performs 15 | # the additional setup, and require it from the spec files that actually need 16 | # it. 17 | # 18 | # The `.rspec` file also contains a few flags that are not defaults but that 19 | # users commonly want. 20 | # 21 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 22 | RSpec.configure do |config| 23 | # rspec-expectations config goes here. You can use an alternate 24 | # assertion/expectation library such as wrong or the stdlib/minitest 25 | # assertions if you prefer. 26 | config.expect_with :rspec do |expectations| 27 | # This option will default to `true` in RSpec 4. It makes the `description` 28 | # and `failure_message` of custom matchers include text for helper methods 29 | # defined using `chain`, e.g.: 30 | # be_bigger_than(2).and_smaller_than(4).description 31 | # # => "be bigger than 2 and smaller than 4" 32 | # ...rather than: 33 | # # => "be bigger than 2" 34 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 35 | end 36 | 37 | # rspec-mocks config goes here. You can use an alternate test double 38 | # library (such as bogus or mocha) by changing the `mock_with` option here. 39 | config.mock_with :rspec do |mocks| 40 | # Prevents you from mocking or stubbing a method that does not exist on 41 | # a real object. This is generally recommended, and will default to 42 | # `true` in RSpec 4. 43 | mocks.verify_partial_doubles = true 44 | end 45 | 46 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 47 | # have no way to turn it off -- the option exists only for backwards 48 | # compatibility in RSpec 3). It causes shared context metadata to be 49 | # inherited by the metadata hash of host groups and examples, rather than 50 | # triggering implicit auto-inclusion in groups with matching metadata. 51 | config.shared_context_metadata_behavior = :apply_to_host_groups 52 | 53 | # The settings below are suggested to provide a good initial experience 54 | # with RSpec, but feel free to customize to your heart's content. 55 | =begin 56 | # This allows you to limit a spec run to individual examples or groups 57 | # you care about by tagging them with `:focus` metadata. When nothing 58 | # is tagged with `:focus`, all examples get run. RSpec also provides 59 | # aliases for `it`, `describe`, and `context` that include `:focus` 60 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 61 | config.filter_run_when_matching :focus 62 | 63 | # Allows RSpec to persist some state between runs in order to support 64 | # the `--only-failures` and `--next-failure` CLI options. We recommend 65 | # you configure your source control system to ignore this file. 66 | config.example_status_persistence_file_path = "spec/examples.txt" 67 | 68 | # Limits the available syntax to the non-monkey patched syntax that is 69 | # recommended. For more details, see: 70 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 71 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 72 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 73 | config.disable_monkey_patching! 74 | 75 | # This setting enables warnings. It's recommended, but in some cases may 76 | # be too noisy due to issues in dependencies. 77 | config.warnings = true 78 | 79 | # Many RSpec users commonly either run the entire suite or an individual 80 | # file, and it's useful to allow more verbose output when running an 81 | # individual spec file. 82 | if config.files_to_run.one? 83 | # Use the documentation formatter for detailed output, 84 | # unless a formatter has already been configured 85 | # (e.g. via a command-line flag). 86 | config.default_formatter = 'doc' 87 | end 88 | 89 | # Print the 10 slowest examples and example groups at the 90 | # end of the spec run, to help surface which specs are running 91 | # particularly slow. 92 | config.profile_examples = 10 93 | 94 | # Run specs in random order to surface order dependencies. If you find an 95 | # order dependency and want to debug it, you can fix the order by providing 96 | # the seed, which is printed after each run. 97 | # --seed 1234 98 | config.order = :random 99 | 100 | # Seed global randomization in this process using the `--seed` CLI option. 101 | # Setting this allows you to use `--seed` to deterministically reproduce 102 | # test failures related to randomization by passing the same `--seed` value 103 | # as the one that triggered the failure. 104 | Kernel.srand config.seed 105 | =end 106 | end 107 | -------------------------------------------------------------------------------- /spec/support/docker_command.rb: -------------------------------------------------------------------------------- 1 | class DockerCommand 2 | 3 | CONTAINER_NAME = "aliases-test-container" 4 | IMAGE_NAME = "aliases-test-image" 5 | 6 | attr_reader :output 7 | 8 | def initialize(command, args, dockerfile) 9 | @command, @args, @dockerfile = command, args, dockerfile 10 | end 11 | 12 | def invoke 13 | start_container 14 | Logger.info "---- Running command: #{@command} #{@args}" 15 | Logger.info "docker exec -ti #{CONTAINER_NAME} #{@command} #{@args.join(" ")}" 16 | @output = `docker exec -ti #{CONTAINER_NAME} #{@command} #{@args.join(" ")}` 17 | self 18 | end 19 | 20 | def query(command) 21 | Logger.info "---- Running command: #{command}" 22 | Logger.info "docker exec -ti #{CONTAINER_NAME} #{command}" 23 | `docker exec -ti #{CONTAINER_NAME} #{command}` 24 | end 25 | 26 | def diff 27 | `docker diff #{CONTAINER_NAME}`.split("\n") 28 | end 29 | 30 | def kill 31 | Logger.info '---- Killing container' 32 | run_command("docker rm --force #{CONTAINER_NAME}") 33 | end 34 | 35 | def start_container 36 | build_container 37 | Logger.info '---- Starting container' 38 | run_command("docker run -ti -v ${APP_ROOT}:/code -d --workdir /code --name #{CONTAINER_NAME} #{IMAGE_NAME} sh") 39 | end 40 | 41 | private 42 | 43 | def build_container 44 | if ENV["REBUILD_CONTAINER"] || !system("docker images -q #{IMAGE_NAME}:latest | grep #{IMAGE_NAME}") 45 | Logger.info '---- Building container' 46 | run_command("docker build --tag #{IMAGE_NAME} --file #{@dockerfile} .") 47 | end 48 | end 49 | 50 | 51 | def run_command(command_string) 52 | if verbose? 53 | Logger.info command_string 54 | system(command_string) 55 | else 56 | `#{command_string}` 57 | end 58 | end 59 | 60 | def verbose? 61 | ENV["VERBOSE"] == "true" 62 | end 63 | 64 | end 65 | -------------------------------------------------------------------------------- /spec/support/dockerfile_repository.rb: -------------------------------------------------------------------------------- 1 | class DockerfileRepository 2 | 3 | def self.find(name) 4 | lookup[name] or raise "Could not find a dockerfile for '#{name}'" 5 | end 6 | 7 | private 8 | 9 | def self.lookup 10 | { 11 | empty: "spec/Dockerfile", 12 | initialized: "spec/dockerfiles/initialized", 13 | } 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/logger.rb: -------------------------------------------------------------------------------- 1 | class Logger 2 | 3 | def self.info(message) 4 | puts message if verbose? 5 | end 6 | 7 | private 8 | 9 | def self.verbose? 10 | ENV["VERBOSE"] == "true" 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /spec/users_spec.rb: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | describe "`users` command" do 4 | 5 | let(:docker_command) { DockerCommand.new(command, args, dockerfile) } 6 | let(:args) { [] } 7 | let(:dockerfile) { DockerfileRepository.find(:initialized) } 8 | subject { docker_command.invoke } 9 | 10 | after { docker_command.kill } 11 | 12 | context "without any subcommand args" do 13 | 14 | let(:command ) { "bash -c 'cd /tmp && /code/target/debug/aliases users'" } 15 | 16 | context "without any custom users" do 17 | 18 | it "lists the default user" do 19 | expect(subject.output).to match("default") 20 | end 21 | end 22 | 23 | context "given there are custom users" do 24 | 25 | before do 26 | docker_command.start_container 27 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases init --user superman'") 28 | end 29 | 30 | it "lists all the users" do 31 | expect(subject.output).to match("default.*superman") 32 | end 33 | end 34 | end 35 | 36 | context "with subcommand `move`" do 37 | 38 | context "when given a username that exists" do 39 | 40 | context "when given a priority value higher than it's current" do 41 | 42 | it "moves the user up to the value given" 43 | end 44 | context "when given a priority value lower than it's current" do 45 | 46 | it "moves the user down to the value given" 47 | end 48 | context "when given a priority value well beyond the lowest user" do 49 | 50 | it "moves the user to the lowest priority" 51 | end 52 | 53 | context "when given a negative priority value" do 54 | 55 | it "moves the users priority down by the given number" 56 | end 57 | end 58 | 59 | context "when the given username does NOT exist" do 60 | 61 | it "has a error exit code" 62 | it "returns a sensible error message" 63 | end 64 | end 65 | 66 | context "with subcommand `use`" do 67 | context "when the user's priority is NOT number 1" do 68 | 69 | it "moves the user to the top of the priority list" 70 | end 71 | 72 | context "when the user's priority is number 1" do 73 | 74 | it "leaves the user at the top of the priority list" 75 | end 76 | end 77 | 78 | context "with subcommand `enable`" do 79 | 80 | let(:command ) { "bash -c 'cat ~/.aliases_cfg'" } # TODO use an alias command instead 81 | 82 | context "when the user is disabled" do 83 | 84 | before do 85 | docker_command.start_container 86 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases init --user superman'") 87 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases disable superman'") 88 | end 89 | 90 | it "becomes enabled" do 91 | expect(JSON.parse(subject.output)["disabled_users"]).to eq nil # TODO this should default to an empty array not nil 92 | end 93 | end 94 | 95 | context "when the user is already enabled" do 96 | 97 | before do 98 | docker_command.start_container 99 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases init --user superman'") 100 | end 101 | 102 | it "stays enabled" do 103 | expect(JSON.parse(subject.output)["disabled_users"]).to eq nil # TODO this should default to an empty array not nil 104 | end 105 | end 106 | end 107 | 108 | context "with subcommand `disable`" do 109 | 110 | let(:command) { "bash -c 'cd /tmp && /code/target/debug/aliases users disable superman'" } 111 | 112 | context "when the user is enabled" do 113 | 114 | before do 115 | docker_command.start_container 116 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases init --user superman'") 117 | end 118 | 119 | it "makes them disabled" do 120 | subject 121 | expect(JSON.parse(docker_command.query("bash -c 'cat ~/.aliases_cfg'"))["disabled_users"].include?("superman")).to eq true 122 | end 123 | end 124 | 125 | context "when the user is disabled" do 126 | 127 | before do 128 | docker_command.start_container 129 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases init --user superman'") 130 | docker_command.query("bash -c 'cd ~ && /code/target/debug/aliases users disable superman'") 131 | end 132 | 133 | it "leaves them disabled" do 134 | subject 135 | expect(JSON.parse(docker_command.query("bash -c 'cat ~/.aliases_cfg'"))["disabled_users"].include?("superman")).to eq true 136 | end 137 | end 138 | 139 | it "means their aliases no longer work" 140 | end 141 | end 142 | -------------------------------------------------------------------------------- /src/aliases/app.rs: -------------------------------------------------------------------------------- 1 | use aliases::commands::{Init, List, Add, Remove, Rehash, Exec, Users, MoveUser, CloneRepo, PullRepo, EnableUser, DisableUser, AliasCommand, Directories}; 2 | use aliases::Config; 3 | 4 | use std::env; 5 | use std::path::{PathBuf}; 6 | use std::process; 7 | 8 | pub struct App { 9 | config: Config, 10 | pub current_path: PathBuf, 11 | } 12 | 13 | impl App { 14 | 15 | pub fn new() -> App { 16 | let config = Config::load(); 17 | App { 18 | config: config, 19 | current_path: env::current_dir().unwrap(), 20 | } 21 | } 22 | 23 | pub fn execute_init(&mut self, global: bool, user: Option<&str>) { 24 | Init::new(calculate_target_path_for_init(global, &self.current_path), self.config.clone(), global, user).execute(); 25 | } 26 | 27 | pub fn execute_list(&mut self, directory: Option<&str>, name: Option<&str>) { 28 | let exit_code = List::new(self.current_path.clone(), directory, name).execute(); 29 | process::exit(exit_code); 30 | } 31 | 32 | pub fn execute_add(&mut self, name: Option<&str>, command: Option<&str>) { 33 | let exit_code = Add::new(self.current_path.clone(), name, command).execute(); 34 | process::exit(exit_code); 35 | } 36 | 37 | pub fn execute_remove(&mut self, name: Option<&str>) { 38 | let exit_code = Remove::new(self.current_path.clone(), name).execute(); 39 | process::exit(exit_code); 40 | } 41 | 42 | pub fn execute_directories(&mut self) { 43 | let exit_code = Directories::new().execute(); 44 | process::exit(exit_code); 45 | } 46 | 47 | pub fn execute_rehash(&mut self) { 48 | // TODO this also needs an exit code 49 | Rehash::new(self.config.shim_path(), self.config.alias_paths()).execute(); 50 | } 51 | 52 | pub fn execute_exec(&mut self, directory: String, name: String, forwarding_args: Vec) { 53 | Exec::new(directory, name, forwarding_args).execute(); 54 | } 55 | 56 | pub fn execute_users(&mut self) { 57 | Users::new(self.config.clone()).execute(); 58 | } 59 | 60 | pub fn execute_clone(&mut self, username: String, repo_url: Option<&str>, enable: bool) { 61 | CloneRepo::new(username, repo_url, enable).execute(); 62 | } 63 | 64 | pub fn execute_pull(&mut self, username: Option<&str>) { 65 | let response = PullRepo::new(username).execute(); 66 | if response.is_error() { 67 | response.print_error_message(); 68 | } 69 | } 70 | 71 | pub fn prioritize_user(&mut self, username: String, position: usize) { 72 | MoveUser::new(username, position).execute(); 73 | } 74 | 75 | pub fn enable_user(&mut self, username: String) { 76 | EnableUser::new(username).execute(); 77 | } 78 | 79 | pub fn disable_user(&mut self, username: String) { 80 | DisableUser::new(username).execute(); 81 | } 82 | } 83 | 84 | fn calculate_target_path_for_init(global: bool, current_path: &PathBuf) -> PathBuf { 85 | match global { 86 | false => { current_path.clone() } 87 | true => { 88 | match env::var("HOME") { 89 | Ok(home_dir) => { 90 | PathBuf::from(home_dir) 91 | }, 92 | Err(_) => { 93 | current_path.clone() 94 | }, 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/aliases/builders/alias_builder.rs: -------------------------------------------------------------------------------- 1 | use yaml_rust::Yaml; 2 | use std::path::PathBuf; 3 | 4 | use aliases::models::Alias; 5 | use aliases::models::Conditional; 6 | use aliases::models::UserConfirmation; 7 | 8 | pub struct AliasBuilder { 9 | name: String, 10 | basename: PathBuf, 11 | yaml: Yaml, 12 | } 13 | 14 | impl AliasBuilder { 15 | 16 | pub fn from_yaml(name: &str, basename: PathBuf, yaml: Yaml) -> AliasBuilder { 17 | AliasBuilder { name: name.to_string(), basename: basename, yaml: yaml } 18 | } 19 | 20 | pub fn build(&self) -> Result { 21 | match self.command() { 22 | None => { Err("No command given. Each alias needs a command.") } 23 | Some(command) => { 24 | Ok(Alias { 25 | name: self.name.clone(), 26 | command: command.clone(), 27 | confirm: self.confirm(), 28 | confirmation_message: self.confirmation_message(command.clone()), 29 | user_confirmation: UserConfirmation::new(self.confirm(), self.confirmation_message(command)), 30 | delayed_backout: self.delayed_backout(), 31 | unit_test: self.unit_test(), 32 | conditional: self.conditional(), 33 | basename: self.basename.clone(), 34 | args: vec![], 35 | enable_positional_arguments: self.enable_positional_arguments(), 36 | quiet: self.quiet(), 37 | }) 38 | } 39 | } 40 | } 41 | 42 | // --------- private ---------// 43 | 44 | fn command(&self) -> Option { 45 | match self.yaml["command"].as_str() { 46 | None => None, 47 | Some(string) => Some(string.to_string()) 48 | } 49 | } 50 | 51 | fn confirm(&self) -> bool { 52 | match self.yaml["confirm"].as_bool() { 53 | None => false, 54 | Some(b) => b 55 | } 56 | } 57 | 58 | fn confirmation_message(&self, command: String) -> String { 59 | match self.yaml["confirmation_message"].as_str() { 60 | None => self.default_confirmation_message(&command), 61 | Some(s) => s.to_string() 62 | } 63 | } 64 | 65 | fn default_confirmation_message(&self, command: &String) -> String { 66 | let message = "About to execute `".to_string(); 67 | message + &command + "`" 68 | } 69 | 70 | fn conditional(&self) -> Conditional { 71 | match self.yaml["conditional"].as_str() { 72 | None => Conditional::new(String::from("true")), 73 | Some(s) => Conditional::new(s.to_string()) 74 | } 75 | } 76 | 77 | fn delayed_backout(&self) -> usize { 78 | match self.yaml["backout_seconds"].as_i64() { 79 | None => 0, 80 | Some(seconds) => seconds.abs() as usize 81 | } 82 | } 83 | 84 | fn unit_test(&self) -> String { 85 | match self.yaml["unit_test"].as_str() { 86 | None => String::from("true"), 87 | Some(s) => s.to_string(), 88 | } 89 | } 90 | 91 | fn quiet(&self) -> bool { 92 | match self.yaml["quiet"].as_bool() { 93 | None => false, 94 | Some(value) => value, 95 | } 96 | } 97 | 98 | fn enable_positional_arguments(&self) -> bool { 99 | match self.yaml["enable_positional_arguments"].as_bool() { 100 | None => false, 101 | Some(value) => value, 102 | } 103 | } 104 | } 105 | 106 | #[cfg(test)] 107 | mod tests { 108 | use super::*; 109 | use yaml_rust::YamlLoader; 110 | use std::path::PathBuf; 111 | 112 | #[test] 113 | fn extracts_the_command_string_from_the_yaml() { 114 | let string = 115 | " 116 | command: test-command 117 | "; 118 | let yaml = YamlLoader::load_from_str(string).unwrap(); 119 | let document = yaml[0].clone(); 120 | let builder = AliasBuilder::from_yaml("test", PathBuf::new(), document); 121 | let alias = builder.build().unwrap(); 122 | assert!(alias.command == "test-command"); 123 | } 124 | 125 | #[test] 126 | fn enables_the_positional_arguments_if_set_in_yaml() { 127 | let string = 128 | " 129 | command: test-command 130 | enable_positional_arguments: true 131 | "; 132 | let yaml = YamlLoader::load_from_str(string).unwrap(); 133 | let document = yaml[0].clone(); 134 | let builder = AliasBuilder::from_yaml("test", PathBuf::new(), document); 135 | let alias = builder.build().unwrap(); 136 | assert!(alias.enable_positional_arguments == true); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/aliases/builders/command_builder.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use regex::Regex; 3 | 4 | pub struct CommandBuilder { 5 | shell: String, 6 | command_name: Option, 7 | args: Vec, 8 | } 9 | 10 | impl CommandBuilder { 11 | 12 | pub fn using_bash() -> Self { 13 | CommandBuilder { shell: String::from("bash"), command_name: None, args: vec![] } 14 | } 15 | 16 | pub fn for_command(&mut self, command_name: &str) -> &mut Self { 17 | self.command_name = Some(command_name.to_string()); 18 | self 19 | } 20 | 21 | pub fn with_args(&mut self, args: &Vec) -> &mut Self { 22 | self.args = args.clone(); 23 | self 24 | } 25 | 26 | pub fn build(&self) -> Command { 27 | let pseudo_command = self.pseudo_build(); 28 | let mut command = Command::new(self.shell.clone()); 29 | command.arg("-c"); 30 | command.arg(pseudo_command.command_string.clone()); 31 | for arg in pseudo_command.args { 32 | command.arg(arg.clone()); 33 | } 34 | command 35 | } 36 | 37 | pub fn pseudo_build(&self) -> PseudoCommand { 38 | let args = self.build_args(); 39 | let command_string = self.build_command_string(); 40 | PseudoCommand { command_string: command_string, args: args } 41 | } 42 | 43 | //---------- private ------------// 44 | 45 | fn build_args(&self) -> Vec { 46 | let num = self.total_numbered_positional_arguments(&self.command_name.clone().unwrap()); 47 | let positional_arguments = self.args.clone().iter().take(num).map(|s| s.clone()).collect::>(); 48 | positional_arguments 49 | } 50 | 51 | fn build_command_string(&self) -> String { 52 | let mut command_string = self.command_name.clone().unwrap(); 53 | let num = self.total_numbered_positional_arguments(&self.command_name.clone().unwrap()); 54 | if self.catch_all() { 55 | let num = self.total_numbered_positional_arguments(&command_string); 56 | let remaining = self.args.clone().iter().skip(num).map(|s| s.clone()).collect::>(); 57 | let result = remaining.iter().fold(String::new(), |acc, ref arg| acc + &arg + " ").trim().to_string(); 58 | command_string = command_string.replace("$@", &result); 59 | } else { 60 | let non_positional_args = self.args.clone().iter().skip(num).map(|s| s.clone()).collect::>(); 61 | for arg in &non_positional_args { 62 | command_string = command_string + " " + arg; 63 | } 64 | } 65 | command_string 66 | } 67 | 68 | fn total_numbered_positional_arguments(&self, command: &String) -> usize { 69 | let re = Regex::new(r"\$(\d+)").unwrap(); 70 | match re.captures_iter(command).map(|capture| capture.get(1).unwrap().as_str().parse::().unwrap()).max() { 71 | Some(value) => { value + 1 } 72 | None => 0 73 | } 74 | } 75 | 76 | fn catch_all(&self) -> bool { 77 | let re = Regex::new(r"\$@").unwrap(); 78 | re.is_match(&self.command_name.clone().unwrap()) 79 | } 80 | } 81 | 82 | pub struct PseudoCommand { 83 | pub command_string: String, 84 | pub args: Vec, 85 | } 86 | 87 | #[cfg(test)] 88 | mod tests { 89 | use super::*; 90 | 91 | #[test] 92 | fn without_args_it_builds_a_basic_command() { 93 | let command = CommandBuilder::using_bash().for_command("seb-test").pseudo_build(); 94 | assert_eq!(command.command_string, "seb-test"); 95 | assert_eq!(command.args.len(), 0); 96 | } 97 | 98 | #[test] 99 | fn with_args_it_appends_them_to_the_command() { 100 | let args = vec!["first".to_string(), "second".to_string()]; 101 | let command = CommandBuilder::using_bash().for_command("seb-test").with_args(&args).pseudo_build(); 102 | assert_eq!(command.command_string, "seb-test first second"); 103 | assert_eq!(command.args.len(), 0); 104 | } 105 | 106 | #[test] 107 | fn when_command_references_positional_args_args_are_set_up() { 108 | let args = vec!["first".to_string(), "second".to_string()]; 109 | let command = CommandBuilder::using_bash().for_command("ls $0 | grep $1").with_args(&args).pseudo_build(); 110 | assert_eq!(command.command_string, "ls $0 | grep $1"); 111 | assert_eq!(command.args, args); 112 | } 113 | 114 | #[test] 115 | fn when_command_references_positional_args_args_are_set_up_and_extra_are_appended_to_the_command() { 116 | let args = vec!["first".to_string(), "second".to_string(), "third".to_string(), "forth".to_string()]; 117 | let command = CommandBuilder::using_bash().for_command("ls $0 | grep $1").with_args(&args).pseudo_build(); 118 | assert_eq!(command.command_string, "ls $0 | grep $1 third forth"); 119 | assert_eq!(command.args, vec!["first".to_string(), "second".to_string()]); 120 | } 121 | 122 | #[test] 123 | fn when_command_references_a_catch_all_args_all_args_are_injected_into_the_command_string() { 124 | let args = vec!["first".to_string(), "second".to_string()]; 125 | let command = CommandBuilder::using_bash().for_command("ls $@ | grep something").with_args(&args).pseudo_build(); 126 | assert_eq!(command.command_string, "ls first second | grep something"); 127 | assert_eq!(command.args.len(), 0); 128 | } 129 | 130 | #[test] 131 | fn when_command_references_positional_args_and_a_catch_all_they_are_both_handled() { 132 | let args = vec!["first".to_string(), "second".to_string(), "third".to_string()]; 133 | let command = CommandBuilder::using_bash().for_command("ls $@ | grep $0 | grep something").with_args(&args).pseudo_build(); 134 | assert_eq!(command.command_string, "ls second third | grep $0 | grep something"); 135 | assert_eq!(command.args, vec!["first"]); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/aliases/builders/mod.rs: -------------------------------------------------------------------------------- 1 | mod alias_builder; 2 | mod command_builder; 3 | 4 | pub use self::alias_builder::AliasBuilder; 5 | pub use self::command_builder::CommandBuilder; 6 | pub use self::command_builder::PseudoCommand; 7 | -------------------------------------------------------------------------------- /src/aliases/collections/mod.rs: -------------------------------------------------------------------------------- 1 | use aliases::models::Alias; 2 | use std::cmp::Ordering; 3 | use std::result::Result; 4 | use yaml_rust::{Yaml, YamlLoader}; 5 | 6 | #[derive(Debug,Clone)] 7 | pub struct Aliases { 8 | raw_collection: Vec, 9 | iteration_index: usize, 10 | } 11 | 12 | impl Aliases { 13 | 14 | pub fn new(raw_collection: Vec) -> Aliases { 15 | Aliases { raw_collection: raw_collection, iteration_index: 0 } 16 | } 17 | 18 | pub fn merge(&self, others: Aliases) -> Aliases { 19 | let mut merged_aliases = self.clone(); 20 | for other_alias in others { 21 | let _ = merged_aliases.push(&other_alias); 22 | } 23 | merged_aliases 24 | } 25 | 26 | pub fn remove(&mut self, alias: &Alias) -> Result<(), &'static str> { 27 | match self.raw_collection.iter().position(|a| a.name == alias.name) { 28 | Some(n) => { 29 | self.raw_collection.remove(n); 30 | Ok(()) 31 | }, 32 | None => { Err("Alias does not exist") } 33 | } 34 | } 35 | 36 | pub fn push(&mut self, alias: &Alias) -> Result<(), &'static str> { 37 | if self.raw_collection.iter().any(|a| { a.name == alias.name }) { 38 | Err("Alias was a duplicate") 39 | } else { 40 | self.raw_collection.push(alias.clone()); 41 | Ok(()) 42 | } 43 | } 44 | 45 | pub fn len(&self) -> usize { 46 | self.raw_collection.len() 47 | } 48 | 49 | pub fn to_yaml(&self) -> Yaml { 50 | let mut yaml_string = String::from(""); 51 | self.raw_collection.iter().fold(&mut yaml_string, |acc, ref alias| { 52 | acc.push_str(&alias.as_yaml()); 53 | acc.push_str("\n"); 54 | return acc; 55 | }); 56 | match YamlLoader::load_from_str(&yaml_string).unwrap().get(0) { 57 | Some(value) => { value.clone() }, 58 | None => { Yaml::from_str("") } 59 | } 60 | } 61 | } 62 | 63 | impl PartialEq for Aliases { 64 | 65 | fn eq(&self, other: &Self) -> bool { 66 | self.raw_collection.cmp(&other.raw_collection) == Ordering::Equal 67 | } 68 | } 69 | 70 | impl Iterator for Aliases { 71 | 72 | type Item = Alias; 73 | 74 | fn next(&mut self) -> Option { 75 | if self.iteration_index < self.raw_collection.len() { 76 | let alias = Some(self.raw_collection[self.iteration_index].clone()); 77 | self.iteration_index += 1; 78 | alias 79 | } else { 80 | self.iteration_index = 0; 81 | None 82 | } 83 | } 84 | } 85 | 86 | #[test] 87 | fn when_empty_it_build_empty_yaml() { 88 | let collection = Aliases::new(vec![]); 89 | let result = collection.to_yaml(); 90 | assert_eq!(result, Yaml::from_str("")); 91 | } 92 | -------------------------------------------------------------------------------- /src/aliases/commands/add.rs: -------------------------------------------------------------------------------- 1 | use aliases::repositories::AliasFileRepository; 2 | use aliases::models::Alias; 3 | use aliases::Config; 4 | use std::path::PathBuf; 5 | use aliases::commands::{Init, Rehash, AliasCommand}; 6 | 7 | pub struct Add { 8 | directory: PathBuf, 9 | name: String, 10 | command: String, 11 | } 12 | 13 | impl Add { 14 | 15 | pub fn new(directory: PathBuf, name: Option<&str>, command: Option<&str>) -> Self { 16 | Add { 17 | directory: directory, 18 | name: name.unwrap().to_string(), // TODO 19 | command: command.unwrap().to_string() // TODO 20 | } 21 | } 22 | 23 | pub fn execute(&self) -> i32 { 24 | let mut alias_file = AliasFileRepository::find(&self.directory); 25 | alias_file.add_alias(self.build_alias()); 26 | AliasFileRepository::save(alias_file); 27 | let config = Config::load(); 28 | Init::new(self.directory.clone(), config.clone(), false, None).execute(); 29 | Rehash::new(config.shim_path(), config.alias_paths()).execute(); 30 | 0 // TODO make this a real exit code 31 | } 32 | 33 | //----- private -----// 34 | 35 | fn build_alias(&self) -> Alias { 36 | let mut alias = Alias::new(); 37 | alias.name = self.name.clone(); 38 | alias.command = self.command.clone(); 39 | alias 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/aliases/commands/clone_repo.rs: -------------------------------------------------------------------------------- 1 | use aliases::commands::{CommandResponse, AliasCommand}; 2 | use aliases::repositories::UserRepository; 3 | use aliases::models::User; 4 | 5 | pub struct CloneRepo { 6 | user: User, 7 | repo_url: Option, 8 | enable: bool, 9 | } 10 | 11 | impl CloneRepo { 12 | 13 | pub fn new(username: String, repo_url: Option<&str>, enable: bool) -> Self { 14 | CloneRepo { 15 | user: UserRepository::find_by_name_or_blow(&username), 16 | repo_url: repo_url.map(|url| url.to_owned()), 17 | enable, 18 | } 19 | } 20 | 21 | fn enable_user(&self) { 22 | if self.enable { 23 | self.user.enable(); 24 | } 25 | } 26 | 27 | } 28 | 29 | impl AliasCommand for CloneRepo { 30 | 31 | fn execute(&self) -> CommandResponse { 32 | match self.user.clone_external_repo(self.repo_url.clone()) { 33 | Err(message) => CommandResponse::new(1, Some(message)), 34 | Ok(_) => { 35 | self.enable_user(); 36 | CommandResponse::success() 37 | }, 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/aliases/commands/directories.rs: -------------------------------------------------------------------------------- 1 | use aliases::Config; 2 | 3 | pub struct Directories; 4 | 5 | impl Directories { 6 | 7 | pub fn new() -> Self { 8 | Directories {} 9 | } 10 | 11 | pub fn execute(&self) -> i32 { 12 | let directories = Config::load().directories(); 13 | println!("{:?}", directories); 14 | 0 // TODO make this a real exit code 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/aliases/commands/disable_user.rs: -------------------------------------------------------------------------------- 1 | use aliases::commands::{CommandResponse, AliasCommand}; 2 | use aliases::repositories::UserRepository; 3 | use aliases::models::User; 4 | 5 | pub struct DisableUser { 6 | user: User, 7 | } 8 | 9 | impl DisableUser { 10 | 11 | pub fn new(username: String) -> Self { 12 | DisableUser { 13 | user: UserRepository::find_by_name_or_blow(&username), 14 | } 15 | } 16 | 17 | } 18 | 19 | impl AliasCommand for DisableUser { 20 | 21 | fn execute(&self) -> CommandResponse { 22 | self.user.disable(); 23 | CommandResponse::success() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/aliases/commands/enable_user.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use aliases::commands::{AliasCommand, CommandResponse}; 4 | use aliases::repositories::UserRepository; 5 | use aliases::models::User; 6 | 7 | pub struct EnableUser { 8 | user: User, 9 | } 10 | 11 | impl EnableUser { 12 | 13 | pub fn new(username: String) -> Self { 14 | EnableUser { 15 | user: UserRepository::find_by_name_or_blow(&username), 16 | } 17 | } 18 | } 19 | 20 | impl AliasCommand for EnableUser { 21 | 22 | fn execute(&self) -> CommandResponse { 23 | match self.user.enable() { 24 | Ok(_) => CommandResponse::success(), 25 | Err(error) => CommandResponse::new(1, Some(error.description().to_owned())), 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/aliases/commands/exec/mod.rs: -------------------------------------------------------------------------------- 1 | use aliases::collections::Aliases; 2 | use aliases::models::Alias; 3 | use aliases::ExecutionWorkflow; 4 | use aliases::repositories::AliasRepository; 5 | use aliases::commands::{AliasCommand, CommandResponse}; 6 | 7 | pub struct Exec { 8 | directory: String, 9 | name: String, 10 | forwarding_args: Vec, 11 | } 12 | 13 | impl Exec { 14 | 15 | pub fn new(directory: String, name: String, forwarding_args: Vec) -> Self { 16 | Exec { 17 | directory: directory, 18 | name: name, 19 | forwarding_args: forwarding_args, 20 | } 21 | } 22 | 23 | 24 | //----------- private -----------// 25 | 26 | fn find_alias(&self) -> Result { 27 | match self.directory_aliases() { 28 | Err(message) => { Err(message) }, 29 | Ok(aliases) => { 30 | match aliases.into_iter().find(|alias| { alias.name == self.name }) { 31 | None => { Err("could not find an alias to execute") }, 32 | Some(alias) => { Ok(alias) }, 33 | } 34 | } 35 | } 36 | } 37 | 38 | fn directory_aliases(&self) -> Result { 39 | AliasRepository::find_for_directory(&self.directory) 40 | } 41 | } 42 | 43 | impl AliasCommand for Exec { 44 | 45 | fn execute(&self) -> CommandResponse { 46 | match self.find_alias() { 47 | Err(message) => { println!("Error! {}", message); } // TODO handle this better? 48 | Ok(mut alias) => { 49 | alias.add_arguments(self.forwarding_args.clone()); 50 | ExecutionWorkflow::new(alias).execute(); 51 | } 52 | } 53 | CommandResponse::success() 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/aliases/commands/init.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use aliases::Config; 4 | use aliases::commands::{AliasCommand, CommandResponse}; 5 | use aliases::models::User; 6 | use aliases::repositories::UserRepository; 7 | 8 | pub struct Init { 9 | target_path: PathBuf, 10 | config: Config, 11 | global: bool, 12 | user: User, 13 | } 14 | 15 | impl Init { 16 | 17 | pub fn new(target_path: PathBuf, config: Config, global: bool, username: Option<&str>) -> Init { 18 | Init { 19 | target_path, 20 | config, 21 | global, 22 | user: username.map_or(UserRepository::default(), |name| UserRepository::find_by_name_or_create(name) ), 23 | } 24 | } 25 | 26 | } 27 | 28 | impl AliasCommand for Init { 29 | 30 | fn execute(&self) -> CommandResponse { 31 | if self.global { 32 | let path_update = String::from("export PATH=\"") + &self.config.shim_directory + ":${PATH}\""; 33 | // what the hell was I trying to do here? 34 | println!("{}\naliases rehash", path_update); 35 | } else { 36 | self.user.init_directory(&self.target_path); 37 | } 38 | CommandResponse::success() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/aliases/commands/list.rs: -------------------------------------------------------------------------------- 1 | use aliases::views::AliasesView; 2 | use aliases::collections::Aliases; 3 | use aliases::factories::AliasFactory; 4 | use aliases::repositories::AliasRepository; 5 | 6 | use std::path::PathBuf; 7 | use std::env; 8 | 9 | pub struct List { 10 | current_path: PathBuf, 11 | directory_filter: Option, 12 | name_filter: Option, 13 | } 14 | 15 | impl List { 16 | 17 | pub fn new(current_path: PathBuf, directory_filter: Option<&str>, name_filter: Option<&str>) -> Self { 18 | // TODO there has to be a better way to do this right? 19 | let directory_string; 20 | match directory_filter { 21 | Some(string) => directory_string = Some(string.to_string()), 22 | None => directory_string = None, 23 | } 24 | let name_string; 25 | match name_filter { 26 | Some(string) => name_string = Some(string.to_string()), 27 | None => name_string = None, 28 | } 29 | List { 30 | current_path: current_path, 31 | directory_filter: directory_string, 32 | name_filter: name_string, 33 | } 34 | } 35 | 36 | // TODO this needs to start using the AliasRepository 37 | pub fn execute(&mut self) -> i32 { 38 | let mut aliases = self.local_aliases().merge(self.parent_aliases()).merge(self.global_aliases()); 39 | if let Some(ref directory_filter) = self.directory_filter { 40 | let mut new_collection = vec![]; 41 | for i in aliases.into_iter().filter(|alias| alias.basename.to_str().unwrap() == *directory_filter) { 42 | new_collection.push(i.clone()); 43 | } 44 | aliases = Aliases::new(new_collection); 45 | } 46 | if let Some(ref name_filter) = self.name_filter { 47 | let mut new_collection = vec![]; 48 | for i in aliases.into_iter().filter(|alias| alias.name == *name_filter) { 49 | new_collection.push(i.clone()); 50 | } 51 | aliases = Aliases::new(new_collection); 52 | } 53 | AliasesView::new(aliases.clone()).render(); 54 | if aliases.len() > 0 { 55 | 0 56 | } else { 57 | 1 58 | } 59 | } 60 | 61 | // ------ private ----- // 62 | 63 | fn global_aliases(&mut self) -> Aliases { 64 | match AliasRepository::find_for_directory(&self.global_aliases_data_file().to_str().unwrap().to_string()) { 65 | Err(_) => { AliasFactory::create_empty() }, 66 | Ok(aliases) => { aliases } 67 | } 68 | } 69 | 70 | fn parent_aliases(&mut self) -> Aliases { 71 | AliasFactory::create_empty() // TODO let's leave this for later, it can be tricky depending on what dir they are in 72 | } 73 | 74 | fn local_aliases(&mut self) -> Aliases { 75 | match AliasRepository::find_for_directory(&self.current_path.to_str().unwrap().to_string()) { 76 | Err(_) => { AliasFactory::create_empty() }, 77 | Ok(aliases) => { aliases } 78 | } 79 | } 80 | 81 | fn global_aliases_data_file(&self) -> PathBuf { 82 | match self.home_dir() { 83 | Some(dir) => dir, 84 | None => PathBuf::new() 85 | } 86 | } 87 | 88 | fn home_dir(&self) -> Option { 89 | match env::var("HOME") { 90 | Ok(home_dir) => { 91 | Some(PathBuf::from(home_dir)) 92 | }, 93 | Err(_) => { 94 | None 95 | }, 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/aliases/commands/mod.rs: -------------------------------------------------------------------------------- 1 | mod init; 2 | mod list; 3 | mod add; 4 | mod remove; 5 | mod rehash; 6 | mod exec; 7 | mod users; 8 | mod move_user; 9 | mod clone_repo; 10 | mod pull_repo; 11 | mod enable_user; 12 | mod disable_user; 13 | mod directories; 14 | 15 | pub use self::init::Init; 16 | pub use self::list::List; 17 | pub use self::add::Add; 18 | pub use self::remove::Remove; 19 | pub use self::rehash::Rehash; 20 | pub use self::exec::Exec; 21 | pub use self::users::Users; 22 | pub use self::move_user::MoveUser; 23 | pub use self::clone_repo::CloneRepo; 24 | pub use self::pull_repo::PullRepo; 25 | pub use self::enable_user::EnableUser; 26 | pub use self::disable_user::DisableUser; 27 | pub use self::directories::Directories; 28 | 29 | pub trait AliasCommand { 30 | fn execute(&self) -> CommandResponse; 31 | } 32 | 33 | pub struct CommandResponse { 34 | code: u8, 35 | message: Option, 36 | } 37 | 38 | impl CommandResponse { 39 | 40 | pub fn success() -> Self { 41 | Self::new(0, None) 42 | } 43 | 44 | pub fn new(code: u8, message: Option) -> Self { 45 | CommandResponse { code: code, message: message } 46 | } 47 | 48 | 49 | pub fn is_error(&self) -> bool { 50 | self.code > 0 51 | } 52 | 53 | pub fn print_error_message(&self) { 54 | match self.message { 55 | None => {} 56 | Some(ref message) => { println!("An error occurred:\n {}", message); } 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/aliases/commands/move_user.rs: -------------------------------------------------------------------------------- 1 | use aliases::commands::{CommandResponse, AliasCommand}; 2 | use aliases::repositories::UserRepository; 3 | use aliases::models::User; 4 | 5 | pub struct MoveUser { 6 | user: User, 7 | position: usize, 8 | } 9 | 10 | impl MoveUser { 11 | 12 | pub fn new(username: String, position: usize) -> Self { 13 | MoveUser { 14 | user: UserRepository::find_by_name_or_blow(&username), 15 | position, 16 | } 17 | } 18 | 19 | } 20 | 21 | impl AliasCommand for MoveUser { 22 | 23 | fn execute(&self) -> CommandResponse { 24 | match self.user.set_priority(self.position) { 25 | Ok(_) => { CommandResponse::success() }, 26 | Err(message) => { 27 | println!("{}", message); 28 | CommandResponse::new(1, Some(message)) 29 | }, 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/aliases/commands/pull_repo.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | use aliases::commands::{AliasCommand, CommandResponse}; 4 | use aliases::models::User; 5 | use aliases::repositories::UserRepository; 6 | 7 | pub struct PullRepo { 8 | user: Option, 9 | } 10 | 11 | impl PullRepo { 12 | 13 | pub fn new(username: Option<&str>) -> Self { 14 | let user = username.map(|name| UserRepository::find_by_name_or_blow(name)); 15 | PullRepo { user: user } 16 | } 17 | 18 | fn repo_dir(&self) -> Result { 19 | match self.user { 20 | None => { Ok(String::new()) }, // TODO this should pull all users 21 | Some(ref user) => { user.home_dir() } 22 | } 23 | } 24 | 25 | } 26 | 27 | impl AliasCommand for PullRepo { 28 | 29 | fn execute(&self) -> CommandResponse { 30 | match self.repo_dir() { 31 | Err(error_message) => { 32 | CommandResponse::new(1, Some(error_message.to_string())) 33 | }, 34 | Ok(repo_dir) => { 35 | let result = Command::new("git") 36 | .arg("pull") 37 | .current_dir(repo_dir) 38 | .output() 39 | .expect("Error! Failed to pull repo"); 40 | if result.status.success() { 41 | CommandResponse::success() 42 | } else { 43 | CommandResponse::new(1, Some(String::from("an error occurred trying to pull the repo"))) 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/aliases/commands/rehash/mod.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | use std::fs; 3 | use scoped_pool::Pool; 4 | 5 | use aliases::factories::ShimFileFactory; 6 | use aliases::repositories::AliasRepository; 7 | use aliases::commands::{AliasCommand, CommandResponse}; 8 | 9 | pub struct Rehash { 10 | pub shim_directory: PathBuf, 11 | pub alias_directories: Vec, 12 | } 13 | 14 | impl Rehash { 15 | 16 | pub fn new(shim_directory: PathBuf, alias_directories: Vec) -> Rehash { 17 | Rehash { 18 | shim_directory: shim_directory, 19 | alias_directories: alias_directories, 20 | } 21 | } 22 | 23 | 24 | //-------- private ----------// 25 | 26 | fn clean_shims_directory(&self) { 27 | let _ = fs::remove_dir_all(&self.shim_directory); 28 | let _ = fs::create_dir_all(&self.shim_directory); 29 | } 30 | } 31 | 32 | impl AliasCommand for Rehash { 33 | 34 | fn execute(&self) -> CommandResponse { 35 | self.clean_shims_directory(); 36 | let pool = Pool::new(4); 37 | for dir in &self.alias_directories { 38 | match AliasRepository::find_for_directory(&dir.to_str().unwrap().to_string()) { 39 | Err(_) => {}, 40 | Ok(aliases) => { 41 | pool.scoped(|scope| { 42 | for alias in aliases { 43 | scope.execute(move || { 44 | ShimFileFactory::create(&alias, &self.shim_directory); 45 | }); 46 | } 47 | }); 48 | }, 49 | } 50 | } 51 | CommandResponse::success() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/aliases/commands/remove.rs: -------------------------------------------------------------------------------- 1 | use aliases::repositories::AliasFileRepository; 2 | use aliases::models::Alias; 3 | use std::path::PathBuf; 4 | use std::io::Write; 5 | 6 | macro_rules! println_stderr( 7 | ($($arg:tt)*) => { { 8 | let result = writeln!(&mut ::std::io::stderr(), $($arg)*); 9 | result.expect("failed printing to stderr"); 10 | } } 11 | ); 12 | 13 | pub struct Remove { 14 | directory: PathBuf, 15 | name: String 16 | } 17 | 18 | impl Remove { 19 | 20 | pub fn new(directory: PathBuf, name: Option<&str>) -> Self { 21 | Remove { 22 | directory: directory, 23 | name: name.unwrap().to_string() 24 | } 25 | } 26 | 27 | pub fn execute(&self) -> i32 { 28 | let mut alias_file = AliasFileRepository::find(&self.directory); 29 | match alias_file.remove_alias(self.build_alias()) { 30 | Ok(_) => { 31 | AliasFileRepository::save(alias_file); 32 | 0 33 | } 34 | Err(_) => { 35 | println_stderr!("Could not find alias \"{}\" in directory {:?}", self.build_alias().name, self.directory); 36 | 1 37 | } 38 | } 39 | } 40 | 41 | //----- private -----// 42 | 43 | fn build_alias(&self) -> Alias { 44 | let mut alias = Alias::new(); 45 | alias.name = self.name.clone(); 46 | alias 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/aliases/commands/test.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/aliases/commands/users.rs: -------------------------------------------------------------------------------- 1 | use aliases::Config; 2 | use aliases::commands::{AliasCommand, CommandResponse}; 3 | 4 | pub struct Users { 5 | config: Config, 6 | } 7 | 8 | impl Users { 9 | 10 | pub fn new(config: Config) -> Self { 11 | Users { config: config } 12 | } 13 | 14 | } 15 | 16 | impl AliasCommand for Users { 17 | 18 | fn execute(&self) -> CommandResponse { 19 | println!("Users"); 20 | println!("-----"); 21 | println!("{:?}", self.config.users()); 22 | CommandResponse::success() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/aliases/config.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::{PathBuf}; 3 | use std::io::prelude::*; 4 | use std::fs::File; 5 | use std::process::Command; 6 | use rustc_serialize::json; 7 | use std::io; 8 | 9 | #[derive(Clone, RustcDecodable, RustcEncodable)] 10 | pub struct Config { 11 | pub shim_directory: String, 12 | alias_directories: Vec, 13 | users: Vec, 14 | disabled_users: Option>, 15 | } 16 | 17 | impl Config { 18 | 19 | pub fn load() -> Config { 20 | if Config::config_file_path().exists() { 21 | Config::load_file(&Config::config_file_path()) 22 | } else { 23 | Config::create(&Config::config_file_path()) 24 | } 25 | } 26 | 27 | pub fn shim_path(&self) -> PathBuf { 28 | let mut command = String::from("echo \""); 29 | command.push_str(&self.shim_directory); 30 | command.push_str("\""); 31 | let output = Command::new("bash") 32 | .arg("-c") 33 | .arg(&command) 34 | .output() 35 | .unwrap_or_else(|e| { panic!("failed to execute child: {}", e) }); 36 | 37 | PathBuf::from(String::from_utf8(output.stdout).unwrap().trim()) 38 | } 39 | 40 | pub fn alias_paths(&self) -> Vec { 41 | self.alias_directories.iter().map(|dir| PathBuf::from(dir)).collect() 42 | } 43 | 44 | pub fn directories(&self) -> Vec { 45 | self.alias_directories.clone() 46 | } 47 | 48 | pub fn users(&self) -> Vec { 49 | self.users.clone() 50 | } 51 | 52 | pub fn update_users(&mut self, users: Vec) { 53 | self.users = users; 54 | self.users.dedup(); 55 | self.update_file(); 56 | } 57 | 58 | pub fn enable_user(&mut self, username: &str) -> Result<(), io::Error> { 59 | match self.disabled_users.clone() { 60 | Some(users) => { 61 | self.disabled_users = Some(users.clone().into_iter().filter(|user| user != username).collect()); 62 | let mut users = self.users(); 63 | users.push(username.to_string()); 64 | self.update_users(users); 65 | }, 66 | None => { 67 | self.disabled_users = Some(vec![]); 68 | } 69 | } 70 | self.update_file() 71 | } 72 | 73 | pub fn disabled_users(&self) -> Vec { 74 | match self.disabled_users.clone() { 75 | Some(users) => { users }, 76 | None => { vec![] } 77 | } 78 | 79 | } 80 | 81 | pub fn disable_user(&mut self, username: &str) { 82 | match self.disabled_users.clone() { 83 | Some(ref mut users) => { 84 | users.push(username.to_string()); 85 | self.disabled_users = Some(users.clone()); 86 | }, 87 | None => { self.disabled_users = Some(vec![username.to_string()]); } 88 | }; 89 | self.update_file(); 90 | } 91 | 92 | pub fn add_alias_directory(&mut self, directory: &PathBuf, username: &String) { 93 | let string = directory.to_str().unwrap().to_owned(); 94 | self.alias_directories.push(string); 95 | self.alias_directories.dedup(); 96 | self.users.push(username.to_owned()); 97 | self.users.dedup(); 98 | self.update_file(); 99 | } 100 | 101 | pub fn set_user_priority(&mut self, username: &String, priority: usize) -> Result<(), String> { 102 | match self.users().into_iter().position(|user| {user == username.to_owned()}) { 103 | Some(index) => { 104 | let mut users = self.users(); 105 | let user = users.remove(index); 106 | users.insert(priority - 1, user); 107 | self.update_users(users); 108 | Ok(()) 109 | }, 110 | None => Err(format!("Error! Could not find the user {}.", username)), 111 | } 112 | } 113 | 114 | // ------- private methods --------// 115 | 116 | fn config_file_path() -> PathBuf { 117 | match env::var("HOME") { 118 | Ok(home_dir) => { 119 | PathBuf::from(home_dir).join(".aliases_cfg") 120 | }, 121 | Err(_) => { 122 | PathBuf::new() // TODO need to handle this better 123 | }, 124 | } 125 | } 126 | 127 | fn create(path: &PathBuf) -> Config { 128 | let mut file = File::create(path).unwrap(); //TODO handle the error case 129 | let default_config = TemplateRepository::config_template(); 130 | file.write_all(default_config.as_bytes()).unwrap(); 131 | let config: Config = json::decode(&default_config).unwrap(); 132 | config 133 | } 134 | 135 | fn load_file(path: &PathBuf) -> Config { 136 | let mut file = File::open(path).unwrap(); //TODO handle the error case 137 | let mut content = String::new(); 138 | let _ = file.read_to_string(&mut content); 139 | let config: Config = json::decode(&content).unwrap(); 140 | config 141 | } 142 | 143 | fn update_file(&self) -> Result<(), io::Error> { 144 | let mut file = try!(File::create(Self::config_file_path())); 145 | let encoded = json::encode(&self).unwrap(); 146 | try!(file.write_all(encoded.as_bytes())); 147 | Ok(()) 148 | } 149 | } 150 | 151 | struct TemplateRepository; 152 | 153 | impl TemplateRepository { 154 | 155 | pub fn config_template() -> String { 156 | "{ 157 | \"shim_directory\" : \"${HOME}/.aliases.d/shims\", 158 | \"alias_directories\" : [], 159 | \"users\" : [\"default\"] 160 | } 161 | ".to_string() 162 | } 163 | } 164 | 165 | -------------------------------------------------------------------------------- /src/aliases/execution_workflow.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use countdown::Countdown; 4 | use aliases::models::Alias; 5 | 6 | macro_rules! println_stderr( 7 | ($($arg:tt)*) => { { 8 | let result = writeln!(&mut ::std::io::stderr(), $($arg)*); 9 | result.expect("failed printing to stderr"); 10 | } } 11 | ); 12 | 13 | pub struct ExecutionWorkflow { 14 | alias: Alias, 15 | } 16 | 17 | impl ExecutionWorkflow { 18 | 19 | pub fn new(alias: Alias) -> Self { 20 | ExecutionWorkflow { alias: alias } 21 | } 22 | 23 | pub fn execute(&self) { 24 | if self.conditional_passes() { 25 | if self.user_confirmation_successful() { 26 | self.allow_for_backout(); 27 | self.output_command_to_be_executed(); 28 | self.execute_command(); 29 | } 30 | } else { 31 | // TODO alert the user 32 | } 33 | } 34 | 35 | //------------- private -----------// 36 | 37 | fn conditional_passes(&self) -> bool { 38 | self.alias.conditional.execute() 39 | } 40 | 41 | fn user_confirmation_successful(&self) -> bool { 42 | self.alias.user_confirmation.execute() 43 | } 44 | 45 | fn allow_for_backout(&self) { 46 | if self.alias.delayed_backout > 0 { 47 | println_stderr!("Executing '{}' in {} seconds", self.alias.command(), self.alias.delayed_backout); 48 | println_stderr!("Press ctrl + c to cancel execution."); 49 | Countdown::new(self.alias.delayed_backout.clone()).start(); 50 | } 51 | } 52 | 53 | fn output_command_to_be_executed(&self) { 54 | if !self.alias.quiet { 55 | println_stderr!("Executing: {}", self.alias.command()); 56 | } 57 | } 58 | 59 | fn execute_command(&self) { 60 | self.alias.execute(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/aliases/factories/alias_factory.rs: -------------------------------------------------------------------------------- 1 | use aliases::collections::Aliases; 2 | use aliases::builders::AliasBuilder; 3 | 4 | use std::path::PathBuf; 5 | use std::io::prelude::*; 6 | use std::fs::File; 7 | use std::result::Result; 8 | use yaml_rust::{YamlLoader, Yaml}; 9 | 10 | pub struct AliasFactory; 11 | 12 | impl AliasFactory { 13 | 14 | pub fn create_empty() -> Aliases { 15 | Aliases::new(vec![]) 16 | } 17 | 18 | pub fn create_from_file(data_file: PathBuf) -> Result { 19 | match AliasFactory::parse_file(&data_file) { 20 | Err(error_string) => { Err(error_string) } 21 | Ok(yaml) => { 22 | match yaml.as_hash() { 23 | None => { Ok(Aliases::new(vec![])) }, 24 | Some(hash) => { 25 | let mut aliases = vec![]; 26 | let basename = data_file.as_path().parent().unwrap().to_path_buf(); // TODO handle this right?? 27 | for (command_name, command_args) in hash { 28 | match AliasBuilder::from_yaml(command_name.as_str().unwrap(), basename.clone(), command_args.clone()).build() { 29 | Err(error_string) => { println!("Unabled to create alias '{:?}' in file '{:?}', error given: {}", command_name, data_file.as_path(), error_string); }, 30 | Ok(alias) => { aliases.push(alias) }, 31 | } 32 | } 33 | Ok(Aliases::new(aliases)) 34 | }, 35 | } 36 | } 37 | } 38 | } 39 | 40 | pub fn create_from_files(data_files: Vec) -> Aliases { 41 | let aliases = AliasFactory::create_empty(); 42 | for data_file in data_files { 43 | match AliasFactory::create_from_file(data_file) { 44 | Err(_) => {}, 45 | Ok(a) => { aliases.merge(a); } 46 | } 47 | } 48 | aliases 49 | } 50 | 51 | // ------------ private methods --------- // 52 | 53 | fn parse_file(data_file: &PathBuf) -> Result { 54 | match File::open(data_file) { 55 | Err(_) => Err("File did not exist."), 56 | Ok(mut file) => { 57 | let mut file_contents = String::new(); 58 | match file.read_to_string(&mut file_contents) { 59 | Err(_) => Err("Error reading the file."), 60 | Ok(_) => { 61 | match YamlLoader::load_from_str(&file_contents) { 62 | Ok(yaml) => { 63 | match yaml.len() { 64 | 0 => Ok(Yaml::Null), 65 | _ => Ok(yaml[0].clone()) 66 | } 67 | }, 68 | Err(_) => Err("Could not parse the yaml.") 69 | } 70 | } 71 | } 72 | }, 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/aliases/factories/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::alias_factory::AliasFactory; 2 | 3 | use aliases::models::Alias; 4 | use std::path::PathBuf; 5 | use std::io::prelude::*; 6 | use std::fs::File; 7 | use crypto::md5::Md5; 8 | use crypto::digest::Digest; 9 | use std::process::Command; 10 | 11 | mod alias_factory; 12 | 13 | pub struct ShimFileFactory; 14 | 15 | impl ShimFileFactory { 16 | 17 | pub fn create(alias: &Alias, dir: &PathBuf) { 18 | // TODO this is not deleting old shims 19 | let filepath = dir.join(alias.name.clone()); 20 | if !filepath.exists() { 21 | info!("Creating new alias '{}'", &alias.name); 22 | match File::create(&filepath) { 23 | Err(error) => { 24 | warn!("An error occurred {} : {:?}", error, &filepath); 25 | }, 26 | Ok(mut file) => { 27 | let _ = file.write_all(&ShimFileFactory::template_string().into_bytes()); 28 | let mut command = String::from("chmod +x "); 29 | command.push_str(filepath.to_str().unwrap()); 30 | // this can fail if there are too many files open we should wait and try again 31 | let _ = Command::new("bash") 32 | .arg("-c") 33 | .arg(&command) 34 | .output() 35 | .unwrap_or_else(|e| { panic!("failed to execute child: {}", e) }); 36 | info!("Successfully created alias '{}' in location {:?}", &alias.name, &filepath); 37 | } 38 | } 39 | } else { 40 | info!("Alias already exists '{}' checking if it's still valid", &alias.name); 41 | if !ShimFileFactory::is_valid(&filepath) { 42 | info!("'{}' not valid, recreating...", &alias.name); 43 | match File::create(&filepath) { 44 | Err(error) => { 45 | warn!("An error occurred {} : {:?}", error, filepath); 46 | }, 47 | Ok(mut file) => { 48 | let _ = file.write_all(&ShimFileFactory::template_string().into_bytes()); 49 | info!("Successfully created alias '{}' in location {:?}", &alias.name, &filepath); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | 56 | pub fn is_valid(file_path: &PathBuf) -> bool { 57 | match File::open(file_path) { 58 | Err(_) => { false }, // TODO handle this properly 59 | Ok(mut file) => { 60 | let mut actual_content = String::new(); 61 | let _ = file.read_to_string(&mut actual_content); 62 | let actual_md5 = ShimFileFactory::md5_for_string(actual_content); 63 | let expected_md5 = ShimFileFactory::md5_for_string(ShimFileFactory::build()); 64 | actual_md5 == expected_md5 65 | } 66 | } 67 | } 68 | 69 | // ------- private methods ---------- // 70 | 71 | fn build() -> String { 72 | ShimFileFactory::template_string() 73 | } 74 | 75 | fn template_string() -> String { 76 | "#!/usr/bin/env bash 77 | set -e 78 | 79 | COMMAND_NAME=\"$(exec basename \"$0\")\" 80 | 81 | if ! hash aliases 2>/dev/null; then 82 | echo \"aliases command doesn't exists, can't continue\" 83 | exit 1 84 | fi 85 | 86 | if aliases list --local --name \"$COMMAND_NAME\" >/dev/null 2>&1; then 87 | aliases exec \"$(pwd -P)\" \"$COMMAND_NAME\" -- \"$@\" 88 | elif aliases list --directory \"$HOME\" --name \"$COMMAND_NAME\" >/dev/null 2>&1; then 89 | aliases exec \"$HOME\" \"$COMMAND_NAME\" -- \"$@\" 90 | else 91 | PATH=${PATH/$HOME\\/.aliases.d\\/shims:/} # remove shims from path 92 | 93 | if hash $COMMAND_NAME 2>/dev/null; then 94 | exec \"$COMMAND_NAME\" \"$@\" 95 | else 96 | echo \"No alias '$COMMAND_NAME' available in this directory\" 97 | exit 1 98 | fi 99 | 100 | fi 101 | ".to_string() 102 | } 103 | 104 | fn md5_for_string(string: String) -> Vec { 105 | let mut md5 = Md5::new(); 106 | md5.input(&string.into_bytes()); 107 | let mut output = String::from("here is my string").into_bytes(); // TODO I know this is bad 108 | md5.result(&mut output); 109 | output 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/aliases/factories/shim_file_factory.rs: -------------------------------------------------------------------------------- 1 | //pub struct ShimFileFactory; 2 | -------------------------------------------------------------------------------- /src/aliases/git.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::process::Command; 3 | use std::path::Path; 4 | 5 | pub struct Git; 6 | 7 | impl Git { 8 | 9 | pub fn clone(url: String, output_dir: &String) -> Result<(), String> { 10 | Self::create_parent_directories(output_dir); 11 | match Self::git_clone(url, output_dir) { 12 | Err(_) => { return Err(String::from("An error occurred")); }, // TODO handle this error case better 13 | Ok(_) => { 14 | Ok(()) 15 | } 16 | } 17 | } 18 | 19 | fn create_parent_directories(output_dir: &String) { 20 | let parent_dir = Path::new(output_dir).parent().unwrap(); 21 | fs::create_dir_all(parent_dir); // TODO handle the result of this 22 | } 23 | 24 | fn git_clone(repo_url: String, output_directory: &str) -> Result<(), &str> { 25 | let result = Command::new("git") 26 | .arg("clone") 27 | .arg(repo_url) 28 | .arg(output_directory) 29 | .output() 30 | .expect("failed to clone repo"); 31 | if result.status.success() { 32 | Ok(()) 33 | } else { 34 | Err("an error occurred trying to clone the repo") // TODO improve this error message most likely repo doesn't exist 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/aliases/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod commands; 2 | pub mod views; 3 | pub mod collections; 4 | pub mod models; 5 | pub mod factories; 6 | pub mod builders; 7 | 8 | mod repositories; 9 | mod app; 10 | mod config; 11 | mod execution_workflow; 12 | mod git; 13 | 14 | pub use self::execution_workflow::ExecutionWorkflow; 15 | pub use self::config::Config; 16 | pub use self::app::App; 17 | pub use self::git::Git; 18 | -------------------------------------------------------------------------------- /src/aliases/models/alias.rs: -------------------------------------------------------------------------------- 1 | use aliases::models::{Conditional, UserConfirmation}; 2 | use aliases::builders::CommandBuilder; 3 | 4 | use std::path::PathBuf; 5 | 6 | #[derive(PartialOrd,Ord,PartialEq,Eq,Debug,Clone)] 7 | pub struct Alias { 8 | pub name: String, 9 | pub command: String, 10 | pub args: Vec, 11 | pub enable_positional_arguments: bool, 12 | pub confirm: bool, 13 | pub confirmation_message: String, 14 | pub conditional: Conditional, 15 | pub user_confirmation: UserConfirmation, 16 | pub delayed_backout: usize, 17 | pub unit_test: String, 18 | pub basename: PathBuf, 19 | pub quiet: bool, 20 | } 21 | 22 | impl Alias { 23 | 24 | pub fn new() -> Alias { 25 | Alias { 26 | name: String::new(), 27 | command: String::new(), 28 | confirm: false, 29 | confirmation_message: String::new(), 30 | user_confirmation: UserConfirmation::new(false, String::new()), 31 | delayed_backout: 0, 32 | conditional: Conditional::default(), 33 | unit_test: String::from("true"), 34 | basename: PathBuf::new(), 35 | args: vec![], 36 | enable_positional_arguments: false, 37 | quiet: false, 38 | } 39 | } 40 | 41 | pub fn execute(&self) { 42 | let mut command = CommandBuilder::using_bash() 43 | .for_command(&self.command) 44 | .with_args(&self.args) 45 | .build(); 46 | 47 | let mut process = command.spawn().unwrap_or_else(|e| { panic!("failed to execute child: {}", e) }); 48 | let _ = process.wait() 49 | .unwrap_or_else(|e| { panic!("failed to wait on child: {}", e) }); 50 | } 51 | 52 | pub fn command(&self) -> String { 53 | let command = CommandBuilder::using_bash() 54 | .for_command(&self.command) 55 | .with_args(&self.args) 56 | .pseudo_build(); 57 | command.command_string 58 | } 59 | 60 | pub fn add_arguments(&mut self, arguments: Vec) { 61 | self.args = arguments.clone(); 62 | } 63 | 64 | pub fn as_yaml(&self) -> String { 65 | AliasYamlBuilder::new(&self).build() 66 | } 67 | } 68 | 69 | struct AliasYamlBuilder<'a> { 70 | alias: &'a Alias, 71 | } 72 | 73 | impl<'a> AliasYamlBuilder<'a> { 74 | 75 | pub fn new(alias: &'a Alias) -> Self { 76 | AliasYamlBuilder { alias: alias } 77 | } 78 | 79 | pub fn build(&self) -> String { 80 | let output = self.build_initial_string(); 81 | //self.add_confirm(&mut output); 82 | //self.add_confirmation_message(&mut output); 83 | //self.add_conditional(&mut output); 84 | //self.add_backout_seconds(&mut output); 85 | //self.add_unit_test(&mut output); 86 | //self.add_quiet(&mut output); 87 | output 88 | //#alias_name: 89 | //# command: ./super_command.sh # required 90 | //# confirm: true # optional 91 | //# confirmation_message: Are you sure you are sure?? # optional 92 | //# conditional: /bin/true # optional 93 | //# backout_seconds: 3 # optional 94 | //# unit_test: '[ true = true ]' # optional 95 | //# quiet: false # optional 96 | } 97 | 98 | //-------- private ------// 99 | 100 | fn build_initial_string(&self) -> String { 101 | format!("\n{}:\n command: {}\n", self.alias.name, self.alias.command) 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/aliases/models/alias_file.rs: -------------------------------------------------------------------------------- 1 | use aliases::models::Alias; 2 | use aliases::collections::Aliases; 3 | use std::path::PathBuf; 4 | use yaml_rust::{YamlEmitter}; 5 | 6 | pub struct AliasFile { 7 | pub path: PathBuf, 8 | aliases: Aliases, 9 | } 10 | 11 | impl AliasFile { 12 | 13 | pub fn new(path: PathBuf, aliases: Aliases) -> Self { 14 | AliasFile { path: path, aliases: aliases } 15 | } 16 | 17 | pub fn add_alias(&mut self, alias: Alias) { 18 | self.aliases.push(&alias); 19 | } 20 | 21 | pub fn remove_alias(&mut self, alias: Alias) -> Result<(), &'static str> { 22 | self.aliases.remove(&alias) 23 | } 24 | 25 | pub fn as_bytes(&self) -> Vec { 26 | let mut output = self.header_content(); 27 | { 28 | let mut emitter = YamlEmitter::new(&mut output); 29 | emitter.dump(&self.aliases.to_yaml()).unwrap(); 30 | } 31 | output.push_str("\n"); 32 | output.into_bytes() 33 | } 34 | 35 | //------- private --------// 36 | 37 | fn header_content(&self) -> String { 38 | String::from("# This file is autogenerated by the aliases tool. 39 | # For more info about aliases type `aliases --help` 40 | # or visit https://github.com/sebglazebrook/aliases 41 | 42 | ") 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/aliases/models/conditional.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | #[derive(PartialOrd,Ord,PartialEq,Eq,Debug,Clone)] 4 | pub struct Conditional { 5 | command: String, 6 | } 7 | 8 | impl Conditional { 9 | 10 | pub fn new(command: String) -> Self { 11 | Conditional { command: command } 12 | } 13 | 14 | pub fn default() -> Self { 15 | Conditional { command: String::from("true") } 16 | } 17 | 18 | pub fn execute(&self) -> bool { 19 | let status = Command::new("bash") 20 | .arg("-c") 21 | .arg(&self.command) 22 | .status() 23 | .unwrap_or_else(|e| { panic!("failed to execute child: {}", e) }); 24 | status.success() 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/aliases/models/mod.rs: -------------------------------------------------------------------------------- 1 | mod alias; 2 | mod alias_file; 3 | mod user; 4 | mod conditional; 5 | mod user_confirmation; 6 | 7 | pub use self::alias::Alias; 8 | pub use self::alias_file::AliasFile; 9 | pub use self::user::User; 10 | pub use self::conditional::Conditional; 11 | pub use self::user_confirmation::UserConfirmation; 12 | -------------------------------------------------------------------------------- /src/aliases/models/user.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | use std::io; 4 | use std::os::unix; 5 | 6 | use aliases::repositories::AliasFileRepository; 7 | use aliases::{Config, Git}; 8 | 9 | #[derive(PartialEq,Eq,Debug,Clone)] 10 | pub struct User { 11 | name: String, 12 | filename: String, 13 | enabled: bool, 14 | } 15 | 16 | impl User { 17 | 18 | pub fn new(name: String, enabled: bool) -> Self { 19 | let filename = match name.as_ref() { 20 | "default" => String::from(".aliases"), 21 | _ => format!(".aliases-{}", &name), 22 | }; 23 | User { 24 | filename: filename, 25 | name, 26 | enabled 27 | } 28 | } 29 | 30 | pub fn filename(&self) -> String { 31 | self.filename.clone() 32 | } 33 | 34 | pub fn is_enabled(&self) -> bool { 35 | self.enabled 36 | } 37 | 38 | pub fn confirm_name(&self, other_name: &str) -> bool { 39 | self.name == other_name 40 | } 41 | 42 | pub fn home_dir(&self) -> Result { 43 | match env::var("HOME") { 44 | Err(_) => { Err("Error! Could not evaluate env var $HOME. Can't continue.") }, 45 | Ok(home_dir) => { 46 | Ok(format!("{}/.aliases.d/users/{}", home_dir, self.name)) 47 | }, 48 | } 49 | } 50 | 51 | pub fn init_directory(&self, target_dir: &PathBuf) -> Result<(), io::Error> { 52 | try!(AliasFileRepository::create(&target_dir, &self.filename)); 53 | Config::load().add_alias_directory(&target_dir, &self.name); 54 | Ok(()) 55 | } 56 | 57 | pub fn enable(&self) -> Result<(), io::Error> { 58 | Config::load().enable_user(&self.name) 59 | } 60 | 61 | pub fn disable(&self) { 62 | Config::load().disable_user(&self.name); 63 | } 64 | 65 | pub fn set_priority(&self, position: usize) -> Result<(), String> { 66 | Config::load().set_user_priority(&self.name, position) 67 | } 68 | 69 | pub fn clone_external_repo(&self, url: Option) -> Result<(), String> { 70 | let external_url = url.unwrap_or(self.default_external_url()); 71 | match Git::clone(external_url, &self.output_directory()) { 72 | Err(message) => Err(message), 73 | Ok(_) => { 74 | self.link_aliases_file(); 75 | Ok(()) 76 | } 77 | } 78 | } 79 | 80 | fn default_external_url(&self) -> String { 81 | format!("https://github.com/{}/dot-aliases", self.name) 82 | } 83 | 84 | fn output_directory(&self) -> String { 85 | self.output_base_dir() + "/" + &self.name 86 | } 87 | 88 | fn output_base_dir(&self) -> String { 89 | match env::var("HOME") { 90 | Err(_) => { String::from("No $HOME environment variable set. Don't know your home directory") }, 91 | Ok(home_dir) => { 92 | format!("{}/.aliases.d/users", home_dir) 93 | }, 94 | } 95 | } 96 | 97 | fn link_aliases_file(&self) { 98 | let target_file = self.output_directory() + "/.aliases"; 99 | let destination_file = env::var("HOME").unwrap().to_string() + &self.filename; // TODO handle this better; 100 | unix::fs::symlink(target_file, destination_file); // TODO handle the result of this and what about if it's not unix? 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/aliases/models/user_confirmation.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Write}; 2 | 3 | #[derive(PartialOrd,Ord,PartialEq,Eq,Debug,Clone)] 4 | pub struct UserConfirmation { 5 | enabled: bool, 6 | message: String, 7 | } 8 | 9 | impl UserConfirmation { 10 | 11 | pub fn new(enabled: bool, message: String) -> Self { 12 | UserConfirmation { enabled: enabled, message: message } 13 | } 14 | 15 | pub fn execute(&self) -> bool { 16 | if self.enabled { 17 | print!("{}. Type 'Yes' to continue... ", self.message); 18 | io::stdout().flush().unwrap(); 19 | let mut user_input = String::new(); 20 | let _ = io::stdin().read_line(&mut user_input); // TODO potential error to be handled here 21 | user_input.trim() == "Yes" 22 | } else { 23 | true 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/aliases/repositories/alias_file_repository.rs: -------------------------------------------------------------------------------- 1 | use aliases::models::AliasFile; 2 | use aliases::factories::AliasFactory; 3 | 4 | use std::path::{Path, PathBuf}; 5 | use std::io::prelude::*; 6 | use std::fs::File; 7 | use std::io; 8 | 9 | pub struct AliasFileRepository; 10 | 11 | impl AliasFileRepository { 12 | 13 | // TODO need to handle different users 14 | pub fn find(directory: &PathBuf) -> AliasFile { 15 | let aliases_filename = String::from(".aliases"); 16 | Self::create(directory, &aliases_filename); 17 | let new_file = directory.join(&aliases_filename); 18 | let aliases = AliasFactory::create_from_file(new_file); 19 | AliasFile::new(directory.clone(), aliases.unwrap()) // TODO 20 | } 21 | 22 | pub fn create(directory: &PathBuf, username: &String) -> Result<(), io::Error> { 23 | if !Path::new(&directory.join(&username)).exists() { 24 | let filepath = directory.join(&username); 25 | let mut new_file = File::create(filepath).unwrap(); 26 | let template_string = Self::template_string(); 27 | let array = template_string.as_bytes(); 28 | try!(new_file.write_all(array)); 29 | } 30 | Ok(()) 31 | } 32 | 33 | pub fn save(alias: AliasFile) { 34 | let mut new_file = File::create(&alias.path.join(".aliases")).unwrap(); // TODO need to check if it's for a user or not 35 | let _ = new_file.write_all(&alias.as_bytes()); 36 | } 37 | 38 | fn template_string() -> String { 39 | String::from("# This file is auto-generated by the aliases tool. 40 | # For more info about aliases type `aliases --help` 41 | # or visit https://github.com/sebglazebrook/aliases 42 | 43 | #alias_name: 44 | # command: ./super_command.sh # required 45 | # confirm: true # optional 46 | # confirmation_message: Are you sure you are sure?? # optional 47 | # conditional: /bin/true # optional 48 | # backout_seconds: 3 # optional 49 | # unit_test: '[ true = true ]' # optional 50 | # quiet: false # optional 51 | ") 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/aliases/repositories/alias_repository.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | use aliases::factories::AliasFactory; 3 | use aliases::collections::Aliases; 4 | use aliases::repositories::UserRepository; 5 | use aliases::models::User; 6 | 7 | pub struct AliasRepository; 8 | 9 | impl AliasRepository { 10 | 11 | pub fn find_for_directory(directory: &String) -> Result { 12 | let mut aliases = Aliases::new(vec![]); // TODO should be able to use map or inject here 13 | for user in Self::available_users().iter() { 14 | match Self::directory_aliases_for_user(directory, &user) { 15 | None => { }, 16 | Some(user_aliases) => { aliases = aliases.merge(user_aliases); } 17 | } 18 | } 19 | Ok(aliases) 20 | } 21 | 22 | //--------- private ------------// 23 | 24 | fn available_users() -> Vec { 25 | UserRepository::enabled() 26 | } 27 | 28 | fn directory_aliases_for_user(directory: &String, user: &User) -> Option { 29 | let aliases_file = PathBuf::from(directory).join(&user.filename()); // TODO use the user's file name 30 | match AliasFactory::create_from_file(aliases_file) { 31 | Err(_) => { None }, 32 | Ok(aliases) => { Some(aliases) } 33 | } 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/aliases/repositories/mod.rs: -------------------------------------------------------------------------------- 1 | mod alias_repository; 2 | mod user_repository; 3 | mod alias_file_repository; 4 | 5 | pub use self::alias_repository::AliasRepository; 6 | pub use self::user_repository::UserRepository; 7 | pub use self::alias_file_repository::AliasFileRepository; 8 | -------------------------------------------------------------------------------- /src/aliases/repositories/user_repository.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use aliases::Config; 4 | use aliases::models::User; 5 | 6 | pub struct UserRepository; 7 | 8 | impl UserRepository { 9 | 10 | pub fn default() -> User { 11 | Self::all().iter().find(|user| user.confirm_name("default") ).map(|user| user.to_owned()).unwrap() 12 | } 13 | 14 | pub fn find_by_name_or_blow(username: &str) -> User { 15 | Self::find_by_name(username).expect("Error! Could not find the user") 16 | } 17 | 18 | pub fn find_by_name(username: &str) -> Option { 19 | Self::all().iter().find(|user| user.confirm_name(username) ).map(|user| user.to_owned()) 20 | } 21 | 22 | pub fn find_by_name_or_create(username: &str) -> User { 23 | match Self::find_by_name(&username) { 24 | Some(user) => { user } 25 | None => { User::new(username.to_string(), true) } 26 | } 27 | } 28 | 29 | pub fn enabled() -> Vec { 30 | Self::all().iter() 31 | .filter(|user| user.is_enabled()) 32 | .cloned() 33 | .collect() 34 | } 35 | 36 | pub fn all() -> Vec { 37 | Config::load().users().into_iter().fold(Self::trumping_users(), |mut acc, username| { 38 | let enabled = Self::is_user_enabled(&username); 39 | let user = User::new(username, enabled); 40 | if !(Self::trumping_users().contains(&user)) { 41 | acc.push(user); 42 | } 43 | acc 44 | }) 45 | } 46 | 47 | 48 | //------------- private -----------// 49 | 50 | fn trumping_users() -> Vec { 51 | match env::var("ALIASES_USER") { 52 | Err(_) => { vec![] }, 53 | Ok(username) => { 54 | vec![User::new(String::from(username), true)] 55 | } 56 | } 57 | } 58 | 59 | fn is_user_enabled(username: &str) -> bool { 60 | let disabled_users = Config::load().disabled_users(); 61 | !disabled_users.iter().any(|&ref u| u == username) 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/aliases/views/aliases.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebglazebrook/aliases/dcc317da9730ca81cb26917affc6fb9d6f511493/src/aliases/views/aliases.rs -------------------------------------------------------------------------------- /src/aliases/views/mod.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::io::Write; 3 | use tabwriter::TabWriter; 4 | 5 | use aliases::collections::Aliases; 6 | 7 | pub struct AliasesView { 8 | aliases: Aliases, 9 | } 10 | 11 | impl AliasesView { 12 | 13 | pub fn new(aliases: Aliases) -> Self { 14 | AliasesView { aliases: aliases } 15 | } 16 | 17 | pub fn render(&self) { 18 | let mut tw = TabWriter::new(io::stdout()); 19 | tw.write("\nALIAS\tCOMMAND\tCONFIRM\n".as_bytes()).unwrap(); 20 | for alias in self.aliases.clone().into_iter() { 21 | let alias_row = String::new() + &alias.name + "\t" 22 | + &alias.command + "\t" 23 | + &alias.confirm.to_string() + "\n"; 24 | tw.write(alias_row.as_bytes()).unwrap(); 25 | } 26 | tw.flush().unwrap(); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate yaml_rust; 4 | extern crate crypto; 5 | extern crate rustc_serialize; 6 | extern crate tabwriter; 7 | extern crate countdown; 8 | extern crate crossbeam; 9 | extern crate regex; 10 | extern crate scoped_pool; 11 | 12 | 13 | mod aliases; 14 | 15 | pub use aliases::App; 16 | 17 | // TODO everything below here shouldn't be here. 18 | // had to do this for the tests, why? 19 | pub use aliases::commands::{Rehash, Exec}; 20 | pub use aliases::builders::AliasBuilder; 21 | pub use aliases::models::{Alias, Conditional}; 22 | pub use aliases::factories::{AliasFactory, ShimFileFactory}; 23 | pub use aliases::collections::Aliases; 24 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate clap; 3 | extern crate aliases; 4 | extern crate env_logger; 5 | 6 | use aliases::App; 7 | 8 | use std::env; 9 | 10 | fn main() { 11 | env_logger::init().unwrap(); 12 | let yaml = load_yaml!("../config/cli.yml"); 13 | let matches = clap::App::from_yaml(yaml).get_matches(); 14 | 15 | match matches.subcommand_name() { 16 | Some("init") => { 17 | if let Some(matches) = matches.subcommand_matches("init") { 18 | App::new().execute_init(matches.is_present("global"), matches.value_of("user")); 19 | } 20 | }, 21 | Some("add") => { 22 | if let Some(matches) = matches.subcommand_matches("add") { 23 | App::new().execute_add(matches.value_of("name"), matches.value_of("command")); 24 | } 25 | }, 26 | Some("remove") => { 27 | if let Some(matches) = matches.subcommand_matches("remove") { 28 | App::new().execute_remove(matches.value_of("name")); 29 | } 30 | }, 31 | Some("directories") => { 32 | App::new().execute_directories(); 33 | }, 34 | Some("list") => { 35 | let home_dir = env::home_dir().unwrap(); 36 | let home_string = home_dir.to_str(); 37 | let current_dir = env::current_dir().unwrap(); 38 | let current_string = current_dir.to_str(); 39 | if let Some(matches) = matches.subcommand_matches("list") { 40 | let mut directory = None; 41 | if let Some(dir) = matches.value_of("directory") { directory = Some(dir); } 42 | if matches.is_present("global") { 43 | directory = home_string; 44 | } 45 | if matches.is_present("local") { 46 | directory = current_string; 47 | } 48 | App::new().execute_list(directory, matches.value_of("name")); 49 | } 50 | }, 51 | Some("rehash") => { 52 | if let Some(_) = matches.subcommand_matches("rehash") { 53 | App::new().execute_rehash(); 54 | } 55 | }, 56 | Some("exec") => { 57 | if let Some(matches) = matches.subcommand_matches("exec") { 58 | let directory = matches.value_of("directory").unwrap().to_string(); 59 | let command_name = matches.value_of("name").unwrap().to_string(); 60 | let mut forwarding_args: Vec<&str> = matches.values_of("name").unwrap().collect(); 61 | forwarding_args.remove(0); 62 | let forwarding_args = forwarding_args.into_iter().map(|arg| arg.to_string() ).collect(); 63 | App::new().execute_exec(directory, command_name, forwarding_args); 64 | } 65 | }, 66 | Some("users") => { 67 | if let Some(subcommand_matches) = matches.subcommand_matches("users") { 68 | match subcommand_matches.subcommand_name() { 69 | Some("move") => { 70 | if let Some(move_matches) = subcommand_matches.subcommand_matches("move") { 71 | let username = move_matches.value_of("username").unwrap().to_string(); 72 | // TODO handle when this isn't an Integer 73 | let prioritization = move_matches.value_of("prioritization").unwrap().parse::().unwrap(); 74 | App::new().prioritize_user(username, prioritization); 75 | } 76 | }, 77 | Some("use") => { 78 | if let Some(move_matches) = subcommand_matches.subcommand_matches("use") { 79 | let username = move_matches.value_of("username").unwrap().to_string(); 80 | App::new().prioritize_user(username, 1); 81 | } 82 | }, 83 | Some("enable") => { 84 | if let Some(move_matches) = subcommand_matches.subcommand_matches("enable") { 85 | let username = move_matches.value_of("username").unwrap().to_string(); 86 | App::new().enable_user(username); 87 | } 88 | }, 89 | Some("disable") => { 90 | if let Some(move_matches) = subcommand_matches.subcommand_matches("disable") { 91 | let username = move_matches.value_of("username").unwrap().to_string(); 92 | App::new().disable_user(username); 93 | } 94 | }, 95 | None => { 96 | App::new().execute_users(); 97 | }, 98 | _ => {}, 99 | } 100 | 101 | } 102 | }, 103 | Some("clone") => { 104 | if let Some(matches) = matches.subcommand_matches("clone") { 105 | App::new().execute_clone( 106 | matches.value_of("username").unwrap().to_string(), 107 | matches.value_of("repo_url"), 108 | matches.is_present("enable") 109 | ); 110 | } 111 | }, 112 | Some("pull") => { 113 | if let Some(matches) = matches.subcommand_matches("pull") { 114 | App::new().execute_pull(matches.value_of("username")); 115 | } 116 | }, 117 | None => { 118 | App::new().execute_list(None, None); 119 | }, 120 | _ => {}, // unknown command - show an error. 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /tests/alias_builder.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin,const_fn)] 2 | #![plugin(stainless)] 3 | 4 | extern crate aliases; 5 | extern crate yaml_rust; 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | 10 | pub use yaml_rust::{YamlLoader}; 11 | pub use aliases::{Alias, AliasBuilder, Conditional}; 12 | pub use std::path::PathBuf; 13 | 14 | describe! alias_builder { 15 | 16 | describe! from_yaml { 17 | 18 | before_each { 19 | let basename = PathBuf::new(); 20 | let yaml_string = 21 | "command: ./super_command.sh 22 | confirm: true 23 | confirmation_message: Are you really really sure?? 24 | conditional: /bin/true 25 | backout_seconds: 3 26 | unit_test: '[ true = true ]' 27 | quiet: true 28 | "; 29 | let docs = YamlLoader::load_from_str(yaml_string).unwrap(); 30 | let doc = &docs[0]; 31 | } 32 | 33 | describe! with_all_the_field_included { 34 | 35 | before_each { 36 | let mut alias = Alias::new(); 37 | match AliasBuilder::from_yaml("command_name", basename.clone(), doc.clone()).build() { 38 | Ok(a) => { alias = a; }, 39 | Err(_) => { }, 40 | }; 41 | } 42 | 43 | it "creates an Alias with all the fields set" { 44 | assert_eq!(alias.name, "command_name"); 45 | assert_eq!(alias.command, "./super_command.sh"); 46 | assert_eq!(alias.confirm, true); 47 | assert_eq!(alias.confirmation_message, "Are you really really sure??"); 48 | assert_eq!(alias.conditional, Conditional::new("/bin/true".to_string())); 49 | assert_eq!(alias.delayed_backout, 3); 50 | assert_eq!(alias.unit_test, "[ true = true ]".to_string()); 51 | assert_eq!(alias.quiet, true); 52 | } 53 | } 54 | 55 | describe! when_there_is_no_name { 56 | } 57 | 58 | describe! when_there_is_no_command { 59 | } 60 | 61 | describe! when_there_is_no_confirmation { 62 | 63 | before_each { 64 | let yaml_string = 65 | "command: ./super_command.sh 66 | unit_test: '[ true == true ]' 67 | conditional: /bin/true 68 | confirmation_message: Are you really really sure?? 69 | "; 70 | let docs = YamlLoader::load_from_str(yaml_string).unwrap(); 71 | let doc = &docs[0]; 72 | } 73 | 74 | it "builds with confirmation turned off" { 75 | let mut alias = Alias::new(); 76 | match AliasBuilder::from_yaml("command_name", basename.clone(), doc.clone()).build() { 77 | Err(_) => {}, 78 | Ok(a) => { alias = a; } 79 | } 80 | assert_eq!(alias.confirm, false); 81 | } 82 | } 83 | 84 | describe! when_there_is_no_confirmation_message { 85 | 86 | before_each { 87 | let yaml_string = 88 | "command: ./super_command.sh 89 | confirm: true 90 | unit_test: '[ true == true ]' 91 | conditional: /bin/true 92 | "; 93 | let docs = YamlLoader::load_from_str(yaml_string).unwrap(); 94 | let doc = &docs[0]; 95 | } 96 | 97 | it "builds with a default confirmation message" { 98 | let mut alias = Alias::new(); 99 | match AliasBuilder::from_yaml("command_name", basename.clone(), doc.clone()).build() { 100 | Err(_) => {}, 101 | Ok(a) => { alias = a; }, 102 | } 103 | assert_eq!(alias.confirmation_message, "About to execute `./super_command.sh`"); 104 | } 105 | } 106 | 107 | describe! when_there_is_no_conditional { 108 | 109 | before_each { 110 | let yaml_string = 111 | "command: ./super_command.sh 112 | confirm: true 113 | confirmation_message: Are you really really sure?? 114 | unit_test: '[ true == true ]' 115 | "; 116 | let docs = YamlLoader::load_from_str(yaml_string).unwrap(); 117 | let doc = &docs[0]; 118 | } 119 | 120 | it "builds without a conditional" { 121 | AliasBuilder::from_yaml("command_name", basename.clone(), doc.clone()).build().is_ok(); 122 | } 123 | } 124 | 125 | describe! when_there_is_no_unit_test { 126 | 127 | before_each { 128 | let yaml_string = 129 | "command: ./super_command.sh 130 | confirm: true 131 | confirmation_message: Are you really really sure?? 132 | conditional: /bin/true 133 | "; 134 | let docs = YamlLoader::load_from_str(yaml_string).unwrap(); 135 | let doc = &docs[0]; 136 | } 137 | 138 | it "builds without a unit test" { 139 | AliasBuilder::from_yaml("command_name", basename.clone(), doc.clone()).build().is_ok(); 140 | } 141 | } 142 | 143 | describe! when_there_is_no_delay_backout { 144 | before_each { 145 | let yaml_string = 146 | "command: ./super_command.sh 147 | confirm: true 148 | confirmation_message: Are you really really sure?? 149 | conditional: /bin/true 150 | "; 151 | let docs = YamlLoader::load_from_str(yaml_string).unwrap(); 152 | let doc = &docs[0]; 153 | } 154 | 155 | it "builds without a delayed backout" { 156 | AliasBuilder::from_yaml("command_name", basename.clone(), doc.clone()).build().is_ok(); 157 | } 158 | } 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /tests/alias_factory.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin,const_fn)] 2 | #![plugin(stainless)] 3 | 4 | extern crate aliases; 5 | 6 | #[cfg(test)] 7 | mod tests { 8 | 9 | pub use std::path::PathBuf; 10 | pub use aliases::{Alias, AliasFactory}; 11 | 12 | describe! alias_builder { 13 | 14 | describe! create_from_file { 15 | 16 | before_each { 17 | let path: PathBuf; 18 | } 19 | 20 | describe! when_the_file_does_not_exists { 21 | 22 | before_each { 23 | path = PathBuf::new(); 24 | } 25 | 26 | it "returns an error" { 27 | let result = AliasFactory::create_from_file(path); 28 | assert!(result.is_err()); 29 | assert_eq!(result.err(), Some("File did not exist.")); 30 | } 31 | } 32 | 33 | describe! when_the_file_exists { 34 | 35 | describe! but_the_content_is_invalid { 36 | 37 | before_each { 38 | path = PathBuf::from("./tests/fixtures/aliases_files/invalid"); 39 | } 40 | 41 | it "returns an error" { 42 | let result = AliasFactory::create_from_file(path); 43 | assert!(result.is_err()); 44 | assert_eq!(result.err(), Some("File invalid content.")); 45 | } 46 | } 47 | 48 | describe! and_the_content_is_valid { 49 | 50 | before_each { 51 | path = PathBuf::from("./tests/fixtures/aliases_files/valid"); 52 | } 53 | 54 | it "returns a collection of aliases" { 55 | let result = AliasFactory::create_from_file(path); 56 | assert!(result.is_ok()); // how can I check the class type or something else?? 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/aliases_collection.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin,const_fn)] 2 | #![plugin(stainless)] 3 | 4 | extern crate aliases; 5 | 6 | #[cfg(test)] 7 | mod tests { 8 | 9 | pub use aliases::{Aliases, Alias}; 10 | 11 | describe! merge { 12 | 13 | before_each { 14 | let mut first = Alias::new(); 15 | first.name = "first".to_string(); 16 | let mut second = Alias::new(); 17 | second.name = "second".to_string(); 18 | let mut third = Alias::new(); 19 | third.name = "third".to_string(); 20 | let mut forth = Alias::new(); 21 | forth.name = "forth".to_string(); 22 | let subject = Aliases::new(vec![first.clone(), second.clone()]); 23 | } 24 | 25 | describe! when_there_are_no_duplicates { 26 | 27 | before_each { 28 | let other = Aliases::new(vec![third.clone(), forth.clone()]); 29 | let result = subject.merge(other); 30 | } 31 | 32 | it "returns a new collection with the aliases from both" { 33 | assert_eq!(result, Aliases::new(vec![first.clone(), second.clone(), third.clone(), forth.clone()])) 34 | } 35 | } 36 | 37 | describe! when_there_are_duplicates { 38 | 39 | before_each { 40 | let other = Aliases::new(vec![first.clone(), third.clone(), forth.clone()]); 41 | let result = subject.merge(other); 42 | } 43 | 44 | it "returns a new collection without duplicates" { 45 | assert_eq!(result, Aliases::new(vec![first.clone(), second.clone(), third.clone(), forth.clone()])); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/fixtures/aliases_files/invalid: -------------------------------------------------------------------------------- 1 | something 2 | that 3 | will not 4 | create any aliases 5 | -------------------------------------------------------------------------------- /tests/fixtures/aliases_files/valid: -------------------------------------------------------------------------------- 1 | test-command: 2 | command: ./super_command.sh 3 | confirm: true 4 | confirmation_message: Are you really really sure?? 5 | conditional: /bin/true 6 | unit_test: '[ true = true ]' 7 | -------------------------------------------------------------------------------- /tests/fixtures/dir_with_local_aliases/.aliases: -------------------------------------------------------------------------------- 1 | server: 2 | command: bundle exec rails server 3 | -------------------------------------------------------------------------------- /tests/fixtures/initialized_dir/.aliases: -------------------------------------------------------------------------------- 1 | test-command: 2 | command: ./super_command.sh 3 | confirm: true 4 | confirmation_message: Are you really really sure?? 5 | conditional: /bin/true 6 | unit_test: '[ true = true ]' 7 | -------------------------------------------------------------------------------- /tests/fixtures/uninitialized_dir/.aliases: -------------------------------------------------------------------------------- 1 | # alias_name: 2 | # command: some command here 3 | 4 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | //#![feature(plugin,const_fn)] 2 | //#![plugin(stainless)] 3 | 4 | //extern crate aliases; 5 | //extern crate gag; 6 | //extern crate regex; 7 | 8 | //#[cfg(test)] 9 | //mod tests { 10 | //pub use aliases::App; 11 | //pub use std::path::PathBuf; 12 | //pub use std::io::prelude::*; 13 | //pub use std::fs; 14 | //pub use std::env; 15 | //pub use std::fs::File; 16 | //pub use gag::BufferRedirect; 17 | //pub use regex::Regex; 18 | 19 | 20 | //pub fn content_for_file(path: &str) -> String { 21 | //let mut file = File::open(path).unwrap(); 22 | //String::new() 23 | //} 24 | 25 | //describe! app { 26 | 27 | //describe! execute_init { 28 | 29 | //before_each { 30 | //let mut app = App::new(); 31 | //let mut global = false; 32 | //} 33 | 34 | //A describe! when_the_current_directory_has_not_previously_been_initialized { 35 | 36 | //before_each { 37 | //let path = PathBuf::from("tests/fixtures/uninitialized_dir"); 38 | //app.current_path = path.clone(); 39 | //fs::remove_file(path.join(".aliases")); 40 | //} 41 | 42 | //it "creates a .aliases file using the stored template" { 43 | //app.execute_init(global); 44 | //let template_aliases_file_content = content_for_file("src/templates/aliases"); 45 | //let target_dir_aliases_file_content = content_for_file("tests/fixtures/uninitialized_dir/.aliases"); 46 | //assert_eq!(target_dir_aliases_file_content, template_aliases_file_content); 47 | //} 48 | 49 | //it "updates the aliases directory repository to include the new directory" {} 50 | //} 51 | 52 | //describe! when_the_current_directory_has_already_been_initialized { 53 | 54 | //before_each { 55 | //let path = PathBuf::from("tests/fixtures/uninitialized_dir"); 56 | //app.current_path = path.clone(); 57 | //let mut buf = BufferRedirect::stdout().unwrap(); 58 | //File::create(path.join(".aliases")); 59 | //} 60 | 61 | //it "it alerts the user" { 62 | //app.execute_init(global); 63 | //let mut output = String::new(); 64 | //buf.read_to_string(&mut output).unwrap(); 65 | //assert_eq!(&output[..], "Directory already initialized.\n"); 66 | //} 67 | //} 68 | 69 | //describe! when_global_is_true { 70 | 71 | //before_each { 72 | //let mut global = true; 73 | //env::set_var("HOME", "/tmp"); 74 | //} 75 | 76 | //describe! and_the_home_directory_has_not_previously_been_initialized { 77 | 78 | //before_each { 79 | //fs::remove_file("/tmp/.aliases"); 80 | //} 81 | 82 | //it "creates a .aliases file using the stored template" { 83 | //app.execute_init(global); 84 | //let template_aliases_file_content = content_for_file("src/templates/aliases"); 85 | //let target_dir_aliases_file_content = content_for_file("/tmp/.aliases"); 86 | //assert_eq!(target_dir_aliases_file_content, template_aliases_file_content); 87 | //} 88 | //} 89 | 90 | //describe! and_the_home_directory_has_already_been_initialized { 91 | 92 | //before_each { 93 | //let path = PathBuf::from("/tmp"); 94 | //let mut buf = BufferRedirect::stdout().unwrap(); 95 | //File::create(path.join(".aliases")); 96 | //} 97 | 98 | //it "it alerts the user" { 99 | //app.execute_init(global); 100 | //let mut output = String::new(); 101 | //buf.read_to_string(&mut output).unwrap(); 102 | //assert_eq!(&output[..], "Directory already initialized.\n"); 103 | //} 104 | //} 105 | //} 106 | //} 107 | 108 | //describe! execute_list { 109 | 110 | //before_each { 111 | ////let mut buf = BufferRedirect::stdout().unwrap(); 112 | ////let mut app = App::new(); 113 | //} 114 | 115 | //describe! there_are_local_aliases { 116 | 117 | //before_each { 118 | ////let path = PathBuf::from("tests/fixtures/dir_with_local_aliases"); 119 | ////app.current_path = path.clone(); 120 | //} 121 | 122 | //it "displays the local aliases" { 123 | ////app.execute_list(); 124 | ////let mut output = String::new(); 125 | ////buf.read_to_string(&mut output).unwrap(); 126 | ////let regex = Regex::new(r"server.*bundle exec rails server").unwrap(); 127 | ////assert!(regex.is_match(output)); 128 | //} 129 | //} 130 | 131 | //describe! there_are_parent_aliases { 132 | 133 | //it "displays the parent aliases" { 134 | //} 135 | //} 136 | 137 | //describe! there_are_global_aliases { 138 | 139 | //it "displays the global aliases" { 140 | //} 141 | //} 142 | //} 143 | 144 | //describe! execute_rehash { 145 | 146 | //describe! when_there_are_new_additions { 147 | 148 | //it "makes them available as functions" {} 149 | //} 150 | 151 | //describe! when_there_are_aliases_that_have_been_removed { 152 | 153 | //it "makes them no longer available as functions" {} 154 | //} 155 | //} 156 | //} 157 | //} 158 | -------------------------------------------------------------------------------- /tests/rehash_command.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin,const_fn)] 2 | #![plugin(stainless)] 3 | 4 | extern crate aliases; 5 | 6 | #[cfg(test)] 7 | mod tests { 8 | 9 | pub use aliases::Rehash; 10 | pub use aliases::Alias; 11 | pub use aliases::ShimFileFactory; 12 | pub use std::path::{Path, PathBuf}; 13 | pub use std::fs; 14 | pub use std::env; 15 | pub use std::fs::File; 16 | 17 | describe! execute { 18 | 19 | before_each { 20 | let current_dir = env::current_dir().unwrap(); 21 | let shim_directory = current_dir.join("tests/fixtures/shims/"); 22 | let rehash = Rehash::new(shim_directory.clone(), vec![current_dir.join("tests/fixtures/initialized_dir/")]); 23 | } 24 | 25 | describe! when_there_are_initialized_aliases { 26 | 27 | describe! when_there_is_no_global_shim_for_an_alias { 28 | 29 | before_each { 30 | let _ = fs::remove_file(shim_directory.join("test-command")); 31 | } 32 | 33 | it "generates one" { 34 | rehash.execute(); 35 | assert!(shim_directory.join("test-command").as_path().exists()); 36 | assert!(ShimFileFactory::is_valid(&shim_directory.join("test-command")), true); 37 | } 38 | } 39 | 40 | describe! when_there_is_already_a_shim_for_an_alias { 41 | 42 | //describe! and_the_alias_has_not_changed { 43 | 44 | //before_each { 45 | //let mut alias = Alias::new(); 46 | //alias.name = String::from("test-command"); 47 | //let _ = ShimFileFactory::create(&alias, &shim_directory); 48 | //let create 49 | //fs::metadata("/some/file/path.txt") 50 | //} 51 | 52 | //it "leaves the existing shim unchanged" { 53 | 54 | //} 55 | //} 56 | 57 | describe! and_the_alias_has_changed { 58 | 59 | before_each { 60 | let _ = File::create(shim_directory.join("test-command")); 61 | } 62 | 63 | it "updates the shim" { 64 | rehash.execute(); 65 | assert!(ShimFileFactory::is_valid(&shim_directory.join("test-command")), true); 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } 72 | --------------------------------------------------------------------------------