Skip to content

Comments

feat: Pack IO options#246

Open
wmiles-sgl wants to merge 9 commits intoQuantco:mainfrom
wmiles-sgl:io-options
Open

feat: Pack IO options#246
wmiles-sgl wants to merge 9 commits intoQuantco:mainfrom
wmiles-sgl:io-options

Conversation

@wmiles-sgl
Copy link

Motivation

Add some additional options for pack output/unpack input, as discussed in #245.

Changes

  • Support '-' for stdout/stdin for pack/unpack
  • Add 'no-tar' option to pack that skips archiving and delivers output to a directory instead
  • If input to unpack is a directory, install directly from it

Notes

  • This is my first time working with rust; apologies if the implementation is non-idiomatic. Any feedback would be appreciated.
  • Documentation is not yet updated.
  • In the interests of code simplicity, I have not attempted to handle pack-to-stdout and verbose; this combination will probably do something unhelpful.
  • I'm uncertain how to implement a test case for the stdio features -- it will need to fork additional processes instead of directly making library calls.
  • In this implementation, '--no-tar' supersedes '--create-executable'. We could add an error if both are set.

@wmiles-sgl wmiles-sgl changed the title Pack IO options feat: Pack IO options Nov 27, 2025
@github-actions github-actions bot added the enhancement New feature or request label Nov 27, 2025
@wmiles-sgl wmiles-sgl marked this pull request as ready for review November 27, 2025 21:29
Copy link
Member

@pavelzw pavelzw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some comments

}

fn default_output_file(platform: Platform, create_executable: bool) -> PathBuf {
fn default_output_file(platform: Platform, create_executable: bool, no_tar: bool) -> PathBuf {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can make an enum

enum OutputMode {
    Default,
    CreateExecutable,
    NoTar,
}

and then match here.

Copy link
Member

@delsner delsner Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum OutputMode {
    Default,
    CreateExecutable,
    DirectoryOnly,
}

How about DirectoryOnly? Discussed with @pavelzw and we found this more intuitive. (Applies to CLI flags as well.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like "mutually exclusive flags mapped to members of an enum" isn't directly supported by clap. clap-rs/clap#2621

Should I work around this by building the enum from if (flag is set) logic?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would still use the flags as boolean but just convert them at the beginning to this enum then

src/pack.rs Outdated
fn open_output_file(
target: &Path,
ext: Option<&str>,
) -> Result<Either<Counter<std::io::Stdout>, Counter<std::fs::File>>> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just use an enum instead of introducing another dependency (that basically mimics what an enum in rust does)

enum Output {
    Stdout(std::io::Stdout),
    File(std::fs::File),
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Either type propagates the Write trait through to its member types -- basically I'm using it to avoid having to either (a) re-implement the Write trait via for the enum, or (b) explicitly resolving the enum at every point of use. It seemed like the least evil solution to import a crate that already had the code for (a).

Perhaps I'm missing something though? Is there a cleaner way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in how many places are we using that it's Write? I would say it's easier to impl Write for Output and just implement it there instead of pulling in either as a dependency. if it's only few places, i'm also fine with just doing a match directly (i.e. option b you proposed)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About a dozen; final_executable.write_all() gets a fair amount of traffic. Re (a), at least from my vantage point, the trickier bit is that I don't know which calls on Write are performance sensitive to tar::Builder, so there's a risk that a minimal impl Write causes slowdowns; a full implementation is probably the way to go to give the compiler the best information.

I'll pull the relevant bits from either in here, then.

src/pack.rs Outdated
fn create_tarball(input_dir: &Path, archive_target: &Path) -> Result<()> {
let outfile = std::fs::File::create(archive_target).map_err(|e| {
fn open_output_file(
target: &Path,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the target can be made an enum as well

enum Target {
    Stdout,
    File(PathBuf)
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean for this to be a library API change in PackOptions? Given that the PathBuf->Writer mapping is resolved in only two locations in pack, it's true we could do a PathBuf -> Target -> Write approach with the "stdout" special case identified early, but I'm not sure if the extra formalism will buy much inside pack.rs vs doing the text analysis later in open_output_file.

#[cfg(not(target_os = "windows"))]
use std::os::unix::fs::PermissionsExt as _;

use countio::Counter;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used to count output bytes when written since we can't check the file size of stdout after packing is completed. The intent was to provide a consistent internal interface to minimize the number of if special cases.

@wmiles-sgl
Copy link
Author

Sorry to bother, what am I missing to move this PR forward? Thanks!

@pavelzw
Copy link
Member

pavelzw commented Jan 15, 2026

sorry for the delay, this pr slipped out of my inbox. i'll look into it next week!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants