This commit is contained in:
锦恢 2024-10-02 22:40:54 +08:00
parent 3e49453f36
commit a6411df61a
12 changed files with 369 additions and 56 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ test.txt
build.bat build.bat
.DS_Store .DS_Store
.cache

21
Cargo.lock generated
View File

@ -160,6 +160,15 @@ dependencies = [
"rustc-demangle", "rustc-demangle",
] ]
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -324,9 +333,9 @@ name = "digital-lsp"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode",
"flexi_logger", "flexi_logger",
"log", "log",
"once_cell",
"path-clean", "path-clean",
"pathdiff", "pathdiff",
"percent-encoding", "percent-encoding",
@ -342,6 +351,7 @@ dependencies = [
"tempdir", "tempdir",
"tokio", "tokio",
"tower-lsp", "tower-lsp",
"uuid",
"vhdl_lang", "vhdl_lang",
"walkdir", "walkdir",
"which", "which",
@ -1719,6 +1729,15 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.8.2" version = "0.8.2"

View File

@ -7,7 +7,8 @@ edition = "2018"
[dependencies] [dependencies]
sv-parser = { version = "0.13.3", path = "sv-parser/sv-parser"} sv-parser = { version = "0.13.3", path = "sv-parser/sv-parser"}
vhdl_lang = { version = "^0.83.0", path = "rust_hdl/vhdl_lang" } vhdl_lang = { version = "^0.83.0", path = "rust_hdl/vhdl_lang" }
once_cell = "1.8" uuid = { version = "1.0", features = ["v4"] }
bincode = "1.3"
percent-encoding = "2.1.0" percent-encoding = "2.1.0"
log = "0.4.19" log = "0.4.19"
tower-lsp = "0.20.0" tower-lsp = "0.20.0"

View File

@ -1,12 +1,127 @@
use std::{collections::HashMap, fs::{self, File}, io::Read, path::PathBuf, str::FromStr, sync::{Arc, RwLock}};
use log::info;
use serde::{Deserialize, Serialize};
use crate::utils::{file_size_in_kb, get_last_modified_time, k_deserialize};
use super::hdlparam::FastHdlparam;
pub enum CacheResult<T> {
Ok(T),
NotNeedCache,
CacheNotFound,
NotHit,
FailDeserialize,
Error(String)
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CacheItem {
/// 文件名字
pub file_name: String,
/// 文件大小,单位 KB
pub size: u64,
/// 版本,根据特定算法得出
pub version: String,
/// 缓存文件的名字
pub cache_name: String
}
/// 用于进行高效 IR 缓存的模块 /// 用于进行高效 IR 缓存的模块
pub struct CacheManager {
/// 缓存文件夹根目录
pub root_dir: PathBuf,
/// meta 文件内容
pub meta_name: String,
/// meta 内容
pub meta: Arc<RwLock<HashMap<String, CacheItem>>>
}
#[allow(unused)]
pub fn dump_ir_storage() { impl CacheManager {
pub fn new(root_dir: &str) -> Self {
// 读入 meta 文件
let root_dir = PathBuf::from_str(root_dir).unwrap();
let meta_name = "index.cache";
let meta_path = root_dir.join(meta_name);
let meta = get_or_init_meta(&meta_path);
// 如果不存在 root dir则创建
if !root_dir.exists() {
match fs::create_dir_all(&root_dir) {
Ok(_) => {},
Err(err) => info!("error happen when create {root_dir:?}: {err:?}")
}
}
CacheManager {
root_dir,
meta_name: meta_name.to_string(),
meta: Arc::new(RwLock::new(meta))
}
}
pub fn is_big_file(&self, path: &PathBuf) -> bool {
if let Ok(size) = file_size_in_kb(path.to_str().unwrap()) {
return size >= 1024;
}
false
}
fn get_version(&self, path: &PathBuf) -> String {
let path_str = path.to_str().unwrap();
if let Ok(modify_ts) = get_last_modified_time(path_str) {
return modify_ts.to_string();
}
"".to_string()
}
/// ## 说明
/// 尝试获取当前的路径文件的 fast 的缓存
///
/// ## None
///
/// * 如果本来就没有缓存
/// * 缓冲的 version 对不上
pub fn try_get_fast_cache(&self, path: &PathBuf) -> CacheResult<FastHdlparam> {
if !self.is_big_file(path) {
return CacheResult::NotNeedCache
}
let path_string = path.to_str().unwrap();
let meta_handle = self.meta.read().unwrap();
if let Some(cache_item) = meta_handle.get(path_string) {
let current_version = self.get_version(path);
let cache_path = self.root_dir.join(&cache_item.cache_name);
if current_version == cache_item.version && cache_path.exists() {
// hit cache
if let Ok(fast) = k_deserialize::<FastHdlparam>(&cache_path) {
return CacheResult::Ok(fast);
}
}
}
CacheResult::CacheNotFound
}
pub fn update_cache() {
}
} }
#[allow(unused)]
pub fn load_ir_storage() {
fn get_or_init_meta(meta_path: &PathBuf) -> HashMap<String, CacheItem> {
if meta_path.exists() {
// 读取文件
if let Ok(meta_map) = k_deserialize::<HashMap<String, CacheItem>>(meta_path) {
return meta_map;
}
}
// 重新创建新的
HashMap::<String, CacheItem>::new()
} }

View File

@ -346,7 +346,7 @@ pub fn hover_module_declaration(
let port_num = module.ports.len(); let port_num = module.ports.len();
let param_num = module.params.len(); let param_num = module.params.len();
let instance_num = module.instances.len(); let instance_num = module.instances.len();
let port_desc = format!("`port` {port_num}, `param` {param_num}, `instantiation` {instance_num}"); let port_desc = format!("`$(instance-param) ` {port_num}, `param` {param_num}, `instantiation` {instance_num}");
// 统计 dir // 统计 dir
let mut input_count = 0 as u32; let mut input_count = 0 as u32;

View File

@ -20,7 +20,7 @@ mod server;
mod sources; mod sources;
mod request; mod request;
use server::{Backend, GLOBAL_BACKEND}; use server::Backend;
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
#[structopt(name = "Digtal LSP", about = "LSP for Digital IDE")] #[structopt(name = "Digtal LSP", about = "LSP for Digital IDE")]
@ -40,8 +40,6 @@ async fn main() {
let (service, socket) = LspService::build(|client| { let (service, socket) = LspService::build(|client| {
let backend = Arc::new(Backend::new(client, log_handle)); let backend = Arc::new(Backend::new(client, log_handle));
let _ = GLOBAL_BACKEND.set(backend.clone());
backend backend
}) })
.custom_method("custom/request", CustomRequest) .custom_method("custom/request", CustomRequest)

View File

@ -16,7 +16,7 @@ use crate::core::sv_parser::make_fast_from_syntaxtree;
use crate::core::vhdl_parser::{make_fast_from_design_file, vhdl_parse}; use crate::core::vhdl_parser::{make_fast_from_design_file, vhdl_parse};
use crate::utils::*; use crate::utils::*;
use crate::server::{Backend, GLOBAL_BACKEND}; use crate::server::Backend;
use crate::sources::recovery_sv_parse; use crate::sources::recovery_sv_parse;
@ -92,7 +92,7 @@ pub fn do_fast<'a>(path: String, backend: &Arc<Backend>) -> Result<FastHdlparam>
info!("parse fast \"{}\"", path); info!("parse fast \"{}\"", path);
let language_id = get_language_id_by_path_str(&path); let language_id = get_language_id_by_path_str(&path);
match language_id.as_str() { let res = match language_id.as_str() {
"vhdl" => do_vhdl_fast(&path, backend), "vhdl" => do_vhdl_fast(&path, backend),
"verilog" | "systemverilog" => do_sv_fast(&path, backend), "verilog" | "systemverilog" => do_sv_fast(&path, backend),
_ => Err(tower_lsp::jsonrpc::Error { _ => Err(tower_lsp::jsonrpc::Error {
@ -100,7 +100,10 @@ pub fn do_fast<'a>(path: String, backend: &Arc<Backend>) -> Result<FastHdlparam>
message: Cow::Owned(format!("invalid file: {path}, expect vhdl, verilog or system verilog!")), message: Cow::Owned(format!("invalid file: {path}, expect vhdl, verilog or system verilog!")),
data: None data: None
}) })
} };
info!("finish parse");
res
} }
fn do_sv_fast(path: &str, backend: &Arc<Backend>) -> Result<FastHdlparam> { fn do_sv_fast(path: &str, backend: &Arc<Backend>) -> Result<FastHdlparam> {
@ -189,16 +192,16 @@ impl Notification for UpdateFastNotification {
type Params = Self; type Params = Self;
} }
#[allow(unused)] // #[allow(unused)]
pub async fn update_fast_to_client(fast: FastHdlparam, path: &PathBuf) { // pub async fn update_fast_to_client(fast: FastHdlparam, path: &PathBuf) {
// info!("send fast to foreend {:?}", fast); // // info!("send fast to foreend {:?}", fast);
info!("send fast to foreend"); // info!("send fast to foreend");
let backend = GLOBAL_BACKEND.get().unwrap(); // let backend = GLOBAL_BACKEND.get().unwrap();
let path = path.to_str().unwrap_or("").to_string(); // let path = path.to_str().unwrap_or("").to_string();
let params = UpdateFastNotification { fast, path }; // let params = UpdateFastNotification { fast, path };
info!("backend client: {:?}", backend.client); // info!("backend client: {:?}", backend.client);
backend.client.send_notification::<UpdateFastNotification>(params).await; // backend.client.send_notification::<UpdateFastNotification>(params).await;
} // }

View File

@ -1,18 +1,15 @@
use crate::core::cache_storage::CacheManager;
use crate::sources::*; use crate::sources::*;
use crate::completion::keyword::*; use crate::completion::keyword::*;
use flexi_logger::LoggerHandle; use flexi_logger::LoggerHandle;
#[allow(unused)] #[allow(unused)]
use log::{debug, info, warn}; use log::{debug, info, warn};
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::string::ToString; use std::string::ToString;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use tower_lsp::jsonrpc::Result; use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer}; use tower_lsp::{Client, LanguageServer};
pub static GLOBAL_BACKEND: OnceCell<Arc<Backend>> = OnceCell::new();
pub struct LSPServer { pub struct LSPServer {
pub srcs: Sources, pub srcs: Sources,
pub key_comps: Vec<CompletionItem>, pub key_comps: Vec<CompletionItem>,
@ -169,8 +166,9 @@ impl Default for VeribleFormat {
#[tower_lsp::async_trait] #[tower_lsp::async_trait]
impl LanguageServer for Backend { impl LanguageServer for Backend {
async fn initialize(&self, _: InitializeParams) -> Result<InitializeResult> { async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
self.server.srcs.init(); self.server.srcs.init();
// params.client_info.unwrap().name
// 申明 LSP 的基本信息和提供的能力 // 申明 LSP 的基本信息和提供的能力
let server_info = Some(ServerInfo { let server_info = Some(ServerInfo {

View File

@ -393,6 +393,7 @@ impl Sources {
} }
} }
/// 更加稳定地解析 sv 和 v /// 更加稳定地解析 sv 和 v
/// 支持遇到错误进行自动修复,然后再解析 /// 支持遇到错误进行自动修复,然后再解析
pub fn recovery_sv_parse( pub fn recovery_sv_parse(
@ -408,7 +409,32 @@ pub fn recovery_sv_parse(
let mut reverted_change = false; let mut reverted_change = false;
let mut text = doc.clone(); let mut text = doc.clone();
while i < parse_iterations { let recover_text_by_byte_idx = |byte_idx: usize, reverted_change: &mut bool, text: &mut Rope| {
let mut line_start = text.byte_to_line(byte_idx);
let mut line_end = text.byte_to_line(byte_idx) + 1;
// println!("byte: {byte_idx}, line_start: {line_start}, line_end: {line_end}");
if !(*reverted_change) {
if let Some(range) = last_change_range {
line_start = range.start.line as usize;
line_end = range.end.line as usize + 1;
*reverted_change = true;
}
}
// 把 last_change 处的地方替换成空格
for line_idx in line_start .. line_end {
let line = text.line(line_idx);
let start_char = text.line_to_char(line_idx);
let line_length = line.len_chars();
text.remove(start_char..(start_char + line_length - 1));
text.insert(start_char, &" ".to_owned().repeat(line_length));
}
};
// 最多解析 500 次
while i < parse_iterations && i < 500 {
i += 1; i += 1;
match parse_sv_str( match parse_sv_str(
&text.to_string(), &text.to_string(),
@ -422,28 +448,13 @@ pub fn recovery_sv_parse(
return Some(syntax_tree); return Some(syntax_tree);
} }
Err(err) => { Err(err) => {
println!("err: {err:?}");
match err { match err {
// 语法错误 // 语法错误
sv_parser::Error::Parse(trace) => match trace { sv_parser::Error::Parse(trace) => match trace {
Some((_, bpos)) => { Some((_, byte_idx)) => {
let mut line_start = text.byte_to_line(bpos); // 把出错的地方替换成空格
let mut line_end = text.byte_to_line(bpos) + 1; recover_text_by_byte_idx(byte_idx, &mut reverted_change, &mut text);
if !reverted_change {
if let Some(range) = last_change_range {
line_start = range.start.line as usize;
line_end = range.end.line as usize + 1;
reverted_change = true;
}
}
// 把 last_change 处的地方替换成空格
for line_idx in line_start .. line_end {
let line = text.line(line_idx);
let start_char = text.line_to_char(line_idx);
let line_length = line.len_chars();
text.remove(start_char..(start_char + line_length - 1));
text.insert(start_char, &" ".to_owned().repeat(line_length));
}
parse_iterations += 1; parse_iterations += 1;
} }
None => return None, None => return None,
@ -476,11 +487,35 @@ pub fn recovery_sv_parse(
let com_define = Define { let com_define = Define {
identifier: not_found_macro_name.to_string(), identifier: not_found_macro_name.to_string(),
arguments: Vec::new(), arguments: Vec::new(),
text: Some(DefineText {text: "undefined".to_string(), origin: None}) text: Some(DefineText {text: "dide-undefined".to_string(), origin: None})
}; };
defines.insert(not_found_macro_name, Some(com_define)); defines.insert(not_found_macro_name, Some(com_define));
parse_iterations += 1; parse_iterations += 1;
} }
sv_parser::Error::Preprocess(trace) => match trace {
Some((_, byte_idx)) => {
// 把出错的地方替换成空格
// println!("text {text:?}");
recover_text_by_byte_idx(byte_idx, &mut reverted_change, &mut text);
parse_iterations += 1;
}
None => return None
}
sv_parser::Error::DefineArgNotFound(trace) => match trace {
Some((_, start_byte_idx, _)) => {
recover_text_by_byte_idx(start_byte_idx, &mut reverted_change, &mut text);
// recover_text_by_byte_idx(end_byte_idx, &mut reverted_change, &mut text);
parse_iterations += 1;
}
_ => return None
}
sv_parser::Error::DefineNoArgs(trace) => match trace {
Some((_, start_byte_idx, _)) => {
recover_text_by_byte_idx(start_byte_idx, &mut reverted_change, &mut text);
parse_iterations += 1;
}
_ => return None
}
_ => error!("parse error, {:?}", err), _ => error!("parse error, {:?}", err),
}; };
} }

View File

@ -6,6 +6,9 @@ const TESTFILES_DIR: &str = "testfiles";
#[allow(unused)] #[allow(unused)]
const DIGTIAL_IDE_TEST: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user"; const DIGTIAL_IDE_TEST: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user";
#[allow(unused)]
const MIPS_DESIGN_TEST: &str = "/home/dide/project/Digital-Test/MipsDesign";
#[allow(unused)] #[allow(unused)]
const TEST_FILE: &str = "/home/dide/project/Digital-Test/MipsDesign/src/MyCpu.v"; const TEST_FILE: &str = "/home/dide/project/Digital-Test/MipsDesign/src/MyCpu.v";
@ -37,19 +40,21 @@ macro_rules! unwrap_option {
#[cfg(test)] #[cfg(test)]
mod test_fast { mod test_fast {
use std::{fs, path::Path}; use std::{env, fs, path::Path};
use crate::core::sv_parser::sv_parser; use crate::core::sv_parser::sv_parser;
use super::*; use super::*;
#[test] #[test]
fn test_mycpu() { fn test_mycpu() {
let hdlparam = sv_parser(TEST_FILE); let hdlparam = sv_parser("/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/undef.sv");
println!("{hdlparam:?}"); // println!("{hdlparam:?}");
assert!(hdlparam.is_some()); assert!(hdlparam.is_some());
} }
#[test] #[test]
fn test_testfiles() { fn test_testfiles() {
let ws_path = env::current_dir().unwrap();
let entries = unwrap_result!(fs::read_dir(TESTFILES_DIR)); let entries = unwrap_result!(fs::read_dir(TESTFILES_DIR));
for entry in entries { for entry in entries {
let entry = unwrap_result!(entry); let entry = unwrap_result!(entry);
@ -58,13 +63,18 @@ mod test_fast {
let extension = unwrap_option!(file_path.extension()); let extension = unwrap_option!(file_path.extension());
let file_path = unwrap_option!(file_path.to_str()); let file_path = unwrap_option!(file_path.to_str());
if extension == "v" || extension == "sv" { if extension == "v" || extension == "sv" {
let file_path = ws_path.join(file_path);
let file_path = file_path.to_str().unwrap();
println!("Test file: {:?}", file_path); println!("Test file: {:?}", file_path);
let _ = sv_parser(file_path); let _ = sv_parser(file_path);
} }
} }
} }
} }
// cargo test --release --package digital-lsp --lib -- test::test_fast::test_digital_ide_test --nocapture
#[test] #[test]
fn test_digital_ide_test() { fn test_digital_ide_test() {
// 判断路径是否存在且为文件夹 // 判断路径是否存在且为文件夹
@ -79,6 +89,20 @@ mod test_fast {
} }
} }
#[test]
fn test_mips_design() {
// 判断路径是否存在且为文件夹
let path = Path::new(MIPS_DESIGN_TEST);
if path.exists() && path.is_dir() {
// 递归遍历文件夹
if let Err(e) = traverse_directory(path) {
eprintln!("Error: {}", e);
}
} else {
eprintln!("Path does not exist or is not a directory");
}
}
#[test] #[test]
fn test_incomplete_example() { fn test_incomplete_example() {
let path = Path::new(INCOMPLETE_EXAMPLE); let path = Path::new(INCOMPLETE_EXAMPLE);
@ -119,6 +143,13 @@ mod test_fast {
if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/hdlconv/pri_encoder_using_assign.sv" { if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/hdlconv/pri_encoder_using_assign.sv" {
continue; continue;
} }
if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/macro_with_args.sv" {
continue;
}
if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/vlog/element/macro.v" {
continue;
}
let file_path = path.to_str().unwrap(); let file_path = path.to_str().unwrap();
let result = sv_parser(file_path); let result = sv_parser(file_path);
assert!(result.is_some()); assert!(result.is_some());
@ -359,3 +390,37 @@ mod test_scope_tree {
Ok(()) Ok(())
} }
} }
#[cfg(test)]
mod test_file {
use std::fs;
use crate::utils::{file_size_in_kb, get_language_id_by_pathbuf, RecursiveFileIterator};
use super::*;
#[test]
fn test_big_file_detect() {
let file_iter = RecursiveFileIterator::new(DIGTIAL_IDE_TEST);
// 超过 1 MB 的文件,我们都认为是大文件
let threshold: u64 = 1024;
for pathbuf in file_iter {
let language_id = get_language_id_by_pathbuf(&pathbuf);
if language_id == "verilog" || language_id == "systemverilog" {
let size = file_size_in_kb(pathbuf.to_str().unwrap());
if let Ok(size) = size {
if size >= threshold {
println!("detect big file : {:?}, size: {}", pathbuf, size);
}
} else {
eprintln!("error to get size of {:?}", pathbuf);
}
}
}
}
#[test]
fn test_cache() {
let test_path = "/home/dide/project/digital-lsp-server/.cache";
fs::create_dir_all("/home/dide/project/digital-lsp-server/.cache");
}
}

View File

@ -1,7 +1,8 @@
use std::{fs, path::PathBuf, str::FromStr}; use std::{fmt::Debug, fs::{self, File}, io::{Read, Write}, path::PathBuf, str::FromStr, time::UNIX_EPOCH};
use log::info; use log::info;
use ropey::Rope; use ropey::Rope;
use serde::{Deserialize, Serialize};
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use crate::sources::LSPSupport; use crate::sources::LSPSupport;
@ -34,7 +35,9 @@ pub struct RecursiveFileIterator {
stack: Vec<PathBuf>, stack: Vec<PathBuf>,
} }
/// 递归获取所有文件
impl RecursiveFileIterator { impl RecursiveFileIterator {
#[allow(unused)]
pub fn new(start_dir: &str) -> Self { pub fn new(start_dir: &str) -> Self {
let start_dir = PathBuf::from_str(start_dir).unwrap(); let start_dir = PathBuf::from_str(start_dir).unwrap();
RecursiveFileIterator { RecursiveFileIterator {
@ -45,7 +48,6 @@ impl RecursiveFileIterator {
impl Iterator for RecursiveFileIterator { impl Iterator for RecursiveFileIterator {
type Item = PathBuf; type Item = PathBuf;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
while let Some(path) = self.stack.pop() { while let Some(path) = self.stack.pop() {
if path.is_dir() { if path.is_dir() {
@ -61,3 +63,79 @@ impl Iterator for RecursiveFileIterator {
None None
} }
} }
/// 计算文件大小,单位 KB
pub fn file_size_in_kb(path: &str) -> Result<u64, std::io::Error> {
// 获取文件元数据
let metadata = fs::metadata(path)?;
// 获取文件大小(以字节为单位)
let file_size_bytes = metadata.len();
// 将文件大小转换为 KB并确保结果在 u32 范围内
let file_size_kb = (file_size_bytes / 1024) as u64;
Ok(file_size_kb)
}
pub fn get_last_modified_time(path: &str) -> Result<u64, std::io::Error> {
// 获取文件元数据
let metadata = fs::metadata(path)?;
// 获取文件的最近修改时间
let modified_time = metadata.modified()?;
let duration = modified_time.duration_since(UNIX_EPOCH).map_err(
|e| std::io::Error::new(std::io::ErrorKind::Other, e)
)?;
let seconds = duration.as_secs();
Ok(seconds)
}
/// 序列化结构体到二进制文件
pub fn k_serialize<T>(path: &PathBuf, data: T) -> Result<(), std::io::Error>
where T: Serialize + Debug {
let buffer: Vec<u8> = bincode::serialize(&data).unwrap();
match File::create(path) {
Ok(mut file) => {
match file.write_all(&buffer) {
Ok(_) => Ok(()),
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
}
/// 反序列化二进制文件到结构体
pub fn k_deserialize<T>(path: &PathBuf) -> Result<T, std::io::Error>
where T: for<'de> Deserialize<'de> + Debug {
match File::open(path) {
Ok(mut file) => {
let mut buffer = Vec::<u8>::new();
match file.read_to_end(&mut buffer) {
Ok(_) => {
let meta_map: Result<T, Box<bincode::ErrorKind>> = bincode::deserialize(&buffer);
if meta_map.is_ok() {
Ok(meta_map.unwrap())
} else {
let err: Box<bincode::ErrorKind> = meta_map.unwrap_err();
info!("error happen when deserialize index.cache from \"{path:?}\": {err:?}");
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, err))
}
}
Err(err) => {
info!("error happen when load bytes to buffer from \"{path:?}\": {err:?}");
Err(err)
}
}
}
Err(err) => {
info!("error happen when read index.cache from \"{path:?}\": {err:?}");
Err(err)
}
}
}