use std::io::Write;
use std::path::PathBuf;
use anyhow::Result;
use clap::Args;
use libsail::collection::Indexable;
use libsail::format::Format;
use libsail::index::Reader;
use libsail::select::Selection;
use crate::cli::{FormatArg, ReadArgs};
use crate::commands::draw_size;
use crate::input::{Backend, Input, Inputs, Needs, dispatch, indexed, indexed_path};
use crate::output::{emit, write_framed, writer};
#[derive(Args)]
pub struct SkimArgs {
#[arg(default_value = "-")]
pub input: PathBuf,
#[arg(short = 'n', long, conflicts_with = "fraction")]
pub count: Option<usize>,
#[arg(short = 'p', long)]
pub fraction: Option<f64>,
#[arg(long)]
pub seed: Option<u64>,
#[arg(long, value_enum)]
pub format: Option<FormatArg>,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[command(flatten)]
pub read: ReadArgs,
}
impl SkimArgs {
pub fn run(self) -> Result<()> {
let inputs = Inputs::plan(
std::slice::from_ref(&self.input),
self.format,
self.read,
Needs::Sweep,
)?;
let entry = &inputs.entries()[0];
let out = writer(self.output.as_deref())?;
let (count, fraction) = (self.count, self.fraction);
let seed = self.seed.unwrap_or_else(rand::random);
match inputs.backend() {
Backend::Stream => {
let held = total(entry, inputs.format())?;
let n = draw_size(count, fraction, held)?;
let mut drawn = Selection::new(held, n, seed).peekable();
let mut reader = Reader::new(entry.reader()?, inputs.format());
let mut at = 0;
let mut out = out;
while reader.advance()? {
if drawn.peek() == Some(&at) {
drawn.next();
write_framed(inputs.format(), reader.record(), &mut out)?;
}
at += 1;
}
out.flush()?;
Ok(())
}
Backend::Indexed => {
let path = indexed_path(entry)?;
let mut out = out;
indexed!(inputs.format(), path, |collection| {
let n = draw_size(count, fraction, collection.len())?;
for at in Selection::new(collection.len(), n, seed) {
let record = collection.record(at)?.expect("a counted record");
write_framed(inputs.format(), &record, &mut out)?;
}
});
out.flush()?;
Ok(())
}
Backend::Memory => {
dispatch!(inputs.format(), entry, |collection, _size, _name, write| {
let n = draw_size(count, fraction, collection.len())?;
emit(collection.sample_in_order(n, seed), write, out)
})
}
}
}
}
fn total(entry: &Input, format: Format) -> Result<usize> {
if let Some(path) = entry.path() {
let at = libsail::index::path_for(path);
if libsail::index::is_current(&at, path)? {
return Ok(libsail::index::count_in(&at)?);
}
}
let mut reader = Reader::new(entry.reader()?, format);
let mut held = 0;
while reader.advance()? {
held += 1;
}
Ok(held)
}