Get most things semi-working except the final sort

[?]
Jan 16, 2022, 4:02 AM
QEKHTVB7CH754NUMHJ3AKVGRNKW3KOAKMALS7B3ZNKCUEH6GP4HQC

Dependencies

  • [2] LLFG625I Should be all the database functions I need
  • [3] HMOBTVJ4 Initialize crate and add expected dependencies
  • [4] AV73DYWQ Initial functions for using sqlite in async environment
  • [5] 5Y7ZXB53 Start picker UI
  • [6] C376NCOV Prevent flooding the website (still need to better mimic browser requests)
  • [7] G5YNDTPH Cargo clippy
  • [8] LPVC545K Get the scraper part of this program working
  • [9] AVLXUT3R Make the boxed errors Send
  • [10] RNW6D777 Minor tidy
  • [11] KUANIPWF Add function for adding name to database
  • [12] YCWYAX6K Functions for scraping names integrated enough to test (they don't work yet)
  • [*] PQ4BG3ZJ The web scrape functions

Change contents

  • edit in src/pick.rs at line 1
    [3.32]
    [3.0]
    use crate::gather;
  • replacement in src/pick.rs at line 6
    [3.62][3.62:95]()
    event::{Event, EventStream},
    [3.62]
    [3.95]
    event::{Event, EventStream, KeyCode},
  • replacement in src/pick.rs at line 8
    [3.138][2.0:41]()
    ExecutableCommand, QueueableCommand,
    [3.138]
    [3.160]
    terminal, ExecutableCommand, QueueableCommand,
  • replacement in src/pick.rs at line 10
    [3.163][3.163:211]()
    use futures::stream::{self, Stream, StreamExt};
    [3.163]
    [3.277]
    use futures::stream::{self, StreamExt};
    use std::borrow::Borrow;
    use tokio::sync::mpsc;
  • replacement in src/pick.rs at line 14
    [3.278][2.42:113]()
    pub async fn pick(db: &AsyncConnection, count: u16) -> DynResult<()> {
    [3.278]
    [2.113]
    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;
    }
    }
  • replacement in src/pick.rs at line 66
    [2.188][2.188:227]()
    let (mut input, done) = events();
    [2.188]
    [2.227]
    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)
    };
    }
  • edit in src/pick.rs at line 102
    [2.262]
    [3.348]
    terminal::disable_raw_mode()?;
  • edit in src/pick.rs at line 104
    [3.359]
    [3.359]
    }
    struct CompareContext<'a, W> {
    db: &'a AsyncConnection,
    events: &'a mut stream::Abortable<EventStream>,
    out: &'a mut W,
    needs_newline: bool,
  • edit in src/pick.rs at line 112
    [3.361]
    [3.361]
    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);
    }
    }
    }
  • edit in src/pick.rs at line 150
    [3.362]
    [3.362]
    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),
    }
    }
    }
  • replacement in src/names_database.rs at line 131
    [2.2735][2.2735:2767]()
    async fn insert_preference(
    [2.2735]
    [2.2767]
    pub async fn insert_preference(
  • replacement in src/main.rs at line 1
    [3.49][3.0:32]()
    use clap::{Parser, Subcommand};
    [3.49]
    [3.32]
    use clap::Parser;
  • edit in src/main.rs at line 6
    [3.987][3.34:72](),[3.2010][3.34:72]()
    use names_database::AsyncConnection;
  • replacement in src/main.rs at line 14
    [3.166][3.166:216](),[3.216][3.0:29](),[3.29][3.244:331](),[3.244][3.244:331](),[3.331][3.30:37](),[3.37][3.988:1019](),[3.1019][3.121:128]()
    #[clap(subcommand)]
    command: Commands,
    }
    #[derive(Debug, Subcommand)]
    enum Commands {
    Gather {
    #[clap(arg_enum)]
    gender: gather::Gender,
    },
    Pick {
    count: u16,
    },
    [3.166]
    [3.337]
    count: u16,
    #[clap(long, arg_enum)]
    gather: Option<gather::Gender>,
  • replacement in src/main.rs at line 23
    [3.112][3.395:427](),[3.1398][3.395:427](),[3.427][3.1399:1402](),[3.466][3.1399:1402](),[3.1402][3.428:444](),[3.444][3.345:410](),[3.226][3.1548:1569](),[3.410][3.1548:1569](),[3.534][3.1548:1569](),[3.1548][3.1548:1569](),[3.1569][3.227:268](),[3.268][3.575:779](),[3.575][3.575:779](),[3.779][2.5272:5371]()
    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?;
    }
    [3.112]
    [3.1643]
    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)
  • replacement in src/main.rs at line 32
    [3.1653][3.780:795](),[3.795][3.1653:1659](),[3.1653][3.1653:1659]()
    Ok(())
    }
    [3.1653]
    [3.466]
    }?;
    Ok(())
  • edit in src/gather.rs at line 28
    [3.539][3.539:592]()
    dbg!(fetch.current_letter, &fetch.form);