QEKHTVB7CH754NUMHJ3AKVGRNKW3KOAKMALS7B3ZNKCUEH6GP4HQC
LLFG625ISXV7P2LUOFUMFIM5V5RJDKWCL47XX43X2CGILVAUQPVQC
5Y7ZXB53EMG34VZI5VQIZ77TZKBB4V6BGIILFZ5ZY73QYK4V2YHQC
G5YNDTPHPYRZZIGWLX2ZLVFRJ3B2NGAPVESSMTO7YEABDFIIE66AC
AV73DYWQ2UVZJYTN3UIW3TDMWNAETLXZRD7VHKTQLFQGHKJ5KI6QC
HMOBTVJ4FEPZWMUV2JDPZYH3EFCW6ED5M6KKNKQJQZVKTDAKTVFQC
YCWYAX6K2DJKT7FO4IAYL6HJOIJLYFKAPGLFJ5XMYSYAS42LP3FQC
LPVC545KD6R5KNOTDPFDOD5FVA3KFBZARKVOLLXJXW3YK3RMNZKQC
KUANIPWFDXDFKJ2LH4FQ6APYOPLNYJ5LVGLSBSC75WUUZPFHILCAC
RNW6D77774CYWWM7JIFXI5TGKBOU6ADJIEZB5N2FXGDCTLUXEQZQC
AVLXUT3ROXQ2PLLTB5XHPBY2BI5PQRRFJTG2GGZTL7K4VEZZXF3AC
PQ4BG3ZJU5SY6XQDJ3SURLAOWGXGXLONIRZNUJXZVMHLYXWOORXQC
pub async fn pick(db: &AsyncConnection, count: u16) -> DynResult<()> {
pub async fn pick(
db: &AsyncConnection,
count: u16,
gather: Option<gather::Gender>,
) -> DynResult<()> {
let (ntx, mut nrx) = mpsc::channel(256);
if let Some(gender) = gather {
let tg = ntx.clone();
let gdb = db.clone();
tokio::spawn(async move {
let mut incoming = gather::all_names(gender);
while let Some(name) = incoming.recv().await {
let (id, fresh) = match gdb.add_name(name.clone()).await {
Ok(r) => r,
Err(_) => break,
};
if fresh {
match tg.send((name, id)).await {
Ok(_) => (),
Err(_) => break,
}
};
}
});
}
{
let db = db.clone();
tokio::spawn(async move {
for name in db
.list_names()
.await
.ok()
.into_iter()
.flat_map(Vec::into_iter)
{
match ntx.send(name).await {
Ok(_) => (),
Err(_) => break,
}
}
});
}
let mut shortlist = Vec::with_capacity(count as usize * 2);
for _ in 0..(count * 2) {
if let Some(name) = nrx.recv().await {
shortlist.push(name);
} else {
break;
}
}
let (mut input, done) = events();
terminal::enable_raw_mode()?;
let (mut input, _done) = events();
let mut comparator = CompareContext {
db,
events: &mut input,
out: &mut stderr,
needs_newline: false,
};
'sponge: loop {
'fetch: while shortlist.len() < count as usize * 2 {
match nrx.recv().await {
Some(name) => {
shortlist.push(name);
}
None => {
break 'fetch;
}
}
}
if shortlist.len() < count.into() {
break 'sponge;
}
let mut i: *mut (String, i64) = &mut shortlist[0];
let j: *mut (String, i64) = {
let len = shortlist.len() - 1;
&mut shortlist[len]
};
let pivot: *mut (String, i64) = &mut shortlist[count as usize];
while i < pivot {
i = unsafe { comparator.partition(i, j, pivot).await? };
}
unsafe {
shortlist.truncate(1 + i.offset_from(&shortlist[0]) as usize)
};
}
impl<'a, W: std::io::Write> CompareContext<'a, W> {
async unsafe fn partition(
&mut self,
lo: *mut (String, i64),
hi: *mut (String, i64),
mut pivot: *mut (String, i64),
) -> DynResult<*mut (String, i64)> {
let mut i = lo.offset(-1);
let mut j = hi.offset(1);
loop {
loop {
i = i.offset(1);
match self.compare(&*i, &*pivot).await? {
std::cmp::Ordering::Less => (),
_ => break,
}
}
loop {
j = j.offset(-1);
match self.compare(&*j, &*pivot).await? {
std::cmp::Ordering::Greater => (),
_ => break,
}
}
if i >= j {
return Ok(j);
} else {
if pivot == i {
pivot = j;
} else if pivot == j {
pivot = i;
}
i.swap(j);
}
}
}
async fn compare(
&mut self,
a: &(String, i64),
b: &(String, i64),
) -> DynResult<std::cmp::Ordering> {
let (a, b) = (a, b);
match self.db.compare(a.1, b.1).await? {
None => {
if self.needs_newline {
self.out.queue(cursor::MoveToNextLine(1))?;
} else {
self.needs_newline = true;
}
print_pair(self.out, a.0.borrow(), b.0.borrow())?;
let (result, better, worse) = loop {
if let Event::Key(key) =
self.events.next().await.ok_or_else(|| {
<std::io::ErrorKind as Into<std::io::Error>>::into(
std::io::ErrorKind::BrokenPipe,
)
})??
{
match key.code {
KeyCode::Left => {
break (std::cmp::Ordering::Greater, a.1, b.1);
}
KeyCode::Right => {
break (std::cmp::Ordering::Less, b.1, a.1);
}
KeyCode::Esc => {
return Err(<std::io::ErrorKind as Into<
std::io::Error,
>>::into(
std::io::ErrorKind::Interrupted
)
.into());
}
KeyCode::Char('c') => {
if key.modifiers.contains(
crossterm::event::KeyModifiers::CONTROL,
) {
return Err(<std::io::ErrorKind as Into<
std::io::Error,
>>::into(
std::io::ErrorKind::Interrupted,
)
.into());
}
}
_ => (),
}
}
};
let tdb = self.db.clone();
tokio::spawn(async move {
tdb.insert_preference(better, worse).await
});
Ok(result)
}
Some(c) => Ok(c),
}
}
}
#[clap(subcommand)]
command: Commands,
}
#[derive(Debug, Subcommand)]
enum Commands {
Gather {
#[clap(arg_enum)]
gender: gather::Gender,
},
Pick {
count: u16,
},
count: u16,
#[clap(long, arg_enum)]
gather: Option<gather::Gender>,
args.command.run(&db).await
}
impl Commands {
async fn run(&self, db: &AsyncConnection) -> DynResult<()> {
match self {
Self::Gather { gender } => {
let mut incoming = gather::all_names(*gender);
while let Some(name) = incoming.recv().await {
db.add_name(name).await?;
}
}
Self::Pick { count } => {
pick::pick(db, *count).await?;
}
match pick::pick(&db, args.count, args.gather).await {
Ok(r) => Ok(r),
Err(e) => {
use crossterm::ExecutableCommand;
let mut stderr = std::io::stderr();
stderr.execute(crossterm::cursor::Show).ok();
crossterm::terminal::disable_raw_mode().ok();
Err(e)