├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── hostfile.rs ├── hostlist.rs ├── hostname.rs ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ansi_term" 5 | version = "0.11.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.11" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "bitflags" 23 | version = "1.0.4" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | 26 | [[package]] 27 | name = "clap" 28 | version = "2.33.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 33 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 34 | "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 38 | ] 39 | 40 | [[package]] 41 | name = "heck" 42 | version = "0.3.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | dependencies = [ 45 | "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 46 | ] 47 | 48 | [[package]] 49 | name = "hostess" 50 | version = "0.2.6" 51 | dependencies = [ 52 | "pad 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 55 | ] 56 | 57 | [[package]] 58 | name = "libc" 59 | version = "0.2.55" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | 62 | [[package]] 63 | name = "numtoa" 64 | version = "0.1.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | 67 | [[package]] 68 | name = "pad" 69 | version = "0.1.5" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | dependencies = [ 72 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 73 | ] 74 | 75 | [[package]] 76 | name = "proc-macro-error" 77 | version = "0.4.4" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | dependencies = [ 80 | "proc-macro-error-attr 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 85 | ] 86 | 87 | [[package]] 88 | name = "proc-macro-error-attr" 89 | version = "0.4.3" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 95 | "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 96 | "syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 97 | ] 98 | 99 | [[package]] 100 | name = "proc-macro2" 101 | version = "1.0.6" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | dependencies = [ 104 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 105 | ] 106 | 107 | [[package]] 108 | name = "quote" 109 | version = "1.0.2" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | dependencies = [ 112 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 113 | ] 114 | 115 | [[package]] 116 | name = "redox_syscall" 117 | version = "0.1.54" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | 120 | [[package]] 121 | name = "redox_termios" 122 | version = "0.1.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | dependencies = [ 125 | "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", 126 | ] 127 | 128 | [[package]] 129 | name = "rustversion" 130 | version = "1.0.1" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | dependencies = [ 133 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 136 | ] 137 | 138 | [[package]] 139 | name = "strsim" 140 | version = "0.8.0" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | 143 | [[package]] 144 | name = "structopt" 145 | version = "0.3.7" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | dependencies = [ 148 | "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", 149 | "structopt-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 150 | ] 151 | 152 | [[package]] 153 | name = "structopt-derive" 154 | version = "0.4.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | dependencies = [ 157 | "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 158 | "proc-macro-error 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 159 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 160 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 161 | "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 162 | ] 163 | 164 | [[package]] 165 | name = "syn" 166 | version = "1.0.11" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | dependencies = [ 169 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 170 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 171 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 172 | ] 173 | 174 | [[package]] 175 | name = "syn-mid" 176 | version = "0.4.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | dependencies = [ 179 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 180 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 181 | "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 182 | ] 183 | 184 | [[package]] 185 | name = "termion" 186 | version = "1.5.2" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | dependencies = [ 189 | "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", 190 | "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 191 | "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", 192 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 193 | ] 194 | 195 | [[package]] 196 | name = "textwrap" 197 | version = "0.11.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | dependencies = [ 200 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 201 | ] 202 | 203 | [[package]] 204 | name = "unicode-segmentation" 205 | version = "1.6.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | 208 | [[package]] 209 | name = "unicode-width" 210 | version = "0.1.5" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | 213 | [[package]] 214 | name = "unicode-xid" 215 | version = "0.2.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | 218 | [[package]] 219 | name = "vec_map" 220 | version = "0.8.1" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | 223 | [[package]] 224 | name = "winapi" 225 | version = "0.3.7" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | dependencies = [ 228 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 229 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 230 | ] 231 | 232 | [[package]] 233 | name = "winapi-i686-pc-windows-gnu" 234 | version = "0.4.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | 237 | [[package]] 238 | name = "winapi-x86_64-pc-windows-gnu" 239 | version = "0.4.0" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | 242 | [[package]] 243 | name = "yansi" 244 | version = "0.5.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | 247 | [metadata] 248 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 249 | "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 250 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 251 | "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" 252 | "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 253 | "checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880" 254 | "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" 255 | "checksum pad 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9b8de33465981073e32e1d75bb89ade49062bb853e7c97ec2c13439095563a" 256 | "checksum proc-macro-error 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428" 257 | "checksum proc-macro-error-attr 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c" 258 | "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" 259 | "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" 260 | "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" 261 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 262 | "checksum rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986" 263 | "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 264 | "checksum structopt 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "884ae79d6aad1e738f4a70dff314203fd498490a63ebc4d03ea83323c40b7b72" 265 | "checksum structopt-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a97f829a34a0a9d5b353a881025a23b8c9fd09d46be6045df6b22920dbd7a93" 266 | "checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" 267 | "checksum syn-mid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" 268 | "checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" 269 | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 270 | "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 271 | "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" 272 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 273 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 274 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 275 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 276 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 277 | "checksum yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" 278 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hostess" 3 | version = "0.2.6" 4 | authors = ["aliast"] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "A tool to maintain hosts" 8 | homepage = "https://github.com/chaixb/hostess" 9 | repository = "https://github.com/chaixb/hostess" 10 | 11 | 12 | [lib] 13 | name = "hostess" 14 | path = "src/lib.rs" 15 | 16 | [dependencies] 17 | structopt = "0.3" 18 | pad = "0.1.5" 19 | yansi = "0.5.0" 20 | 21 | [features] 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 xiaobing 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hostess 2 | 3 | Command-line utility for managing your /etc/hosts file, built with rust 4 | 5 | ## Install 6 | 7 | `cargo install hostess` 8 | 9 | ## Usage 10 | 11 | | Command | Description | 12 | | :-------------------------- | :------------- | 13 | | `hostess list` | List all hosts | 14 | | `hostess add [domain] [ip]` | Add a host | 15 | | `hostess rm [domain]` | Remove a host | 16 | | `hostess on [domain]` | Enable a host | 17 | | `hostess off [domain]` | Disable a host | 18 | 19 | ## Todo 20 | 21 | - [ ] regex 22 | -------------------------------------------------------------------------------- /src/hostfile.rs: -------------------------------------------------------------------------------- 1 | // borrowed from 2 | // https://github.com/cbednarski/hostess/blob/master/hostfile.go 3 | 4 | use super::hostlist; 5 | use super::hostname; 6 | 7 | use pad::*; 8 | use std::cmp; 9 | use std::error; 10 | use std::fs; 11 | use std::fs::File; 12 | use std::io::prelude::*; 13 | use std::io::BufReader; 14 | use yansi::Paint; 15 | 16 | #[derive(Debug)] 17 | pub struct Hostfile { 18 | path: String, 19 | hosts: hostlist::Hostlist, 20 | } 21 | 22 | impl Hostfile { 23 | fn new(path: &str) -> Hostfile { 24 | let file = File::open(path).unwrap(); 25 | let file_reader = BufReader::new(file); 26 | let mut hosts: hostlist::Hostlist = Vec::new(); 27 | 28 | for line in file_reader.lines() { 29 | hosts.append(&mut Hostfile::parse_line(line.unwrap())); 30 | } 31 | 32 | let hostfile = Hostfile { 33 | path: String::from(path), 34 | hosts, 35 | }; 36 | 37 | hostfile 38 | } 39 | 40 | pub fn default() -> Hostfile { 41 | let default_path = if cfg!(target_os = "windows") { 42 | "C:/Windows/System32/drivers/etc" 43 | } else { 44 | "/etc/hosts" 45 | }; 46 | Hostfile::new(default_path) 47 | } 48 | 49 | /// append item to hosts 50 | pub fn append(&mut self, domain: String, ip: &str) -> Result<(), Box> { 51 | let host = hostname::Hostname { 52 | ip: Some(ip.parse()?), 53 | domain: Some(domain), 54 | valid: true, 55 | enabled: true, 56 | comment: String::from(""), 57 | }; 58 | 59 | self.hosts.push(host); 60 | self.save(); 61 | 62 | println!("{}", Paint::green("success!")); 63 | 64 | Ok(()) 65 | } 66 | 67 | /// remove item from hosts 68 | pub fn remove(&mut self, domain: String) { 69 | self 70 | .hosts 71 | .retain(|host| host.domain != Some(domain.clone())); 72 | println!("{} {}", domain, Paint::red("removed!")); 73 | self.save(); 74 | } 75 | 76 | // enable item 77 | pub fn on(&mut self, domain: String) { 78 | for host in &mut self.hosts { 79 | if host.domain == Some(domain.clone()) { 80 | host.enabled = true; 81 | } 82 | } 83 | self.save(); 84 | println!("{}", Paint::green("success!")); 85 | } 86 | 87 | // disable item 88 | pub fn off(&mut self, domain: String) { 89 | for host in &mut self.hosts { 90 | if host.domain == Some(domain.clone()) { 91 | host.enabled = false; 92 | } 93 | } 94 | 95 | println!("{} {}", domain, Paint::green("disabled!")); 96 | self.save(); 97 | } 98 | 99 | /// save write contents to hosts file 100 | /// this action remove all comments currently 101 | /// TODO: keep action 102 | pub fn save(&self) { 103 | let mut result = String::new(); 104 | 105 | for host in &self.hosts { 106 | let is_comment = if host.enabled { "" } else { "# " }; 107 | let line = if host.valid { 108 | format!( 109 | "{}{} {}\n", 110 | is_comment, 111 | host.ip.unwrap(), 112 | host.domain.as_ref().unwrap() 113 | ) 114 | } else { 115 | format!("{}\n", host.comment.clone()) 116 | }; 117 | 118 | result.push_str(line.as_str()); 119 | } 120 | 121 | fs::write(&self.path, result).unwrap(); 122 | } 123 | 124 | // output hosts 125 | pub fn format(&self) { 126 | let mut max_domain_len = 0; 127 | let mut max_ip_len = 0; 128 | 129 | // compute max len for output 130 | for host in &self.hosts { 131 | if let Some(ip) = host.ip { 132 | max_domain_len = cmp::max(host.domain.as_ref().unwrap().len(), max_domain_len); 133 | max_ip_len = cmp::max(ip.to_string().len(), max_ip_len); 134 | } 135 | } 136 | 137 | for host in &self.hosts { 138 | let status = if host.enabled { 139 | format!("({})", Paint::green("on")) 140 | } else { 141 | format!("({})", Paint::red("off")) 142 | }; 143 | if let Some(ip) = host.ip { 144 | println!( 145 | "{} -> {} {}", 146 | host.domain.as_ref().unwrap().pad_to_width(max_domain_len), 147 | ip.to_string().pad_to_width(max_ip_len), 148 | status 149 | ); 150 | } 151 | } 152 | } 153 | 154 | fn parse_line(source_string: String) -> hostlist::Hostlist { 155 | let mut hostlist: hostlist::Hostlist = Vec::new(); 156 | let mut enabled = true; 157 | 158 | // comment 159 | let mut source = source_string.clone(); 160 | 161 | // 去除首尾空白 162 | source = source.trim().to_string(); 163 | if source.starts_with("#") { 164 | enabled = false; 165 | source.remove(0); 166 | } 167 | 168 | // Parse other # for actual comments 169 | let line: Vec<&str> = source.split("#").collect(); 170 | 171 | // host 条目 172 | let content: &str = line.get(0).unwrap(); 173 | // 评论内容 174 | let comment: &str = line.get(1).unwrap_or(&""); 175 | let mut words = content.split_whitespace(); 176 | 177 | // force now! 178 | let ip = words.next().unwrap_or_default().parse(); 179 | 180 | if let Ok(ip) = ip { 181 | for (_, domain) in words.enumerate() { 182 | let id = hostname::Hostname { 183 | ip: Some(ip), 184 | enabled, 185 | valid: true, 186 | domain: Some(domain.to_string()), 187 | comment: String::from(comment), 188 | }; 189 | hostlist.push(id); 190 | } 191 | } else { 192 | // 如果parse失败,将整行当作注释直接存储。 193 | let id = hostname::Hostname { 194 | ip: None, 195 | domain: None, 196 | enabled: false, 197 | comment: source_string, 198 | valid: false, 199 | }; 200 | hostlist.push(id); 201 | } 202 | 203 | hostlist 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/hostlist.rs: -------------------------------------------------------------------------------- 1 | use super::hostname; 2 | 3 | pub type Hostlist = Vec; 4 | -------------------------------------------------------------------------------- /src/hostname.rs: -------------------------------------------------------------------------------- 1 | use std::net::IpAddr; 2 | use std::cmp::{PartialEq}; 3 | 4 | #[derive(Debug, PartialEq)] 5 | pub struct Hostname { 6 | pub domain: Option, 7 | pub ip: Option, 8 | pub enabled: bool, 9 | pub comment: String, 10 | pub valid: bool, 11 | // TODO: ip_v6: bool 12 | } 13 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod hostfile; 2 | pub mod hostlist; 3 | pub mod hostname; 4 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use hostess::hostfile; 2 | use structopt::StructOpt; 3 | 4 | #[derive(Debug, StructOpt)] 5 | #[structopt(name = "hostess")] 6 | 7 | enum Opt { 8 | #[structopt(about = "List all hosts")] 9 | List {}, 10 | 11 | #[structopt(about = "Enable a host")] 12 | On { 13 | #[structopt(name = "DOMAIN")] 14 | domain: String, 15 | }, 16 | 17 | #[structopt(about = "Disable a host")] 18 | Off { 19 | #[structopt(name = "DOMAIN")] 20 | domain: String, 21 | }, 22 | 23 | #[structopt(about = "Remove a host")] 24 | Rm { 25 | #[structopt(name = "DOMAIN")] 26 | domain: String, 27 | }, 28 | 29 | #[structopt(about = "Add a host")] 30 | Add { 31 | // domain 和 ip的位置不能随意变动 32 | #[structopt(name = "DOMAIN")] 33 | domain: String, 34 | #[structopt(name = "IP", default_value = "127.0.0.1")] 35 | ip: String, 36 | }, 37 | } 38 | 39 | fn main() { 40 | let matches = Opt::from_args(); 41 | let mut hosts = hostfile::Hostfile::default(); 42 | 43 | match matches { 44 | Opt::List {} => { 45 | hosts.format(); 46 | } 47 | Opt::On { domain } => { 48 | hosts.on(domain); 49 | } 50 | Opt::Off { domain } => { 51 | hosts.off(domain); 52 | } 53 | Opt::Add { ip, domain } => { 54 | hosts.append(domain, &ip).unwrap(); 55 | } 56 | Opt::Rm { domain } => { 57 | hosts.remove(domain); 58 | } 59 | } 60 | } 61 | --------------------------------------------------------------------------------