├── .all-contributorsrc
├── .gitattributes
├── .github
├── FUNDING.yml
└── workflows
│ ├── ci.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── build.rs
├── demos
├── DEMOS.md
├── define.gif
├── direct_demo.gif
├── file_demo.gif
├── history_demo.gif
├── main_demo.gif
├── stack-overflow.gif
├── stocks.gif
└── what.gif
├── dist-workspace.toml
├── flake.nix
├── scripts
├── is-fast-projects.ps1
└── is-fast-projects.sh
├── src
├── actions
│ ├── generate_config.rs
│ ├── mod.rs
│ └── prepare_pages.rs
├── app
│ ├── enum_values.rs
│ ├── event_loop.rs
│ ├── history.rs
│ ├── mod.rs
│ ├── page.rs
│ ├── text.rs
│ └── tui.rs
├── cli
│ ├── command.rs
│ ├── mod.rs
│ └── parser.rs
├── config
│ ├── alternate_headers.toml
│ ├── color_conversion.rs
│ ├── config.toml
│ ├── files.rs
│ ├── format.rs
│ ├── glob_generation.rs
│ ├── load.rs
│ ├── log.rs
│ ├── mod.rs
│ ├── site.rs
│ ├── site_raw.rs
│ └── tool_raw.rs
├── database
│ ├── history_database.rs
│ └── mod.rs
├── errors
│ ├── error.rs
│ └── mod.rs
├── main.rs
├── page
│ ├── mod.rs
│ └── structure.rs
├── pipe
│ ├── history.rs
│ └── mod.rs
├── search_engine
│ ├── cache.rs
│ ├── duckduckgo.rs
│ ├── google.rs
│ ├── kagi.rs
│ ├── link.rs
│ ├── mod.rs
│ ├── scrape.rs
│ ├── search.rs
│ └── search_type.rs
├── transform
│ ├── cache.rs
│ ├── filter.rs
│ ├── format.rs
│ ├── mod.rs
│ ├── page.rs
│ ├── pretty_print.rs
│ └── syntax_highlight.rs
└── tui
│ ├── display.rs
│ ├── general_widgets.rs
│ ├── history_content.rs
│ ├── history_widgets.rs
│ ├── mod.rs
│ ├── page_content.rs
│ └── page_widgets.rs
├── tests
└── data
│ ├── expected_ansi_text.txt
│ ├── expected_nth.txt
│ ├── expected_selected.txt
│ ├── expected_text.txt
│ ├── invalid_lang_config.toml
│ ├── sample.html
│ ├── selector_override_config.toml
│ ├── test_output_margin_wrap.txt
│ ├── test_output_title.txt
│ ├── test_output_title_margin_wrap.txt
│ ├── unformatted.txt
│ └── unformatted_result.txt
└── wix
└── main.wxs
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "is-fast",
3 | "projectOwner": "Magic-JD",
4 | "files": [
5 | "README.md"
6 | ],
7 | "commitType": "docs",
8 | "commitConvention": "angular",
9 | "contributorsPerLine": 7,
10 | "contributors": [
11 | {
12 | "login": "pwnwriter",
13 | "name": "Nabeen Tiwaree",
14 | "avatar_url": "https://avatars.githubusercontent.com/u/90331517?v=4",
15 | "profile": "http://pwnwriter.me",
16 | "contributions": [
17 | "platform"
18 | ]
19 | },
20 | {
21 | "login": "rehanzo",
22 | "name": "Rehan",
23 | "avatar_url": "https://avatars.githubusercontent.com/u/60243794?v=4",
24 | "profile": "https://github.com/rehanzo",
25 | "contributions": [
26 | "plugin"
27 | ]
28 | },
29 | {
30 | "login": "d3-X-t3r",
31 | "name": "d3Xt3r",
32 | "avatar_url": "https://avatars.githubusercontent.com/u/1624052?v=4",
33 | "profile": "https://github.com/d3-X-t3r",
34 | "contributions": [
35 | "ideas"
36 | ]
37 | }
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | tests/data/sample.html linguist-generated
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [magic-jd]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: is-fast CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | rustfmt:
11 | name: Check Formatting
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout repository
15 | uses: actions/checkout@v4
16 |
17 | - name: Set up Rust
18 | uses: dtolnay/rust-toolchain@stable
19 | with:
20 | toolchain: stable
21 | components: rustfmt
22 |
23 | - name: Run rustfmt
24 | run: cargo fmt --check
25 |
26 | clippy:
27 | name: Lint with Clippy
28 | runs-on: ubuntu-latest
29 | steps:
30 | - name: Checkout repository
31 | uses: actions/checkout@v4
32 |
33 | - name: Set up Rust
34 | uses: dtolnay/rust-toolchain@stable
35 | with:
36 | toolchain: stable
37 | components: clippy
38 |
39 | - name: Cache dependencies
40 | uses: Swatinem/rust-cache@v2
41 |
42 | - name: Run Clippy
43 | run: cargo clippy --all-targets -- -D warnings > clippy-report.json
44 |
45 | - name: Upload Clippy Report
46 | uses: actions/upload-artifact@v4
47 | with:
48 | name: clippy-report
49 | path: clippy-report.json
50 |
51 | test:
52 | name: Run Tests
53 | runs-on: ubuntu-latest
54 | steps:
55 | - name: Checkout repository
56 | uses: actions/checkout@v4
57 |
58 | - name: Set up Rust
59 | uses: dtolnay/rust-toolchain@stable
60 |
61 | - name: Cache dependencies
62 | uses: Swatinem/rust-cache@v2
63 |
64 | - name: Install cargo-tarpaulin
65 | run: cargo install cargo-tarpaulin
66 |
67 | - name: Run tests
68 | run: cargo tarpaulin --out Xml --output-dir target/llvm-cov
69 |
70 | - name: Upload Coverage Report
71 | uses: actions/upload-artifact@v4
72 | with:
73 | name: coverage-report
74 | path: target/llvm-cov/cobertura.xml
75 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Run nix build
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - 'src/*'
9 | - 'flake.*'
10 | - 'Cargo.*'
11 |
12 | jobs:
13 | run-tests:
14 | strategy:
15 | matrix:
16 | os: [ubuntu-latest, macos-latest]
17 | runs-on: ${{ matrix.os }}
18 |
19 | steps:
20 | - name: Checkout code
21 | uses: actions/checkout@v2
22 |
23 | - name: Set up Nix
24 | uses: DeterminateSystems/nix-installer-action@main
25 |
26 | - name: Nix test
27 | run: nix develop -c cargo test -- --skip=generate_config::tests::test_run_creates_config_file
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | .idea
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [0.16.2]
4 | ### Fix
5 | - Let site selection in config be applied. (currently only the flag is working correctly)
6 |
7 | ### Added
8 | - Configuration for timeout.
9 |
10 | ## [0.16.1]
11 | ### Fix
12 | - Change reqwest to ureq due to issues with cloudflare blocking reqwest.
13 |
14 | ## [0.16.0]
15 | ### Added
16 | - Log feature flag for allowing logging to be easily set.
17 | - Documentation for new features, and explaining how to get more detailed logs.
18 | - Upgraded the logging, logging more actions.
19 |
20 | ## [0.15.14]
21 | ### Fix
22 | - Allow arguments to be passed into the open tool rather than just including a tool name.
23 | - Update the release files so they use correct ubuntu version
24 |
25 | ## [0.15.3]
26 | ### Fix
27 | - Fixed bug with cursor not appearing after tui shutdown
28 |
29 | ## [0.15.1]
30 | ### Added
31 | - Added support for kitty text size protocol.
32 | - For supported terminals (kitty v0.40.0+, not using tmux, zellig, screen, ect) text size can be set in the configuration.
33 | - This will only show when the text is output to the terminal directly using piped.
34 | - This must be enabled in the configuration in the misc section.
35 |
36 | ## [0.14.1]
37 | ### Fix
38 | - Added brotli decoding to handle some sites that use it.
39 |
40 | ## [0.14.0]
41 | ### Added
42 | - Styles also works with basic CSS syntax.
43 | - Styles will combine together, with priority being basic, tag, untagged class, tagged class, untagged id, tagged id.
44 | - e.g. The element is div#this.that
45 | - Default style is undefined
46 | - .that (matches any .that) is BOLD and RED
47 | - \#this is ITALIC and BLUE
48 | - div is UNDERLINED
49 | - div.that is GREEN
50 | - div#this is ORANGE
51 | - The result will be BOLD, ITALIC, UNDERLINED, and ORANGE (with the color priority order being ORANGE, BLUE, GREEN, RED).
52 | - Styles can be passed into the query directly.
53 | - Format is `--style-element="tag#id.class.otherclass:fg=red;bg=green;bold"`
54 | - For booleans `bold` is equal to `bold=true`
55 | - Multiple style elements can be added with multiple flags: `--style-element="x:fg=red" --style-element="y:fg=blue"`
56 | - All the normal style elements are supported.
57 | - Scripts updated to add color to the stock script.
58 |
59 | ## [0.13.3]
60 | ### Added
61 | - You can now specify if you want to replace or extend the block, indent and ignored elements.
62 |
63 | ## [0.13.2]
64 | ### Fixed
65 | - Longest and therefore most specific glob match will always be returned.
66 |
67 | ## [0.13.1]
68 | ### Added
69 | - Indent elements, allowing you to indent nested elements. Currently only set for lists.
70 | - To allow nested lists
71 | - Like this
72 | - To display
73 | - Correctly
74 | - This is set in the site specific configuration, and I hope that this can be used with custom selectors to help comment chains ect to display better in the future.
75 |
76 | ### Fixed
77 | - Ordered the http headers when requesting a page, as this can effect if the request is blocked or not.
78 |
79 | ## [0.13.0]
80 | ### Added
81 | - Site specific configuration. Configuration that should only apply to one site, can now be matched only to that site.
82 | - Site configs are added with `[custom_config]` `"*site.glob.match/*" = ["alternate_headers.toml", "interesting_colorscheme.toml"]`
83 | - All configs supplied will be added to the base config.
84 | - The custom config files should be placed in the same location as your standard config.
85 | - A small number of custom files will be inbuilt into the binary (currently just alternate_headers). A local file with the same name will take priority over them.
86 | - Headers has been added as a new configuration. These can be configured generally or on a site by site basis.
87 |
88 | ### Fixed
89 | - The stock mini script works again.
90 |
91 | ## [0.12.4]
92 | ### Added
93 | - Additional selectors for old.reddit.com and better selectors for stack overflow.
94 |
95 | ### Fixed
96 | - Too high indentation, reduced it slightly.
97 |
98 | ## [0.12.3]
99 | ### Added
100 | - Ability to configure the log, database and config location through environment variables.
101 |
102 | ## [0.12.2]
103 | ### Fixed
104 | - Buggy list icons when part of an ordered list.
105 |
106 | ### Added
107 | - Numbers for ordered lists.
108 | - Indentation for nested lists.
109 |
110 | ## [0.12.1]
111 | ### Fixed
112 | - Unicode is now correctly rendered for all sites.
113 | - Preloading no longer starts a new thread if one is not needed.
114 |
115 | ### Changed
116 | - Removed a number of dependencies, where two dependencies were used that performed the same function.
117 |
118 | ## [0.12.0]
119 | ### Added
120 | - New flag for adding additional ignore tags. `--ignore="div"`
121 | - New flag for not applying block elements (reducing non formatted or ` ` code to a single line).
122 | - All tags for ignored and blocked elements support basic css selector features (.class or #id)
123 | - Title `--pretty-print` value will now default to the page title if no title is provided.
124 | - Support multiple elements with one flag for `--nth-element`
125 | - A number of new default page selectors.
126 | - New script for doing quick conversion checks.
127 |
128 | ### Fixed
129 | - Google search page is now supported to view, as are a number of other pages that were previously blocked.
130 | - Spaces in direct urls are now converted to + for ease of scripting use.
131 |
132 | ### Changed
133 | - Refactor of code across many files, splitting up logic especially in the Config struct/s.
134 | - Updated a number of dependencies in the cargo lock.
135 |
136 | ## [0.11.4]
137 | ### Changed
138 | - Increased the level of details in the logs.
139 |
140 | ### Fixed
141 | - Allow the title to be updated in the history.
142 | - Output shown and exit with error code when no results are found.
143 |
144 | ## [0.11.3]
145 | ### Fixed
146 | - Increased the timeout due to the previous timeout being too short.
147 |
148 | ## [0.11.2]
149 | ### Added
150 | - Logging to file when the `RUST_LOG` env var is enabled.
151 | - The log file will only be created if that environment variable is enabled.
152 | - Logs will be placed in the is-fast config directory.
153 | - When enabled, there will be an output to stderr to show the log location.
154 | - Explicit flag for cache level (readwrite, read, write, none, flash).
155 | - Better error messages when the page fails to load.
156 |
157 | ### Changed
158 | - Page title is now extracted from HTML, meaning that there is no need to retrieve from the search results.
159 |
160 | ### Fixed
161 | - History tracking now works with everything except the `--file` flag.
162 | - Parallel loading of the next page is much more efficient.
163 | - Errors or non-content now lead to that url being purged from the cache, preventing a bad cache causing lasting issues.
164 |
165 | ## [0.11.1]
166 | ### Changed
167 | - Error when updating
168 |
169 | ## [0.11.0]
170 | ### Added
171 | - Additional configuration for printed output using the --pretty-print command.
172 | - Format is command:value,command:value
173 | - Can wrap the output. - Command = wrap, no value needed, use --pretty-print="wrap"
174 | - Can apply a margin to the output. - Command = margin, value = number, use --pretty-print="margin:10"
175 | - NOTE: Margin this will automatically wrap. I think this should be the desired behaviour anyway if you want margins.
176 | - Can apply a title to the output. - Command = title, value = string, use --pretty-print="title:TITLE"
177 | - NOTE: The title cannot contain the characters , or : due to parsing issues.
178 |
179 | ### Changed
180 | - Updated documentation to include these configuration changes.
181 | - Updated the example scripts to take advantage of these new features.
182 |
183 | ### Fixed
184 | - Bug where ad results would sometimes be retrieved from duckduckgo
185 |
186 | ## [0.10.1]
187 | ### Added
188 | - The ability to cache your results. Although this option is off by default, enabling it speeds up the time mostly static pages take to reload if you close and open them again.
189 | - Added configuration for this
190 | - TTL
191 | - Max cache size
192 | - Setting for it (Disabled, Read, Write, ReadWrite)
193 | - Added command flags for this:
194 | - `--cache` will cache the result even if caching is normally disabled.
195 | - `--no-cache` will not cache the result even if caching is normally enabled.
196 | - `--flash-cache` uses a special mode where the cache size is maximum for the duration of the request, but the TTL is only 5 seconds. This has some application for scripting, where you don't want to fill your cache, but you want results to persist there throughout the duration of your script.
197 | - `--clear-cache` removes all items from the cache.
198 | - Added additional command flags for history too.
199 | - `--clear-history` clears the history.
200 | - `--clear-all` clears both cache and history.
201 | - `--no-history` will not log history for that request.
202 |
203 | ## [0.10.0]
204 | ### Fixed
205 | - Accidentally skipped updating in 0.10.1
206 |
207 | ### Changed
208 | - Enabled command flags to be able to be passed into the config, centralizing the configuration logic.
209 | - Split config file into the raw and processed config.
210 |
211 | ### Fixed
212 | - Clippy pedantic issues.
213 | - Bug where input is buffered while waiting for the loading screen causing unexpected behaviour on page load.
214 |
215 | ## [0.9.0]
216 | ### Added
217 | - A number of exciting features for scripting with `is-fast`
218 | - Scripts Directory containing a number of example scripts for how you could use `is-fast` for useful programs.
219 | - New flag `--last` - will immediately open the last viewed page (requires history to be enabled)
220 | - New flag `--nth-element` - will select the nth element that matches the css selector in the case there are multiple matches.
221 | - Contributers section to give the kind people that contribute to this project the appreciation they deserve ❤️
222 | - Generation of flag autocomplete scripts as part of the build process.
223 |
224 | ### Changed
225 | - Moved the selection logic from the link to the page.
226 | - Page now responsible for user passed flags.
227 | - Simplifies the link creation in the search engine.
228 |
229 | ## [0.8.5] - 2025-03-11
230 | ### Added
231 | - Support for 32 bit linux releases
232 |
233 | ## [0.8.4] - 2025-03-11
234 | ### Fixed
235 | - Automated changelog and release notes 🤞
236 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "is-fast"
3 | version = "0.16.2"
4 | edition = "2021"
5 | repository = "https://github.com/Magic-JD/is-fast"
6 | homepage = "https://github.com/Magic-JD/is-fast"
7 | description = "Internet search fast - view webpages from the terminal."
8 | authors = ["Joseph Daunt"]
9 |
10 | [package.metadata.wix]
11 | upgrade-guid = "98558109-78F3-44AC-94BB-270EA47A5129"
12 | path-guid = "1E9BD8F8-FFD2-4F99-B266-C805B17FEAB9"
13 | license = false
14 | eula = false
15 |
16 | [dependencies]
17 | crossterm = "0.28.1"
18 | ratatui = "0.29.0"
19 | ureq = "2.9"
20 | scraper = "0.23.1"
21 | thiserror = "2.0.11"
22 | once_cell = "1.20.3"
23 | syntect = "5.2.0"
24 | serde = { version = "1.0.218", features = ["derive"] }
25 | toml = "0.8.20"
26 | dirs = "6.0.0"
27 | clap = { version = "4.5.31", features = ["derive"] }
28 | open = "5.3.2"
29 | dashmap = "7.0.0-rc1"
30 | globset = "0.4.16"
31 | rusqlite = { version = "0.34.0", features = ["bundled"] }
32 | chrono = "0.4.40"
33 | nucleo-matcher = "0.3.1"
34 | serde_json = "1.0.139"
35 | csv = "1.3.1"
36 | enum_dispatch = "0.3.13"
37 | nu-ansi-term = "0.50.1"
38 | log = "0.4.26"
39 | env_logger = "0.11.7"
40 | zstd = "0.13.3"
41 | parking_lot = "0.12.3"
42 | textwrap = "0.16.2"
43 | encoding_rs = "0.8.35"
44 | encoding_rs_io = "0.1.7"
45 | ctor = "0.4.1"
46 | brotli = "7.0.0"
47 | shell-words = "1.1.0"
48 |
49 | [build-dependencies]
50 | clap = { version = "4.5.31", features = ["derive", "cargo", "env"] }
51 | clap_mangen = "0.2.26"
52 | clap_complete = "4.5.46"
53 |
54 | [dev-dependencies]
55 | serial_test = "3.2.0"
56 | tempfile = "3.17.1"
57 |
58 | # The profile that 'dist' will build with
59 | [profile.dist]
60 | inherits = "release"
61 | lto = "thin"
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Joseph Daunt
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 |
--------------------------------------------------------------------------------
/build.rs:
--------------------------------------------------------------------------------
1 | use clap::CommandFactory;
2 | use clap_complete::{
3 | generate_to,
4 | shells::{Bash, Fish, PowerShell, Zsh},
5 | };
6 | use clap_mangen::Man;
7 | use std::fs::File;
8 | use std::io::Write;
9 |
10 | include!("src/cli/command.rs");
11 |
12 | fn main() {
13 | println!("cargo:rerun-if-changed=build.rs");
14 | println!("cargo:rerun-if-changed=src/cli/command.rs");
15 |
16 | let cmd = Cli::command();
17 |
18 | let mut buffer = Vec::new();
19 | Man::new(cmd)
20 | .render(&mut buffer)
21 | .expect("Failed to generate man page");
22 |
23 | let out_dir = std::env::var("OUT_DIR").unwrap();
24 | let man_path = std::path::Path::new(&out_dir).join("is-fast.1");
25 |
26 | let mut file = File::create(&man_path).expect("Failed to create man file");
27 | file.write_all(&buffer).expect("Failed to write man file");
28 |
29 | let mut cmd = Cli::command();
30 | generate_to(Bash, &mut cmd, "is-fast", &out_dir).unwrap();
31 | generate_to(Zsh, &mut cmd, "is-fast", &out_dir).unwrap();
32 | generate_to(Fish, &mut cmd, "is-fast", &out_dir).unwrap();
33 | generate_to(PowerShell, &mut cmd, "is-fast", &out_dir).unwrap();
34 | }
35 |
--------------------------------------------------------------------------------
/demos/DEMOS.md:
--------------------------------------------------------------------------------
1 | # **Demos: Exploring the Capabilities of `is-fast`**
2 |
3 | `is-fast` is designed for speed and efficiency in browsing, searching, and extracting web content. Below are demonstrations showcasing its key features.
4 |
5 | ---
6 |
7 | ## 🚀 **Fast & Efficient Browsing**
8 | Effortlessly see search results, scroll through pages, and quickly jump between results. Users can see the top results for their searches and navigate between them. The next page is always preloaded, so you shouldn't have to wait.
9 |
10 | 
11 |
12 | ---
13 |
14 | ## 🔍 **Search Your History with Fuzzy Matching**
15 | Easily find and reopen pages you've visited before using powerful fuzzy search. Your history is stored in a local database so it's always available.
16 |
17 | 
18 |
19 | ---
20 |
21 | ## 🔗 **Direct Navigation & Content Extraction**
22 | Already know the page you're looking for? Jump straight to it. Using selectors, you can filter and extract only the relevant parts of a webpage. Piping the output allows integration with other tools.
23 |
24 | 
25 |
26 | ---
27 |
28 | ## 📂 **Working with Local Files**
29 | No internet? No problem! Load and format saved HTML files with ease. Use predefined selectors based on the original URL, or specify custom ones for targeted extraction. Output formatted content to standard out for further processing.
30 |
31 | 
32 |
33 | ---
34 | # Scripting Applications
35 |
36 | These are a number of small scripts that I wrote to demonstrate the power of `is-fast` as a CLI and scripting tool. You can find these scripts in the [scripts](../scripts) folder.
37 |
38 | ## What
39 |
40 | This script returns the first paragraph of the wikipedia article about what you searched, giving you a nice overview of that person/thing.
41 |
42 | ```sh
43 | isf_what() {
44 | is-fast \
45 | --direct "en.wikipedia.org/wiki/${*}" \
46 | --selector "div.mw-content-ltr > p" \
47 | --color=always \
48 | --piped \
49 | --nth-element 1 \
50 | --pretty-print="margin:20"
51 | }
52 | ```
53 |
54 | 
55 |
56 | ## Stocks
57 |
58 | Check current stock prices!
59 |
60 | ```shell
61 | isf_stock() {
62 | is-fast \
63 | --direct "https://finance.yahoo.com/quote/${1}/" \
64 | --selector "section.container > h1, span.base" \
65 | --piped \
66 | --no-cache \
67 | --pretty-print="margin:5"
68 | }
69 | ```
70 |
71 | 
72 |
73 | ## Define
74 |
75 | This script finds the definition of the given word.
76 |
77 | ```sh
78 | # NOTE capitalization is specific for ZSH - for BASH change to ${1^}
79 | isf_define() {
80 | is-fast \
81 | --direct "www.merriam-webster.com/dictionary/${1}" \
82 | --selector "div.sb" \
83 | --nth-element 1 \
84 | --color=always \
85 | --pretty-print="margin:20,title:${(C)1}" \
86 | --piped
87 | }
88 |
89 | ```
90 | 
91 |
92 | ## Search Stack Overflow
93 |
94 | This script searches Stack Overflow for a related question, and displays a well formatted question and answer directly to the terminal.
95 |
96 | ```sh
97 | isf_so() {
98 | QUESTION=$(is-fast ${*} --site "www.stackoverflow.com" --selector "div.question .js-post-body" --color=always --pretty-print="margin:20,title:Question" --piped --flash-cache) # Find the question content.
99 | ANSWER=$(is-fast --last --selector "div.accepted-answer .js-post-body" --color=always --pretty-print="margin:20,title:Answer" --piped --flash-cache) # Separately find the answer content.
100 | cat << EOF
101 |
102 | $QUESTION
103 | $ANSWER
104 |
105 | EOF
106 | }
107 |
108 | ```
109 |
110 | 
111 |
--------------------------------------------------------------------------------
/demos/define.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Magic-JD/is-fast/eea32cf82df4e0a575610d9d9fbafed26ec222d9/demos/define.gif
--------------------------------------------------------------------------------
/demos/direct_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Magic-JD/is-fast/eea32cf82df4e0a575610d9d9fbafed26ec222d9/demos/direct_demo.gif
--------------------------------------------------------------------------------
/demos/file_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Magic-JD/is-fast/eea32cf82df4e0a575610d9d9fbafed26ec222d9/demos/file_demo.gif
--------------------------------------------------------------------------------
/demos/history_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Magic-JD/is-fast/eea32cf82df4e0a575610d9d9fbafed26ec222d9/demos/history_demo.gif
--------------------------------------------------------------------------------
/demos/main_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Magic-JD/is-fast/eea32cf82df4e0a575610d9d9fbafed26ec222d9/demos/main_demo.gif
--------------------------------------------------------------------------------
/demos/stack-overflow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Magic-JD/is-fast/eea32cf82df4e0a575610d9d9fbafed26ec222d9/demos/stack-overflow.gif
--------------------------------------------------------------------------------
/demos/stocks.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Magic-JD/is-fast/eea32cf82df4e0a575610d9d9fbafed26ec222d9/demos/stocks.gif
--------------------------------------------------------------------------------
/demos/what.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Magic-JD/is-fast/eea32cf82df4e0a575610d9d9fbafed26ec222d9/demos/what.gif
--------------------------------------------------------------------------------
/dist-workspace.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = ["cargo:."]
3 |
4 | # Config for 'dist'
5 | [dist]
6 | # The preferred dist version to use in CI (Cargo.toml SemVer syntax)
7 | cargo-dist-version = "0.28.0"
8 | # CI backends to support
9 | ci = "github"
10 | # The installers to generate for each app
11 | installers = ["shell", "powershell", "homebrew", "msi"]
12 | # A GitHub repo to push Homebrew formulas to
13 | tap = "magic-jd/homebrew-tap"
14 | # Target platforms to build apps for (Rust target-triple syntax)
15 | targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc", "i686-unknown-linux-gnu", "i686-unknown-linux-musl"]
16 | # Path that installers should place binaries in
17 | install-path = "CARGO_HOME"
18 | # Publish jobs to run in CI
19 | publish-jobs = ["homebrew"]
20 | # Whether to install an updater program
21 | install-updater = false
22 |
23 | [dist.github-custom-runners]
24 | global = "ubuntu-latest"
25 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "is-fast";
3 |
4 | inputs = {
5 | nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
6 | };
7 |
8 | outputs =
9 | { self, nixpkgs, ... }:
10 | let
11 | forAllSystems =
12 | function:
13 | nixpkgs.lib.genAttrs [
14 | "x86_64-linux"
15 | "aarch64-linux"
16 | "x86_64-darwin"
17 | "aarch64-darwin"
18 | ] (system: function nixpkgs.legacyPackages.${system});
19 |
20 | darwinDeps =
21 | pkgs: with pkgs; [
22 | darwin.apple_sdk.frameworks.SystemConfiguration
23 | libiconv
24 | ];
25 | in
26 | {
27 | devShells = forAllSystems (pkgs: {
28 | default = pkgs.mkShell {
29 | name = "is-fast";
30 | packages =
31 | (with pkgs; [
32 | cargo
33 | cargo-edit
34 | clippy
35 | rustc
36 | ])
37 | ++ (pkgs.lib.optional pkgs.stdenvNoCC.isDarwin (darwinDeps pkgs));
38 | };
39 | });
40 | formatter = forAllSystems (pkgs: pkgs.nixfmt-rfc-style);
41 | packages = forAllSystems (pkgs: {
42 | is-fast =
43 | with pkgs;
44 | let
45 | fs = lib.fileset;
46 | sourceFiles = fs.unions [
47 | ./tests
48 | ./Cargo.lock
49 | ./Cargo.toml
50 | ./src
51 | ];
52 |
53 | cargoToml = with builtins; (fromTOML (readFile ./Cargo.toml));
54 | pname = cargoToml.package.name;
55 | version = cargoToml.package.version;
56 | cargoLock.lockFile = ./Cargo.lock;
57 | darwinBuildInputs = (darwinDeps pkgs);
58 | in
59 | pkgs.rustPlatform.buildRustPackage {
60 | inherit pname version cargoLock;
61 | src = fs.toSource {
62 | root = ./.;
63 | fileset = sourceFiles;
64 | };
65 | nativeBuildInputs = [
66 | clippy
67 | rustfmt
68 | openssl
69 | ];
70 | buildInputs = [ ] ++ lib.optionals stdenv.isDarwin darwinBuildInputs;
71 |
72 | # Skip fake home for now
73 | app_test = ''
74 | cargo fmt --manifest-path ./Cargo.toml --all --check
75 | cargo clippy -- --deny warnings
76 | cargo test -- --skip=generate_config::tests::test_run_creates_config_file
77 | '';
78 |
79 | preBuildPhases = [ "app_test" ];
80 |
81 | };
82 | default = self.packages.${pkgs.system}.is-fast;
83 | });
84 | apps = forAllSystems (pkgs: {
85 | default = {
86 | type = "app";
87 | program = "${self.packages.${pkgs.system}.is-fast}/bin/is-fast";
88 | };
89 | });
90 | };
91 | }
92 |
--------------------------------------------------------------------------------
/scripts/is-fast-projects.ps1:
--------------------------------------------------------------------------------
1 | # Check stock prices using is-fast. Args must be a stock symbol (e.g., AAPL).
2 | function isf_stock {
3 | param (
4 | [string]$symbol
5 | )
6 | is-fast `
7 | --direct "https://finance.yahoo.com/quote/$symbol/" `
8 | --selector "section.container > h1, span.base" `
9 | --piped `
10 | --no-cache `
11 | --pretty-print="margin:5"
12 | }
13 |
14 | # What is something? Give it a word or a name, and it will return the first Wikipedia paragraph of that thing.
15 | function isf_what {
16 | param (
17 | [string]$query
18 | )
19 | is-fast `
20 | --direct "https://en.wikipedia.org/wiki/$query" `
21 | --selector "div.mw-content-ltr > p" `
22 | --color=always `
23 | --piped `
24 | --nth-element 1 `
25 | --pretty-print="margin:20"
26 | }
27 |
28 | # Search Stack Overflow, showing only the question and answer text.
29 | function isf_so {
30 | param (
31 | [string]$query
32 | )
33 | $QUESTION = is-fast $query --site "www.stackoverflow.com" --selector "div.question .js-post-body" --color=always --pretty-print="margin:20,title:Question" --piped --flash-cache
34 | $ANSWER = is-fast --last --selector "div.accepted-answer .js-post-body" --color=always --pretty-print="margin:20,title:Answer" --piped --flash-cache
35 | Write-Output @"
36 |
37 | $QUESTION
38 | $ANSWER
39 |
40 | "@
41 | }
42 |
43 | # Get a simple definition of a word.
44 | function isf_define {
45 | param (
46 | [string]$word
47 | )
48 | is-fast `
49 | --direct "https://www.merriam-webster.com/dictionary/$word" `
50 | --selector "div.sb" `
51 | --nth-element 1 `
52 | --color=always `
53 | --pretty-print="margin:20,title:$($word.ToUpper())" `
54 | --piped
55 | }
56 |
57 | # Check the current number of stars in the repo.
58 | function isf_stars {
59 | is-fast `
60 | --direct "https://github.com/Magic-JD/is-fast" `
61 | --selector "span#repo-stars-counter-star" `
62 | --pretty-print="title:Current Stars,margin:5" `
63 | --color=always `
64 | --piped `
65 | --no-cache
66 | }
67 |
68 | # Checks the Google page to get the information for the info box.
69 | function isf_quick {
70 | param (
71 | [string]$query
72 | )
73 | is-fast `
74 | --direct "https://www.google.com/search?q=$query" `
75 | --piped `
76 | --selector="div.ezO2md" `
77 | --ignore="a" `
78 | --no-block `
79 | --nth-element 1 `
80 | --pretty-print="margin:20" `
81 | --color=always `
82 | --no-cache
83 | }
84 |
--------------------------------------------------------------------------------
/scripts/is-fast-projects.sh:
--------------------------------------------------------------------------------
1 | # A number of scripts that I created to show the flexibility of is-fast for scripting. This is not meant to be an exhaustive list of all the things that is-fast can do,
2 | # but rather just some examples of neat functions that I put together to show how you could use this tool in your workflow.
3 |
4 | # Check stock prices using is-fast. Args must be a stock symbol (e.g. AAPL).
5 | # Insert the stock symbol into the url
6 | # Select span elements with the base class
7 | # We want this output to display directly in the terminal, rather than being shown in the tui so we use --piped.
8 | # By default these spans are not colored, but if displaying in the terminal it is fine to include ansi-codes
9 | isf_stock() {
10 | is-fast \
11 | --direct "https://finance.yahoo.com/quote/${1}/" \
12 | --selector "section.container > h1, span.base" \
13 | --piped \
14 | --no-cache \
15 | --color=always \
16 | --style-element="span.txt-negative:fg=red" \
17 | --style-element="span.txt-positive:fg=green" \
18 | --pretty-print="margin:5"
19 | }
20 |
21 | # What is something? Give it a word or a name and it will return the first wikipedia paragraph of that thing. This will work if there is a wikipedia article with that
22 | # exact name. Works for most people and things. E.g. isf_what albert einstein
23 | isf_what() {
24 | is-fast \
25 | --direct "en.wikipedia.org/wiki/${*}" \
26 | --selector "div.mw-content-ltr > p" \
27 | --color=always \
28 | --piped \
29 | --nth-element 1 \
30 | --pretty-print="margin:20" \
31 | --style-element="sup:size=half"
32 | # We get the first paragraph with content only from the child p's of div.mw-content-ltr
33 | # note: the first paragraph can be achieved with css selectors only, but is sometimes empty on the site - this then avoids any issues with the selected paragraph being empty.)
34 | }
35 |
36 | # Search stack overflow, showing only the question and answer text. Note must use --last for this, as the history output/order is not deterministic.
37 | isf_so() {
38 | QUESTION=$(is-fast ${*} --site "www.stackoverflow.com" --selector "div.question .js-post-body" --color=always --pretty-print="margin:20,title:Question" --piped --flash-cache) # Find the question content.
39 | ANSWER=$(is-fast --last --selector "div.accepted-answer .js-post-body" --color=always --pretty-print="margin:20,title:Answer" --piped --flash-cache) # Separately find the answer content.
40 | cat << EOF # Format as desired
41 |
42 | $QUESTION
43 | $ANSWER
44 |
45 | EOF
46 | }
47 |
48 | # Get a simple definition of a word.
49 | # NOTE capitalization is specific for ZSH - for BASH change to ${1^}
50 | isf_define() {
51 | is-fast \
52 | --direct "www.merriam-webster.com/dictionary/${1}" \
53 | --selector "div.sb" \
54 | --nth-element 1 \
55 | --color=always \
56 | --pretty-print="margin:20,title:${(C)1}" \
57 | --piped
58 | }
59 |
60 | # Check the current number of stars in the repo.
61 | isf_stars() {
62 | is-fast \
63 | --direct "https://github.com/Magic-JD/is-fast" \
64 | --selector "span#repo-stars-counter-star" \
65 | --pretty-print="title:Current Stars,margin:5" \
66 | --color=always \
67 | --piped \
68 | --no-cache
69 | }
70 |
71 | # Checks the google page to get the information for the info box, works for most conversions (with thanks to d3-X-t3r for this suggestion)
72 | # E.g. isf_quick 200f to c
73 | # isf_quick 30 GBP to USD
74 | # isf_quick Weather Berlin
75 | isf_quick() {
76 | is-fast \
77 | --direct "https://www.google.com/search?q=${*}" \
78 | --piped \
79 | --selector="div.ezO2md" \
80 | --ignore="a" \
81 | --no-block \
82 | --nth-element 1 \
83 | --pretty-print="margin:20" \
84 | --color=always \
85 | --no-cache
86 | }
87 |
--------------------------------------------------------------------------------
/src/actions/generate_config.rs:
--------------------------------------------------------------------------------
1 | use crate::config::files::config_path;
2 | use crate::config::load::DEFAULT_CONFIG;
3 | use std::fs;
4 |
5 | pub fn run() {
6 | println!("Generating config file...");
7 | let config_path = config_path();
8 | if config_path.exists() {
9 | eprintln!("Config file already exists at {config_path:?}");
10 | } else if let Err(message) = fs::write(&config_path, DEFAULT_CONFIG)
11 | .map_err(|e| format!("Error writing config file: {e}"))
12 | {
13 | eprintln!("{message}");
14 | } else {
15 | println!("Config file generated at {config_path:?}");
16 | }
17 | }
18 |
19 | #[cfg(test)]
20 | mod tests {
21 | use super::*;
22 | use serial_test::serial;
23 | use std::env;
24 | use std::path::PathBuf;
25 | use tempfile::TempDir;
26 |
27 | #[test]
28 | #[serial]
29 | fn test_run_creates_config_file() {
30 | let temp_dir = TempDir::new().expect("Failed to create temp dir");
31 |
32 | let fake_home = convert_to_canon(temp_dir);
33 |
34 | env::set_var("XDG_CONFIG_HOME", &fake_home);
35 | run();
36 |
37 | let config_path = fake_home.join("is-fast/config.toml");
38 | assert!(config_path.exists(), "Config file should be created");
39 | }
40 |
41 | fn convert_to_canon(temp_dir: TempDir) -> PathBuf {
42 | if temp_dir.path().is_relative() {
43 | fs::canonicalize(temp_dir.path()).expect("Failed to canonicalize temp dir")
44 | } else {
45 | temp_dir.path().to_path_buf()
46 | }
47 | }
48 |
49 | #[test]
50 | #[serial]
51 | fn test_run_fails_if_config_exists() {
52 | use std::env;
53 | use std::fs;
54 |
55 | let temp_dir = TempDir::new().expect("Failed to create temp dir");
56 | let fake_home = convert_to_canon(temp_dir);
57 |
58 | env::set_var("XDG_CONFIG_HOME", &fake_home);
59 |
60 | let config_path = fake_home.join("is-fast/config.toml");
61 | fs::create_dir_all(config_path.parent().unwrap()).unwrap();
62 | fs::write(&config_path, "existing content").unwrap();
63 |
64 | let output = std::panic::catch_unwind(run);
65 |
66 | assert!(output.is_ok(), "Function should not panic");
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/actions/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod generate_config;
2 | pub mod prepare_pages;
3 |
--------------------------------------------------------------------------------
/src/actions/prepare_pages.rs:
--------------------------------------------------------------------------------
1 | use crate::cli::command::OpenArgs;
2 | use crate::config::load::Config;
3 | use crate::database::history_database::get_latest_history;
4 | use crate::errors::error::IsError;
5 | use crate::search_engine::link::HtmlSource::{FileSource, LinkSource};
6 | use crate::search_engine::link::{File, HtmlSource, Link};
7 | use crate::search_engine::search::find_links;
8 | use once_cell::sync::Lazy;
9 |
10 | static SITE: Lazy