mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
feat: add methods to retrieve current subscription details in PrfItem and refactor profile handling in IProfiles
This commit is contained in:
@@ -584,6 +584,33 @@ impl PrfItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PrfItem {
|
||||||
|
/// 获取current指向的订阅的merge
|
||||||
|
pub fn current_merge(&self) -> Option<String> {
|
||||||
|
self.option.as_ref().and_then(|o| o.merge.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取current指向的订阅的script
|
||||||
|
pub fn current_script(&self) -> Option<String> {
|
||||||
|
self.option.as_ref().and_then(|o| o.script.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取current指向的订阅的rules
|
||||||
|
pub fn current_rules(&self) -> Option<String> {
|
||||||
|
self.option.as_ref().and_then(|o| o.rules.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取current指向的订阅的proxies
|
||||||
|
pub fn current_proxies(&self) -> Option<String> {
|
||||||
|
self.option.as_ref().and_then(|o| o.proxies.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取current指向的订阅的groups
|
||||||
|
pub fn current_groups(&self) -> Option<String> {
|
||||||
|
self.option.as_ref().and_then(|o| o.groups.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 向前兼容,默认为订阅启用自动更新
|
// 向前兼容,默认为订阅启用自动更新
|
||||||
fn default_allow_auto_update() -> Option<bool> {
|
fn default_allow_auto_update() -> Option<bool> {
|
||||||
Some(true)
|
Some(true)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use anyhow::{Context, Result, bail};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml_ng::Mapping;
|
use serde_yaml_ng::Mapping;
|
||||||
use smartstring::alias::String;
|
use smartstring::alias::String;
|
||||||
use std::collections::HashSet;
|
use std::{collections::HashSet, sync::Arc};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
/// Define the `profiles.yaml` schema
|
/// Define the `profiles.yaml` schema
|
||||||
@@ -104,8 +104,8 @@ impl IProfiles {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current(&self) -> Option<String> {
|
pub fn get_current(&self) -> Option<&String> {
|
||||||
self.current.clone()
|
self.current.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get items ref
|
/// get items ref
|
||||||
@@ -130,6 +130,15 @@ impl IProfiles {
|
|||||||
bail!("failed to get the profile item \"uid:{}\"", uid_str);
|
bail!("failed to get the profile item \"uid:{}\"", uid_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_item_arc(&self, uid: &str) -> Option<Arc<PrfItem>> {
|
||||||
|
self.items.as_ref().and_then(|items| {
|
||||||
|
items
|
||||||
|
.iter()
|
||||||
|
.find(|it| it.uid.as_deref() == Some(uid))
|
||||||
|
.map(|it| Arc::new(it.clone()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// append new item
|
/// append new item
|
||||||
/// if the file_data is some
|
/// if the file_data is some
|
||||||
/// then should save the data to file
|
/// then should save the data to file
|
||||||
@@ -355,76 +364,6 @@ impl IProfiles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取current指向的订阅的merge
|
|
||||||
pub fn current_merge(&self) -> Option<String> {
|
|
||||||
match (self.current.as_ref(), self.items.as_ref()) {
|
|
||||||
(Some(current), Some(items)) => {
|
|
||||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
|
||||||
let merge = item.option.as_ref().and_then(|e| e.merge.clone());
|
|
||||||
return merge;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取current指向的订阅的script
|
|
||||||
pub fn current_script(&self) -> Option<String> {
|
|
||||||
match (self.current.as_ref(), self.items.as_ref()) {
|
|
||||||
(Some(current), Some(items)) => {
|
|
||||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
|
||||||
let script = item.option.as_ref().and_then(|e| e.script.clone());
|
|
||||||
return script;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取current指向的订阅的rules
|
|
||||||
pub fn current_rules(&self) -> Option<String> {
|
|
||||||
match (self.current.as_ref(), self.items.as_ref()) {
|
|
||||||
(Some(current), Some(items)) => {
|
|
||||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
|
||||||
let rules = item.option.as_ref().and_then(|e| e.rules.clone());
|
|
||||||
return rules;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取current指向的订阅的proxies
|
|
||||||
pub fn current_proxies(&self) -> Option<String> {
|
|
||||||
match (self.current.as_ref(), self.items.as_ref()) {
|
|
||||||
(Some(current), Some(items)) => {
|
|
||||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
|
||||||
let proxies = item.option.as_ref().and_then(|e| e.proxies.clone());
|
|
||||||
return proxies;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取current指向的订阅的groups
|
|
||||||
pub fn current_groups(&self) -> Option<String> {
|
|
||||||
match (self.current.as_ref(), self.items.as_ref()) {
|
|
||||||
(Some(current), Some(items)) => {
|
|
||||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
|
||||||
let groups = item.option.as_ref().and_then(|e| e.groups.clone());
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 判断profile是否是current指向的
|
/// 判断profile是否是current指向的
|
||||||
pub fn is_current_profile_index(&self, index: &String) -> bool {
|
pub fn is_current_profile_index(&self, index: &String) -> bool {
|
||||||
self.current.as_ref() == Some(index)
|
self.current.as_ref() == Some(index)
|
||||||
@@ -447,11 +386,11 @@ impl IProfiles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 通过 uid 获取名称
|
/// 通过 uid 获取名称
|
||||||
pub fn get_name_by_uid(&self, uid: &String) -> Option<String> {
|
pub fn get_name_by_uid(&self, uid: &String) -> Option<&String> {
|
||||||
if let Some(items) = &self.items {
|
if let Some(items) = &self.items {
|
||||||
for item in items {
|
for item in items {
|
||||||
if item.uid.as_ref() == Some(uid) {
|
if item.uid.as_ref() == Some(uid) {
|
||||||
return item.name.clone();
|
return item.name.as_ref();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -549,14 +488,14 @@ impl IProfiles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 获取所有 active profile 关联的文件名
|
/// 获取所有 active profile 关联的文件名
|
||||||
fn get_all_active_files(&self) -> HashSet<String> {
|
fn get_all_active_files(&self) -> HashSet<&str> {
|
||||||
let mut active_files = HashSet::new();
|
let mut active_files: HashSet<&str> = HashSet::new();
|
||||||
|
|
||||||
if let Some(items) = &self.items {
|
if let Some(items) = &self.items {
|
||||||
for item in items {
|
for item in items {
|
||||||
// 收集所有类型 profile 的文件
|
// 收集所有类型 profile 的文件
|
||||||
if let Some(file) = &item.file {
|
if let Some(file) = &item.file {
|
||||||
active_files.insert(file.clone());
|
active_files.insert(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对于主 profile 类型(remote/local),还需要收集其关联的扩展文件
|
// 对于主 profile 类型(remote/local),还需要收集其关联的扩展文件
|
||||||
@@ -569,35 +508,35 @@ impl IProfiles {
|
|||||||
&& let Ok(merge_item) = self.get_item(merge_uid)
|
&& let Ok(merge_item) = self.get_item(merge_uid)
|
||||||
&& let Some(file) = &merge_item.file
|
&& let Some(file) = &merge_item.file
|
||||||
{
|
{
|
||||||
active_files.insert(file.clone());
|
active_files.insert(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(script_uid) = &option.script
|
if let Some(script_uid) = &option.script
|
||||||
&& let Ok(script_item) = self.get_item(script_uid)
|
&& let Ok(script_item) = self.get_item(script_uid)
|
||||||
&& let Some(file) = &script_item.file
|
&& let Some(file) = &script_item.file
|
||||||
{
|
{
|
||||||
active_files.insert(file.clone());
|
active_files.insert(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(rules_uid) = &option.rules
|
if let Some(rules_uid) = &option.rules
|
||||||
&& let Ok(rules_item) = self.get_item(rules_uid)
|
&& let Ok(rules_item) = self.get_item(rules_uid)
|
||||||
&& let Some(file) = &rules_item.file
|
&& let Some(file) = &rules_item.file
|
||||||
{
|
{
|
||||||
active_files.insert(file.clone());
|
active_files.insert(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(proxies_uid) = &option.proxies
|
if let Some(proxies_uid) = &option.proxies
|
||||||
&& let Ok(proxies_item) = self.get_item(proxies_uid)
|
&& let Ok(proxies_item) = self.get_item(proxies_uid)
|
||||||
&& let Some(file) = &proxies_item.file
|
&& let Some(file) = &proxies_item.file
|
||||||
{
|
{
|
||||||
active_files.insert(file.clone());
|
active_files.insert(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(groups_uid) = &option.groups
|
if let Some(groups_uid) = &option.groups
|
||||||
&& let Ok(groups_item) = self.get_item(groups_uid)
|
&& let Ok(groups_item) = self.get_item(groups_uid)
|
||||||
&& let Some(file) = &groups_item.file
|
&& let Some(file) = &groups_item.file
|
||||||
{
|
{
|
||||||
active_files.insert(file.clone());
|
active_files.insert(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ impl Handle {
|
|||||||
Self::send_event(FrontendEvent::ProfileUpdateCompleted { uid });
|
Self::send_event(FrontendEvent::ProfileUpdateCompleted { uid });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 利用 &str 等缩短 Clone
|
||||||
pub fn notice_message<S: Into<String>, M: Into<String>>(status: S, msg: M) {
|
pub fn notice_message<S: Into<String>, M: Into<String>>(status: S, msg: M) {
|
||||||
let handle = Self::global();
|
let handle = Self::global();
|
||||||
let status_str = status.into();
|
let status_str = status.into();
|
||||||
|
|||||||
@@ -455,7 +455,7 @@ impl Tray {
|
|||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
let profiles = profiles.latest_arc();
|
let profiles = profiles.latest_arc();
|
||||||
if let Some(current_profile_uid) = profiles.get_current()
|
if let Some(current_profile_uid) = profiles.get_current()
|
||||||
&& let Ok(profile) = profiles.get_item(¤t_profile_uid)
|
&& let Ok(profile) = profiles.get_item(current_profile_uid)
|
||||||
{
|
{
|
||||||
current_profile_name = match &profile.name {
|
current_profile_name = match &profile.name {
|
||||||
Some(profile_name) => profile_name.to_string(),
|
Some(profile_name) => profile_name.to_string(),
|
||||||
@@ -842,7 +842,7 @@ async fn create_tray_menu(
|
|||||||
let profiles_ref = profiles_config.latest_arc();
|
let profiles_ref = profiles_config.latest_arc();
|
||||||
profiles_ref
|
profiles_ref
|
||||||
.get_current()
|
.get_current()
|
||||||
.and_then(|uid| profiles_ref.get_item(&uid).ok())
|
.and_then(|uid| profiles_ref.get_item(uid).ok())
|
||||||
.and_then(|profile| profile.selected.clone())
|
.and_then(|profile| profile.selected.clone())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,6 +44,43 @@ struct ProfileItems {
|
|||||||
profile_name: String,
|
profile_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ProfileItems {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
config: Default::default(),
|
||||||
|
profile_name: Default::default(),
|
||||||
|
merge_item: ChainItem {
|
||||||
|
uid: "".into(),
|
||||||
|
data: ChainType::Merge(Mapping::new()),
|
||||||
|
},
|
||||||
|
script_item: ChainItem {
|
||||||
|
uid: "".into(),
|
||||||
|
data: ChainType::Script(tmpl::ITEM_SCRIPT.into()),
|
||||||
|
},
|
||||||
|
rules_item: ChainItem {
|
||||||
|
uid: "".into(),
|
||||||
|
data: ChainType::Rules(SeqMap::default()),
|
||||||
|
},
|
||||||
|
proxies_item: ChainItem {
|
||||||
|
uid: "".into(),
|
||||||
|
data: ChainType::Proxies(SeqMap::default()),
|
||||||
|
},
|
||||||
|
groups_item: ChainItem {
|
||||||
|
uid: "".into(),
|
||||||
|
data: ChainType::Groups(SeqMap::default()),
|
||||||
|
},
|
||||||
|
global_merge: ChainItem {
|
||||||
|
uid: "Merge".into(),
|
||||||
|
data: ChainType::Merge(Mapping::new()),
|
||||||
|
},
|
||||||
|
global_script: ChainItem {
|
||||||
|
uid: "Script".into(),
|
||||||
|
data: ChainType::Script(tmpl::ITEM_SCRIPT.into()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_config_values() -> ConfigValues {
|
async fn get_config_values() -> ConfigValues {
|
||||||
let clash_config = { Config::clash().await.latest_arc().0.clone() };
|
let clash_config = { Config::clash().await.latest_arc().0.clone() };
|
||||||
|
|
||||||
@@ -89,18 +126,10 @@ async fn get_config_values() -> ConfigValues {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
async fn collect_profile_items() -> ProfileItems {
|
async fn collect_profile_items() -> ProfileItems {
|
||||||
// 从profiles里拿东西 - 先收集需要的数据,然后释放锁
|
// 从profiles里拿东西 - 先收集需要的数据,然后释放锁
|
||||||
let (
|
let (current, merge_uid, script_uid, rules_uid, proxies_uid, groups_uid, name) = {
|
||||||
current,
|
|
||||||
merge_uid,
|
|
||||||
script_uid,
|
|
||||||
rules_uid,
|
|
||||||
proxies_uid,
|
|
||||||
groups_uid,
|
|
||||||
_current_profile_uid,
|
|
||||||
name,
|
|
||||||
) = {
|
|
||||||
let current = {
|
let current = {
|
||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
let profiles_clone = profiles.latest_arc();
|
let profiles_clone = profiles.latest_arc();
|
||||||
@@ -109,13 +138,31 @@ async fn collect_profile_items() -> ProfileItems {
|
|||||||
|
|
||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
let profiles_ref = profiles.latest_arc();
|
let profiles_ref = profiles.latest_arc();
|
||||||
|
let current_profile_uid = match profiles_ref.get_current() {
|
||||||
|
Some(uid) => uid.clone(),
|
||||||
|
None => return ProfileItems::default(),
|
||||||
|
};
|
||||||
|
|
||||||
let merge_uid = profiles_ref.current_merge().unwrap_or_default();
|
let current_item = match profiles_ref.get_item_arc(¤t_profile_uid) {
|
||||||
let script_uid = profiles_ref.current_script().unwrap_or_default();
|
Some(item) => item,
|
||||||
let rules_uid = profiles_ref.current_rules().unwrap_or_default();
|
None => return ProfileItems::default(),
|
||||||
let proxies_uid = profiles_ref.current_proxies().unwrap_or_default();
|
};
|
||||||
let groups_uid = profiles_ref.current_groups().unwrap_or_default();
|
|
||||||
let current_profile_uid = profiles_ref.get_current().unwrap_or_default();
|
let merge_uid = current_item
|
||||||
|
.current_merge()
|
||||||
|
.unwrap_or_else(|| "Merge".into());
|
||||||
|
let script_uid = current_item
|
||||||
|
.current_script()
|
||||||
|
.unwrap_or_else(|| "Script".into());
|
||||||
|
let rules_uid = current_item
|
||||||
|
.current_rules()
|
||||||
|
.unwrap_or_else(|| "Rules".into());
|
||||||
|
let proxies_uid = current_item
|
||||||
|
.current_proxies()
|
||||||
|
.unwrap_or_else(|| "Proxies".into());
|
||||||
|
let groups_uid = current_item
|
||||||
|
.current_groups()
|
||||||
|
.unwrap_or_else(|| "Groups".into());
|
||||||
|
|
||||||
let name = profiles_ref
|
let name = profiles_ref
|
||||||
.get_item(¤t_profile_uid)
|
.get_item(¤t_profile_uid)
|
||||||
@@ -130,7 +177,6 @@ async fn collect_profile_items() -> ProfileItems {
|
|||||||
rules_uid,
|
rules_uid,
|
||||||
proxies_uid,
|
proxies_uid,
|
||||||
groups_uid,
|
groups_uid,
|
||||||
current_profile_uid,
|
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@@ -140,7 +186,7 @@ async fn collect_profile_items() -> ProfileItems {
|
|||||||
let item = {
|
let item = {
|
||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
let profiles = profiles.latest_arc();
|
let profiles = profiles.latest_arc();
|
||||||
profiles.get_item(merge_uid).ok().cloned()
|
profiles.get_item(&merge_uid).ok().cloned()
|
||||||
};
|
};
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
<Option<ChainItem>>::from_async(&item).await
|
<Option<ChainItem>>::from_async(&item).await
|
||||||
@@ -157,7 +203,7 @@ async fn collect_profile_items() -> ProfileItems {
|
|||||||
let item = {
|
let item = {
|
||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
let profiles = profiles.latest_arc();
|
let profiles = profiles.latest_arc();
|
||||||
profiles.get_item(script_uid).ok().cloned()
|
profiles.get_item(&script_uid).ok().cloned()
|
||||||
};
|
};
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
<Option<ChainItem>>::from_async(&item).await
|
<Option<ChainItem>>::from_async(&item).await
|
||||||
@@ -174,7 +220,7 @@ async fn collect_profile_items() -> ProfileItems {
|
|||||||
let item = {
|
let item = {
|
||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
let profiles = profiles.latest_arc();
|
let profiles = profiles.latest_arc();
|
||||||
profiles.get_item(rules_uid).ok().cloned()
|
profiles.get_item(&rules_uid).ok().cloned()
|
||||||
};
|
};
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
<Option<ChainItem>>::from_async(&item).await
|
<Option<ChainItem>>::from_async(&item).await
|
||||||
@@ -191,7 +237,7 @@ async fn collect_profile_items() -> ProfileItems {
|
|||||||
let item = {
|
let item = {
|
||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
let profiles = profiles.latest_arc();
|
let profiles = profiles.latest_arc();
|
||||||
profiles.get_item(proxies_uid).ok().cloned()
|
profiles.get_item(&proxies_uid).ok().cloned()
|
||||||
};
|
};
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
<Option<ChainItem>>::from_async(&item).await
|
<Option<ChainItem>>::from_async(&item).await
|
||||||
@@ -208,7 +254,7 @@ async fn collect_profile_items() -> ProfileItems {
|
|||||||
let item = {
|
let item = {
|
||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
let profiles = profiles.latest_arc();
|
let profiles = profiles.latest_arc();
|
||||||
profiles.get_item(groups_uid).ok().cloned()
|
profiles.get_item(&groups_uid).ok().cloned()
|
||||||
};
|
};
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
<Option<ChainItem>>::from_async(&item).await
|
<Option<ChainItem>>::from_async(&item).await
|
||||||
|
|||||||
@@ -152,13 +152,13 @@ async fn perform_profile_update(
|
|||||||
let profiles = Config::profiles().await;
|
let profiles = Config::profiles().await;
|
||||||
profiles.latest_arc().is_current_profile_index(uid)
|
profiles.latest_arc().is_current_profile_index(uid)
|
||||||
};
|
};
|
||||||
let profile_name = {
|
let profiles = Config::profiles().await;
|
||||||
let profiles = Config::profiles().await;
|
let profiles_arc = profiles.latest_arc();
|
||||||
profiles
|
let profile_name = profiles_arc
|
||||||
.latest_arc()
|
.get_name_by_uid(uid)
|
||||||
.get_name_by_uid(uid)
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_else(|| String::from("UnKown Profile"));
|
||||||
};
|
|
||||||
let mut last_err;
|
let mut last_err;
|
||||||
|
|
||||||
match PrfItem::from_url(url, None, None, merged_opt.as_ref()).await {
|
match PrfItem::from_url(url, None, None, merged_opt.as_ref()).await {
|
||||||
|
|||||||
Reference in New Issue
Block a user