Skip to content

Commit

Permalink
improve sorting performance and state management.
Browse files Browse the repository at this point in the history
Previous code would calculating hashes when there's any update from working dir, move it into tarui state management to avoid duplicating hash calculating.

File watcher now shared the same score state from tarui, reduced some unnecessary file/hash operations.
  • Loading branch information
j4w3ny committed Jun 25, 2022
1 parent e7471a7 commit a2a0a2a
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 134 deletions.
8 changes: 3 additions & 5 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,8 @@ const Home = () => {
});
console.log(arr);
invoke('set_scores', {
linkScoreMaps: arr,
});
// setScores(arr);
refreshInfo();
scores: arr,
}).then(() => refreshInfo());
}}>
<ArrowUpward />
</IconButton>
Expand All @@ -249,7 +247,7 @@ const Home = () => {
console.log(arr);
setScores(arr);
invoke('set_scores', {
linkScoreMaps: arr,
scores: arr,
}).then(() => {
refreshInfo();
});
Expand Down
59 changes: 58 additions & 1 deletion core/src/base/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{fs::File, path::Path};

pub use link::{Link, OpenGraph};
use serde::{Deserialize, Serialize};

use walkdir::{DirEntry, WalkDir};
#[derive(Debug, Serialize, Deserialize)]
pub struct LinkScoreMap {
pub name: String,
Expand All @@ -30,6 +30,8 @@ pub enum Mode {
pub type Scores = Vec<Score>;
#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Clone)]
pub struct Score {
pub name: String,

pub hash: String,
// Score could take a negative value.
pub value: i64,
Expand All @@ -46,20 +48,75 @@ impl Score {
.crc32
)
}
/// Parse scores from string.
///
/// Note that the name in each item is set to default `String::new()`, since we can't parse name from crc32.
pub fn parse(content: String) -> Scores {
let splited = content
.split("\n")
.map(|val| {
let mapped = val.split(": ").collect::<Vec<&str>>();
Score {
// TODO
name: String::new(),
hash: mapped[0].to_string(),
value: i64::from_str_radix(mapped[1], 10).unwrap(),
}
})
.collect::<Vec<Score>>();
splited
}
/// Parse the given string into scores and merge scores by reading all `.link` in the given path.
///
/// Scores name will be filled with `.link` name during merging.
pub fn parse_and_merge(content: String, path: impl AsRef<Path>) -> Scores {
let splited = Score::parse(content);
let merged_scores = Score::merge(splited, path);
merged_scores
}
/// Merge scores with reading given path.
///
/// Scores name will be filled with `.link` name during merging.
pub fn merge(merge_scores: Scores, path: impl AsRef<Path>) -> Scores {
let entrys = WalkDir::new(path)
.max_depth(1)
.into_iter()
.filter(|entry| {
entry
.as_ref()
.unwrap()
.file_name()
.to_str()
.unwrap()
.to_string()
.ends_with(".link")
})
.map(|e| e.unwrap())
.collect::<Vec<DirEntry>>();

let init_scores = entrys
.iter()
.map(|entry| Score {
name: entry.file_name().to_string_lossy().to_string(),
hash: Score::calc_hash(entry.path()),
// Default to 0
value: 0,
})
.collect::<Scores>();

let merged_scores = init_scores
.iter()
.map(|score|
// Merge score item if the item already existed in to-be-merged scores
// Item not found in init_scores will be ignored. (Remove from the list)
match merge_scores.iter().find(|&s| s.hash == score.hash) {
// replace name with file name
Some(item) => Score { name: score.name.clone(), ..item.clone() },
None => score.clone(),
})
.collect::<Scores>();
merged_scores
}
pub fn format(hash: String, value: i64) -> String {
String::from(format!("{hash}: {value}"))
}
Expand Down
87 changes: 23 additions & 64 deletions core/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use std::{
fs::{self, File},
io::{Read, Write},
path::PathBuf,
sync::{Arc, Mutex},
};

use crate::{
base::{Link, LinkScoreMap, OpenGraph, Score},
Cli, SCORES_PATH,
base::{Link, LinkScoreMap, OpenGraph, Score, Scores},
Cli, ARK_SHELF_WORKING_DIR, SCORES_PATH,
};

use tauri::{Builder, Runtime};
Expand Down Expand Up @@ -39,8 +40,8 @@ fn delete_link(name: String, state: tauri::State<Cli>) {
fs::remove_file(format!("{}/{}", &state.path, name)).expect("cannot remove the link");
}

fn get_fs_links(state: tauri::State<Cli>) -> Vec<DirEntry> {
WalkDir::new(state.path.clone())
fn get_fs_links() -> Vec<DirEntry> {
WalkDir::new(ARK_SHELF_WORKING_DIR.as_path())
.max_depth(1)
.into_iter()
.filter(|file| {
Expand All @@ -58,12 +59,14 @@ fn get_fs_links(state: tauri::State<Cli>) -> Vec<DirEntry> {

#[tauri::command(async)]
/// Read names of `.link` in user specific directory
fn read_link_list(state: tauri::State<Cli>) -> Vec<String> {
fn read_link_list() -> Vec<String> {
let mut path_list = vec![];
for item in get_fs_links(state.clone()) {
for item in get_fs_links() {
dbg!(&item);
let file_name = item.file_name().to_str().unwrap().to_string();
path_list.push(file_name);
}
dbg!(&path_list);
path_list
}

Expand All @@ -74,77 +77,33 @@ async fn generate_link_preview(url: String) -> Result<OpenGraph, String> {

/// Get the score list
#[tauri::command(async)]
fn get_scores(state: tauri::State<Cli>) -> Result<Vec<LinkScoreMap>, String> {
let mut file = File::open(SCORES_PATH.as_path()).map_err(|e| e.to_string())?;
let mut string_buf = String::new();
file.read_to_string(&mut string_buf)
.map_err(|e| e.to_string())?;
let scores = Score::parse(string_buf);
let fs_links = get_fs_links(state);

let link_score_maps = fs_links
.iter()
.map(|entry| {
let item = scores
.iter()
.find(|s| Score::calc_hash(entry.path()) == s.hash)
.unwrap();
LinkScoreMap {
name: entry.file_name().to_str().unwrap().to_string(),
value: item.value,
}
})
.collect::<Vec<_>>();
Ok(link_score_maps)
fn get_scores(scores: tauri::State<Arc<Mutex<Scores>>>) -> Result<Scores, String> {
// let mut file = File::open(SCORES_PATH.as_path()).map_err(|e| e.to_string())?;
// let mut string_buf = String::new();
// file.read_to_string(&mut string_buf)
// .map_err(|e| e.to_string())?;
// let scores = Score::parse_and_merge(string_buf);

Ok(scores.lock().unwrap().clone())
}

/// Set scores
///
/// Only affected scores file.
#[tauri::command(async)]
fn set_scores(link_score_maps: Vec<LinkScoreMap>, state: tauri::State<Cli>) -> Result<(), String> {
let mut buf = String::new();
let mut scores_file = File::options()
.read(true)
.open(SCORES_PATH.as_path())
.unwrap();
scores_file
.read_to_string(&mut buf)
.map_err(|e| e.to_string())?;

let scores = Score::parse(buf);
let fs_links = get_fs_links(state);

let transformed = link_score_maps
.iter()
.map(|s| {
let item = fs_links
.iter()
.find(|e| e.file_name().to_os_string() == OsString::from(s.name.clone()))
.unwrap();

Score {
hash: Score::calc_hash(item.path()),
value: s.value,
}
})
.collect::<Vec<_>>();

let merged = scores
.iter()
.map(|s| match transformed.iter().find(|&ms| ms.hash == s.hash) {
Some(item) => item.clone(),
None => s.clone(),
})
.collect::<Vec<_>>();
fn set_scores(
scores: Scores,
state_scores: tauri::State<Arc<Mutex<Scores>>>,
) -> Result<(), String> {
*state_scores.lock().unwrap() = scores.clone();

let mut scores_file = File::options()
.write(true)
.truncate(true)
.open(SCORES_PATH.as_path())
.unwrap();
scores_file
.write_all(Score::into_lines(merged).as_bytes())
.write_all(Score::into_lines(scores).as_bytes())
.map_err(|e| e.to_string())?;
Ok(())
}
Expand Down
Loading

0 comments on commit a2a0a2a

Please sign in to comment.