Skip to content

Commit

Permalink
refactor: improvements (#6)
Browse files Browse the repository at this point in the history
- improve database
- improve test-only code
  • Loading branch information
markxoe authored Jun 8, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 37e1657 commit 3c7642a
Showing 6 changed files with 61 additions and 73 deletions.
5 changes: 3 additions & 2 deletions src/commands/derive_db.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ use log::info;

use crate::{
data::{
database,
database::Database,
maps::{link_map::LinkMap, page_map::PageMap},
parsers::{links, pages, redirects},
},
@@ -112,7 +112,8 @@ fn derive_db_command(args: DeriveDbArgs) {
.with_finish_message("Serialized and written to file")
.build();
spinner.enable_background();
database::serialize(output.as_str(), &links, &lookup);
Database::new(links, lookup).to_file(output.as_str());

spinner.finish();
}
}
23 changes: 13 additions & 10 deletions src/commands/interactive.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ use clap::Args;
use crate::{
data::{
algorithm::bfs,
database,
database::Database,
maps::page_map::{PageMap, PageMapResult},
},
indication::ProgressBuilder,
@@ -27,20 +27,23 @@ impl ArgExecutor for InteractiveArgs {
fn interactive_cmd(args: &InteractiveArgs) {
let db = args.db.to_string();

let spinner = ProgressBuilder::spinner()
.with_message("📝 Deserializing DB")
.build();
spinner.enable_background();
let data = database::deserialize(&db);
spinner.finish();
let db = {
let spinner = ProgressBuilder::spinner()
.with_message("📝 Deserializing DB")
.build();
spinner.enable_background();
let data = Database::from_file(&db);
spinner.finish();
data
};

println!(
"Usage: Enter a start page and a target page to find the shortest path between them
If you want to exit press ctrl+d or ctrl+c\n"
);

let links = data.links;
let lookup = data.pages;
let links = db.links;
let lookup = db.pages;

fn page_input_loop(prompt: &str, pages: &PageMap) -> Option<PageMapResult> {
loop {
@@ -75,12 +78,12 @@ If you want to exit press ctrl+d or ctrl+c\n"
let end = end.unwrap();

let (path, time) = {
let time_before = std::time::Instant::now();
let spinner = ProgressBuilder::spinner()
.with_message("Searching for path")
.build();
spinner.enable_background();

let time_before = std::time::Instant::now();
let path = bfs::find_shortest_path(start.id, end.id, &links);
let time = time_before.elapsed().as_millis();

31 changes: 19 additions & 12 deletions src/data/algorithm/bfs.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,21 @@ use log::debug;

use crate::data::maps::link_map::LinkMap;

fn rebuild_path(predecessors: HashMap<i32, i32>, start: i32, end: i32) -> Vec<i32> {
let mut out_path = VecDeque::new();
out_path.push_front(end);
let mut at = end;
while let Some(&node) = predecessors.get(&at) {
if at == start {
break;
}
out_path.push_front(node);
at = node;
}

Vec::from(out_path)
}

pub fn find_shortest_path(start: i32, end: i32, links: &LinkMap) -> Option<Vec<i32>> {
if start == end {
return Some(vec![start]);
@@ -12,6 +27,7 @@ pub fn find_shortest_path(start: i32, end: i32, links: &LinkMap) -> Option<Vec<i
let mut queue = VecDeque::new();
let mut predecessor = HashMap::new();
let mut visited = HashSet::new(); // note: having a set of visited nodes improves performance by a few percent while increasing memory usage

queue.push_back(start);
predecessor.insert(start, start);
visited.insert(start);
@@ -20,9 +36,11 @@ pub fn find_shortest_path(start: i32, end: i32, links: &LinkMap) -> Option<Vec<i

while let Some(at) = queue.pop_front() {
let neighbors = links.get(at);

if neighbors.is_none() {
continue;
}

for &neighbor in neighbors.unwrap() {
if visited.contains(&neighbor) {
continue;
@@ -33,19 +51,8 @@ pub fn find_shortest_path(start: i32, end: i32, links: &LinkMap) -> Option<Vec<i
visited.insert(neighbor);

if neighbor == end {
let mut out_path = VecDeque::new();
out_path.push_front(neighbor);
let mut at = neighbor;
while let Some(&node) = predecessor.get(&at) {
if at == start {
break;
}
out_path.push_front(node);
at = node;
}

debug!("Found path in {} steps", steps);
return Some(Vec::from(out_path));
return Some(rebuild_path(predecessor, start, end));
}
}

32 changes: 15 additions & 17 deletions src/data/database.rs
Original file line number Diff line number Diff line change
@@ -2,30 +2,28 @@ use serde::{Deserialize, Serialize};

use crate::data::maps::{link_map::LinkMap, page_map::PageMap};

#[derive(Serialize)]
struct SerializerDatabase<'a> {
pub links: &'a LinkMap,
pub pages: &'a PageMap,
}

#[derive(Deserialize)]
#[derive(Serialize, Deserialize)]
pub struct Database {
pub links: LinkMap,
pub pages: PageMap,
}

pub fn serialize(outfile: &str, links: &LinkMap, pages: &PageMap) {
let db = SerializerDatabase { links, pages };
impl Database {
pub fn new(links: LinkMap, pages: PageMap) -> Self {
Self { links, pages }
}

let file = std::fs::File::create(outfile).unwrap();
let writer = std::io::BufWriter::new(file);
pub fn to_file(&self, outfile: &str) {
let file = std::fs::File::create(outfile).unwrap();
let writer = std::io::BufWriter::new(file);

ciborium::into_writer(&db, writer).expect("Error writing db");
}
ciborium::into_writer(self, writer).expect("Error writing db");
}

pub fn deserialize(infile: &str) -> Database {
let file = std::fs::File::open(infile).unwrap();
let reader = std::io::BufReader::new(file);
pub fn from_file(infile: &str) -> Database {
let file = std::fs::File::open(infile).unwrap();
let reader = std::io::BufReader::new(file);

ciborium::from_reader(reader).expect("Error reading db")
ciborium::from_reader(reader).expect("Error reading db")
}
}
15 changes: 10 additions & 5 deletions src/indication.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#![allow(dead_code)] // todo: remove this when tests are implemented
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};

fn progressbar_template(len: u64) -> ProgressBar {
fn progressbar(len: u64) -> ProgressBar {
let pb = ProgressBar::new(len);
pb.set_style(
ProgressStyle::with_template(
@@ -45,7 +44,7 @@ impl ProgressReporter {
finish_message: &'static str,
len: u64,
) -> Self {
let progress = progressbar_template(len);
let progress = progressbar(len);
progress.set_prefix(format!("[{}/{}]", step, steps));
progress.set_message(message.clone());

@@ -73,6 +72,7 @@ impl ProgressReporter {
}
}

#[cfg(test)]
pub fn new_empty() -> Self {
Self {
progress: None,
@@ -106,6 +106,7 @@ impl ProgressReporter {
enum ProgressType {
Progress,
Spinner,
#[cfg(test)]
Empty,
}

@@ -119,6 +120,7 @@ pub struct ProgressBuilder {
}

impl ProgressBuilder {
#[cfg(test)]
pub fn empty() -> Self {
Self {
bar_type: ProgressType::Empty,
@@ -129,6 +131,7 @@ impl ProgressBuilder {
step: None,
}
}

pub fn new() -> Self {
Self {
bar_type: ProgressType::Progress,
@@ -172,6 +175,7 @@ impl ProgressBuilder {
self
}

#[cfg(test)]
fn build_empty(self) -> ProgressReporter {
ProgressReporter::new_empty()
}
@@ -192,8 +196,8 @@ impl ProgressBuilder {
let finish_message = self.finish_message;

let steps = self
.steps
.map(|steps| (steps, self.step.expect("only steps given, no step")));
.step
.map(|step| (step, self.steps.expect("only step given, steps missing")));

ProgressReporter::new_spinner(message, finish_message, steps)
}
@@ -202,6 +206,7 @@ impl ProgressBuilder {
match self.bar_type {
ProgressType::Progress => self.build_progress(),
ProgressType::Spinner => self.build_spinner(),
#[cfg(test)]
ProgressType::Empty => self.build_empty(),
}
}
28 changes: 1 addition & 27 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clap::{Parser, Subcommand};
use clap::Parser;

mod commands;
mod data;
@@ -11,32 +11,6 @@ struct Args {
command: Option<commands::Commands>,
}

#[derive(Subcommand, Debug)]
enum SubCommands {
DeriveDB {
/// Path to the page.sql file
#[arg(short, long)]
pages_sql: String,

/// Path to the redirects.sql file
#[arg(short, long)]
redirects_sql: String,

/// Path to the links.sql file
#[arg(short, long)]
links_sql: String,

/// Output Path
#[arg(short, long)]
output: String,
},

Test {
#[arg(short, long)]
db: String,
},
}

fn main() {
env_logger::init();
let args = Args::parse();

0 comments on commit 3c7642a

Please sign in to comment.