Skip to content

Library API: Input Source #729

@eth-p

Description

@eth-p

I've been thinking of ideas for the library API that can provide more powerful functionality, while also reducing how much of the bat internals are exposed. Feedback would be appreciated.


I think we should rework/refactor input sourcing for bat-as-a-library. The current implementation only allows specifying input sources (via InputFile) from STDIN, the filesystem, and the embedded preview string.

For an API, we should have something a bit more generalized:

  • Accepts any Read trait as an input source.
  • Accepts any string as an input source.
  • Contains custom "properties" (see below)
  • Contains metadata (e.g. syntax name, file name, file encoding, etc.)

Properties:

For extensibility, there should be some way to add extra fields ("properties") to an input source. This would allow features to be implemented cleanly without significantly modifying the internal code. One example would be moving the git change data into a property, and passing the property to the Decoration impl instead of the InteractivePrinter.

There are a couple ways we could implement this:

Generics:

We could make everything generic, and put all the properties in a single struct.
This would be fairly performant since all the hard work is done at compile-time, but it wouldn't work well when mixing external code (e.g. custom decorations) with built-in features.

HashMap<&'static str, Any>:

Rather than having to know everything at compile-time, we could use a hashmap of Any. This would allow us to split off parts of bat into cargo features (e.g. git support), and allows for mixing and matching of various types of properties.

Both the HashMap and Any will incur a runtime performance hit, but that could easily be mitigated by using a builder that reads and caches the data once per input source:

trait SomethingBuilder {
    fn create(&mut self, input: &Input) -> Box<dyn Something>;
}

struct MySomethingBuilder {}
impl SomethingBuilder for MySomethingBuilder {
    fn create(&mut self, input: &Input) -> Box<dyn Something> {
        Something::new(
            input.properties()
                .get("thing")
                .and_then(|v| v.downcast_ref::<Thing>())
                .unwrap()
        )
    }
}

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions