Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: Pre-defined types for stdout and stderr #8

Closed
schneems opened this issue Sep 15, 2024 · 0 comments
Closed

Idea: Pre-defined types for stdout and stderr #8

schneems opened this issue Sep 15, 2024 · 0 comments

Comments

@schneems
Copy link
Collaborator

The current API looks like this in it's fully generic form:

use bullet_stream::{Print, state::{Bullet, Header}};
use std::io::Write;

let mut not_started = Print::new(std::io::stdout());
let output = start_buildpack(not_started);

output.bullet("Ruby version").sub_bullet("Installing Ruby").done();

fn start_buildpack<W>(mut output: Print<Header<W>>) -> Print<Bullet<W>>
 where W: Write + Send + Sync + 'static {
     output.h2("Example Buildpack")
}

The ergonomic problem is that doing this every time is a lot of typing, so it's easier to define a concrete type like:

use std::io::Stdout;

fn start_buildpack<W>(mut output: Print<Header<Stdout>>) -> Print<Bullet<Stdout>>{
     output.h2("Example Buildpack")
}

The most common outputs are stdout and stderr, so we could make type aliases for them so you could use Print<Header> instead of needing to type Print<Header<Stdout>>, but we could take it even further by removing the need to have a Print prefix. The implementation would look kinda like this:

//! Generic aliases for stdout
//!
//! ```rust
//! use bullet_stream::Print;
//! use bullet_stream::stdout::{Header, Bullet, SubBullet,  , Background};
//!
//! let mut output = Print::new(std::io::stdout());
//! start_buildpack(output).done();
//!
//! fn start_buildpack(mut output: Header) -> Bullet {
//!     output.h2("Example Buildpack")
//!}
//! ```
use crate::Print;
use std::io::Stdout;

pub type Header = Print<crate::state::Header<Stdout>>;
pub type Bullet = Print<crate::state::Bullet<Stdout>>;
pub type SubBullet = Print<crate::state::SubBullet<Stdout>>;
pub type Stream = Print<crate::state::Stream<Stdout>>;
pub type Background = Print<crate::state::Background<Stdout>>;

So we've gone from

fn start_buildpack<W>(mut output: Print<Header<W>>) -> Print<Bullet<W>>
 where W: Write + Send + Sync + 'static {
     output.h2("Example Buildpack")
}

to

fn start_buildpack(mut output: Header) -> Bullet {
     output.h2("Example Buildpack")
}

Which is significant typing savings.

However, the impact is that it would lock each file into a specific output type, if we provide a stdout and stderr variant they could swap between the two easilly. It diverges from the interface of bullet_stream::state module as they would have the same name but mean semantically different things (i.e. one is a Print<Header> while the other is a Header). Another impact is that the person reading the code isn't immediately clear what the argument is being used for. I.e. a Print<Header<Stdout>> is quite descriptive, a Header could be an HTTP header or a markdown streaming header (as in this case). Basically I don't know if the indirection is helpful or not.

So it seems like making this available would make the code possibly easier to write, but possibly harder to read (if the user is unfamiliar with the API). I'm leaning towards not doing this. It's not that hard for people to do this themselves if they wanted in their own repos. But maybe I would like to document these use cases and patterns for people who are newer to Rust.

@schneems schneems mentioned this issue Sep 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant