升级
This commit is contained in:
56
Cargo.lock
generated
56
Cargo.lock
generated
@@ -18,7 +18,9 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing-appender",
|
||||
]
|
||||
|
||||
@@ -1258,6 +1260,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
@@ -1491,6 +1502,45 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.11+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.5+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.3"
|
||||
@@ -1899,6 +1949,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
|
||||
@@ -21,6 +21,8 @@ colored = "3.1.1"
|
||||
env_logger = "0.11.8"
|
||||
encoding_rs = "0.8.35"
|
||||
tracing-appender = "0.2.4"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
toml = "0.9.11"
|
||||
|
||||
[build-dependencies]
|
||||
cxx-build = "1.0.192"
|
||||
|
||||
3
build.rs
3
build.rs
@@ -1,6 +1,7 @@
|
||||
fn main() {
|
||||
cxx_build::bridge("src/lib.rs")
|
||||
cxx_build::bridges(["src/lib.rs", "src/config.rs"])
|
||||
.std("c++20")
|
||||
.compile("dnf_utils");
|
||||
println!("cargo:rerun-if-changed=src/lib.rs");
|
||||
println!("cargo:rerun-if-changed=src/config.rs");
|
||||
}
|
||||
|
||||
324
src/config.rs
Normal file
324
src/config.rs
Normal file
@@ -0,0 +1,324 @@
|
||||
use crate::utils::cxx_string_to_string;
|
||||
use cxx::CxxString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
use toml::Value;
|
||||
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
extern "Rust" {
|
||||
type Config;
|
||||
|
||||
/// 载入配置文件
|
||||
fn load_or_create(path: &CxxString) -> Result<Box<Config>>;
|
||||
/// 保存配置文件
|
||||
fn save_config(cfg: &Box<Config>, path: &CxxString) -> Result<()>;
|
||||
|
||||
/// 做不做每日任务
|
||||
fn is_daily_missions(cfg: &Box<Config>) -> bool;
|
||||
fn set_daily_missions(cfg: &mut Box<Config>, value: bool);
|
||||
|
||||
/// 做不做群岛
|
||||
fn is_archipelago(cfg: &Box<Config>) -> bool;
|
||||
fn set_archipelago(cfg: &mut Box<Config>, value: bool);
|
||||
|
||||
/// 做不做深渊
|
||||
fn is_spiral_abyss(cfg: &Box<Config>) -> bool;
|
||||
fn set_spiral_abyss(cfg: &mut Box<Config>, value: bool);
|
||||
|
||||
/// 做不做妖气
|
||||
fn is_demonic_energy(cfg: &Box<Config>) -> bool;
|
||||
fn set_demonic_energy(cfg: &mut Box<Config>, value: bool);
|
||||
|
||||
/// 森林
|
||||
fn is_forest(cfg: &Box<Config>) -> bool;
|
||||
fn set_forest(cfg: &mut Box<Config>, value: bool);
|
||||
|
||||
/// 分解
|
||||
fn is_decompose(cfg: &Box<Config>) -> bool;
|
||||
fn set_decompose(cfg: &mut Box<Config>, value: bool);
|
||||
|
||||
/// 从配置文件中读取一个配置项
|
||||
fn get_config_value(
|
||||
path: &CxxString,
|
||||
path_name: &CxxString,
|
||||
key: &CxxString,
|
||||
default_value: &CxxString,
|
||||
) -> String;
|
||||
|
||||
/// 设置配置文件中的一个配置项
|
||||
fn set_config_value(
|
||||
path: &CxxString,
|
||||
path_name: &CxxString,
|
||||
key: &CxxString,
|
||||
value: &CxxString,
|
||||
) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Config {
|
||||
/// 做不做每日任务
|
||||
is_daily_missions: bool,
|
||||
/// 做不做群岛
|
||||
is_archipelago: bool,
|
||||
/// 做不做深渊
|
||||
is_spiral_abyss: bool,
|
||||
/// 做不做妖气
|
||||
is_demonic_energy: bool,
|
||||
/// 森林
|
||||
is_forest: bool,
|
||||
/// 分解
|
||||
is_decompose: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
is_daily_missions: false,
|
||||
is_archipelago: false,
|
||||
is_spiral_abyss: false,
|
||||
is_demonic_energy: false,
|
||||
is_forest: false,
|
||||
is_decompose: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// 生成带“文档注释”的 toml 文本(toml/serde 不会自动输出 Rust 的 /// 注释,所以这里手动拼)
|
||||
pub fn to_toml_with_comments(&self) -> String {
|
||||
// 你也可以加一个总注释、版本号等
|
||||
format!(
|
||||
r#"# 自动生成的配置文件
|
||||
# 实现可能修改配置存储结构,请不要使用程序化工具修改此配置文件,如果非要这么做,请链接utils.lib后,使用其提供的接口。
|
||||
|
||||
# 做不做每日任务
|
||||
is_daily_missions = {is_daily_missions}
|
||||
|
||||
# 做不做群岛
|
||||
is_archipelago = {is_archipelago}
|
||||
|
||||
# 做不做深渊
|
||||
is_spiral_abyss = {is_spiral_abyss}
|
||||
|
||||
# 做不做妖气
|
||||
is_demonic_energy = {is_demonic_energy}
|
||||
|
||||
# 森林
|
||||
is_forest = {is_forest}
|
||||
|
||||
# 分解
|
||||
is_decompose = {is_decompose}
|
||||
"#,
|
||||
is_daily_missions = self.is_daily_missions,
|
||||
is_archipelago = self.is_archipelago,
|
||||
is_spiral_abyss = self.is_spiral_abyss,
|
||||
is_demonic_energy = self.is_demonic_energy,
|
||||
is_forest = self.is_forest,
|
||||
is_decompose = self.is_decompose,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn save_with_comments(&self, path: impl AsRef<Path>) -> io::Result<()> {
|
||||
let path = path.as_ref();
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
fs::write(path, self.to_toml_with_comments())
|
||||
}
|
||||
|
||||
/// 读取配置:不存在则创建(写入默认值+注释),存在则从文件构造实例。
|
||||
///
|
||||
/// - toml 缺字段:会自动用 Default 补齐(因为 #[serde(default)])
|
||||
/// - 可选:如果你想“读取后把缺的字段补齐回写”,这里也做了回写
|
||||
pub fn load_or_create(path: impl AsRef<Path>) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let path = path.as_ref();
|
||||
|
||||
if !path.exists() {
|
||||
let cfg = Self::default();
|
||||
cfg.save_with_comments(path)?;
|
||||
return Ok(cfg);
|
||||
}
|
||||
|
||||
let text = fs::read_to_string(path)?;
|
||||
let cfg: Self = toml::from_str(&text)?;
|
||||
|
||||
// 可选:自动回写补全(比如用户旧版配置少字段时,补齐并带注释写回去)
|
||||
// 如果你不想覆盖用户文件,把下面两行删掉即可。
|
||||
cfg.save_with_comments(path)?;
|
||||
|
||||
Ok(cfg)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load_or_create(path: &CxxString) -> Result<Box<Config>, Box<dyn std::error::Error>> {
|
||||
let path = cxx_string_to_string(path);
|
||||
let config = Config::load_or_create(&path)?;
|
||||
Ok(Box::new(config))
|
||||
}
|
||||
|
||||
pub(crate) fn save_config(
|
||||
this: &Box<Config>,
|
||||
path: &CxxString,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let path = cxx_string_to_string(path);
|
||||
this.save_with_comments(path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn is_daily_missions(this: &Box<Config>) -> bool {
|
||||
this.is_daily_missions
|
||||
}
|
||||
|
||||
pub(crate) fn set_daily_missions(this: &mut Box<Config>, flag: bool) {
|
||||
this.is_daily_missions = flag;
|
||||
}
|
||||
|
||||
pub(crate) fn is_archipelago(this: &Box<Config>) -> bool {
|
||||
this.is_archipelago
|
||||
}
|
||||
|
||||
pub(crate) fn set_archipelago(this: &mut Box<Config>, flag: bool) {
|
||||
this.is_archipelago = flag;
|
||||
}
|
||||
|
||||
pub(crate) fn is_spiral_abyss(this: &Box<Config>) -> bool {
|
||||
this.is_spiral_abyss
|
||||
}
|
||||
|
||||
pub(crate) fn set_spiral_abyss(this: &mut Box<Config>, flag: bool) {
|
||||
this.is_spiral_abyss = flag;
|
||||
}
|
||||
|
||||
pub(crate) fn is_demonic_energy(this: &Box<Config>) -> bool {
|
||||
this.is_demonic_energy
|
||||
}
|
||||
|
||||
pub(crate) fn set_demonic_energy(this: &mut Box<Config>, flag: bool) {
|
||||
this.is_demonic_energy = flag;
|
||||
}
|
||||
|
||||
pub(crate) fn is_forest(this: &Box<Config>) -> bool {
|
||||
this.is_forest
|
||||
}
|
||||
|
||||
pub(crate) fn set_forest(this: &mut Box<Config>, flag: bool) {
|
||||
this.is_forest = flag;
|
||||
}
|
||||
|
||||
pub(crate) fn is_decompose(this: &Box<Config>) -> bool {
|
||||
this.is_decompose
|
||||
}
|
||||
|
||||
pub(crate) fn set_decompose(this: &mut Box<Config>, flag: bool) {
|
||||
this.is_decompose = flag;
|
||||
}
|
||||
|
||||
pub(crate) fn set_config_value(
|
||||
path: &CxxString,
|
||||
path_name: &CxxString,
|
||||
key: &CxxString,
|
||||
value: &CxxString,
|
||||
) -> bool {
|
||||
let path_str = cxx_string_to_string(path);
|
||||
let path_name_str = cxx_string_to_string(path_name);
|
||||
let key_str = cxx_string_to_string(key);
|
||||
let value_str = cxx_string_to_string(value);
|
||||
|
||||
// 创建父目录(如果不存在)
|
||||
let path = Path::new(&path_str);
|
||||
if let Some(parent) = path.parent() {
|
||||
if let Err(_) = fs::create_dir_all(parent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 读取现有内容或创建新的 TOML 值
|
||||
let mut value: Value = if path.exists() {
|
||||
match fs::read_to_string(&path_str) {
|
||||
Ok(content) => {
|
||||
toml::from_str(&content).unwrap_or_else(|_| Value::Table(toml::map::Map::new()))
|
||||
}
|
||||
Err(_) => Value::Table(toml::map::Map::new()),
|
||||
}
|
||||
} else {
|
||||
Value::Table(toml::map::Map::new())
|
||||
};
|
||||
|
||||
// 确保根值是一个表
|
||||
let root_table = match value.as_table_mut() {
|
||||
Some(t) => t,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// 获取或创建 path_name 对应的表
|
||||
let path_table = root_table
|
||||
.entry(&path_name_str)
|
||||
.or_insert(Value::Table(toml::map::Map::new()))
|
||||
.as_table_mut();
|
||||
|
||||
let path_table = match path_table {
|
||||
Some(t) => t,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
// 设置键值对
|
||||
path_table.insert(key_str, Value::String(value_str));
|
||||
|
||||
// 序列化并写入文件
|
||||
let toml_string = match toml::to_string_pretty(&value) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
match fs::write(&path_str, toml_string) {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 从配置文件中读取一个配置项
|
||||
pub(crate) fn get_config_value(
|
||||
path: &CxxString,
|
||||
path_name: &CxxString,
|
||||
key: &CxxString,
|
||||
default_value: &CxxString,
|
||||
) -> String {
|
||||
let path_str = cxx_string_to_string(path);
|
||||
let path_name_str = cxx_string_to_string(path_name);
|
||||
let key_str = cxx_string_to_string(key);
|
||||
let default_str = cxx_string_to_string(default_value);
|
||||
|
||||
// 读取文件内容
|
||||
let content = match fs::read_to_string(&path_str) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return default_str,
|
||||
};
|
||||
|
||||
// 解析 TOML
|
||||
let value: Value = match toml::from_str(&content) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return default_str,
|
||||
};
|
||||
|
||||
// 导航到 path_name 下的 table
|
||||
let table = match value.get(&path_name_str) {
|
||||
Some(Value::Table(t)) => t,
|
||||
_ => return default_str,
|
||||
};
|
||||
|
||||
// 获取 key 对应的值
|
||||
let result = match table.get(&key_str) {
|
||||
Some(v) => match v.as_str() {
|
||||
Some(s) => s.to_string(),
|
||||
None => v.to_string(),
|
||||
},
|
||||
None => return default_str,
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
29
src/lib.rs
29
src/lib.rs
@@ -1,9 +1,12 @@
|
||||
use ::log::{debug, error, info, trace, warn};
|
||||
use encoding_rs::Encoding;
|
||||
use tokio::runtime::Runtime;
|
||||
use crate::utils::cxx_string_to_string;
|
||||
|
||||
mod log;
|
||||
mod network;
|
||||
mod config;
|
||||
pub mod utils;
|
||||
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
@@ -76,30 +79,4 @@ fn http_get(url: &cxx::CxxString) -> Result<String, Box<dyn std::error::Error>>
|
||||
rt.block_on(network::http_get(&url))
|
||||
}
|
||||
|
||||
fn cxx_string_to_string(s: &cxx::CxxString) -> String {
|
||||
match s.to_str() {
|
||||
Ok(s) => return s.to_string(),
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
// 不是UTF-8,尝试转换
|
||||
let candidates = [
|
||||
"gb18030", // 覆盖 GBK/GB2312 的常见场景
|
||||
"windows-1252", // 西欧常见
|
||||
"shift_jis", // 日文常见
|
||||
"big5", // 繁体中文常见
|
||||
];
|
||||
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
for label in candidates {
|
||||
let enc = Encoding::for_label(label.as_bytes()).unwrap();
|
||||
let (cow, _actual_used, had_errors) = enc.decode(bytes);
|
||||
if !had_errors {
|
||||
return cow.into_owned();
|
||||
}
|
||||
}
|
||||
|
||||
/// 编码全部没有命中的话,则丢弃无法解析的部分
|
||||
s.to_string_lossy().into_owned()
|
||||
}
|
||||
|
||||
29
src/utils.rs
Normal file
29
src/utils.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use encoding_rs::Encoding;
|
||||
|
||||
pub fn cxx_string_to_string(s: &cxx::CxxString) -> String {
|
||||
match s.to_str() {
|
||||
Ok(s) => return s.to_string(),
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
// 不是UTF-8,尝试转换
|
||||
let candidates = [
|
||||
"gb18030", // 覆盖 GBK/GB2312 的常见场景
|
||||
"windows-1252", // 西欧常见
|
||||
"shift_jis", // 日文常见
|
||||
"big5", // 繁体中文常见
|
||||
];
|
||||
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
for label in candidates {
|
||||
let enc = Encoding::for_label(label.as_bytes()).unwrap();
|
||||
let (cow, _actual_used, had_errors) = enc.decode(bytes);
|
||||
if !had_errors {
|
||||
return cow.into_owned();
|
||||
}
|
||||
}
|
||||
|
||||
// 编码全部没有命中的话,则丢弃无法解析的部分
|
||||
s.to_string_lossy().into_owned()
|
||||
}
|
||||
Reference in New Issue
Block a user