
Show HN: Hunter – Lag-free terminal file browser in Rust - rabito
https://github.com/rabite0/hunter
======
octobanana
This looks neat, I'll give it a go later today.

One of the features I use Ranger for is to visually select multiple music
files and then open them with mpv to play. Would that be something Hunter
supports, or would support in the future?

What has been the most challenging aspect you've encountered so far while
building this?

Thanks for sharing!

~~~
rabito
Hi, sorry for the late answer.

Yes, you can select multiple files with space and open them all at once using
the exec feature (type "!"). When you press tab it will insert "$s", which
will then be substituted with either the normal selection, or when multiple
files are selected with those multiple files. This works pretty much like in
ranger. It doesn't work when you just open them by "entering" them. I'm not
sure if that even makes sense since different file types could be selected and
it's not clear what the right thing to do would be. Maybe opening them one
after another? I'm not actually sure what ranger does in that case.

The most difficult thing.. That's actually hard to say since I started this to
learn more about Rust and it's the largest project I've written so far at
>8000 loc, so basically everything. :) But specifically, I guess getting the
asynchronous stuff working right was the hardest part. hunter loads everything
asynchronously and on demand, like the metadata and directory size. This is to
speed up loading times. I'm still not sure it's 100% correct. Rust makes it
easy to prevent data races, and it's very strict about what you can share
across threads and how you do it, but it doesn't prevent race conditions and
logic errors, and reasoning about concurrent programs is still hard.

I actually implemented my own Async type instead of using Futures or something
pre-made, because as far as I can tell especially futures come with a pretty
heavy binary size cost, which in turn takes time to load, making startup
slower on a slow HDD. Other than that just getting the design right was pretty
hard. Although the basic foundation is pretty solid I think.

Another reason I started this is to see how usable my TUI widgets are and when
I first scrolled through a directory listing at light speed I got somewhat
addicted. :) I kept those widgets really generic and they're easy to use in
the sense that you can for example get a ListView for your type by just
implementing a trait for it and they're very composable so that you can easily
get a TabView for your ListView and so on. My plan is to put all that in a
library eventually.

Error handling is another problem area. When I started I actually didn't make
use of any error handling at all, in the sense that I didn't return errors
from my functions, instead handling everything by just returning early, or
returning empty strings or whatever made sense. This got problematic after I
started heavily using mutexes, channels and other things that return errors,
since I couldn't use ? and most of the time there is really nothing to do
other than returning early with the error (and logging it), or skipping that
operation when it happens in a loop.

I then went the other extreme and started returning Results everywhere. The
problem is that right now there is just one single enum with all the dozens of
possible errors, so it's hard to say what error can actually happen. Another
problem is that I had to remove backtraces after implementing the Async type
since backtraces can't be shared between threads for some reason and now the
log viewer is pretty crippled. It's actually a "FoldView" and you could fold
those backtraces in and out to see what went wrong where, now it's pretty much
just a simple list. So yeah, splitting those error types is definitely on my
todo list. :)

EDIT: fixed typo

