CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
CI / runtime smoke (ubuntu x64, xvfb) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
首个 iced + Material 3 Expressive 版本的源码快照。 完整历史与日常提交在 dev 分支;main 只放阶段性快照。
142 lines
4.9 KiB
Rust
142 lines
4.9 KiB
Rust
//! `ull java vendors`:四家发行版的可用性 / 镜像 / 当前优先级顺序(D23)。
|
||
//!
|
||
//! 从 `commands/java.rs` 拆出来(那边 list/install 已经占满一屏)。这里只读不写:
|
||
//! 不联网、不改配置,把 `mirrors.json` 的表与设置里的优先级排成一张人读 + `--json` 的对照表。
|
||
//!
|
||
//! 存在的意义是**可观测**:用户问「国内为什么默认走 Oracle 而不是微软」时,命令要能直接答
|
||
//! (顺序 + 每家的镜像与实测吞吐),而不是让人翻文档。
|
||
|
||
use serde_json::{json, Value};
|
||
use unlucky_core::download::MirrorTable;
|
||
use unlucky_core::game::java::{vendor, JdkVendor};
|
||
|
||
use super::java::{region_profile, JavaSettings};
|
||
use crate::context::Ctx;
|
||
use crate::error::CliError;
|
||
use crate::output::Report;
|
||
|
||
/// `ull java vendors`。
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// 设置文件坏掉时退化为默认值;命令本身不产生用户错误。
|
||
pub fn vendors(ctx: &Ctx) -> Result<Report, CliError> {
|
||
let settings = JavaSettings::load(ctx);
|
||
let domestic = region_profile(ctx).is_domestic();
|
||
let order = vendor::order(&settings.priority, domestic);
|
||
let table = MirrorTable::builtin();
|
||
let rows: Vec<Value> = order
|
||
.iter()
|
||
.enumerate()
|
||
.map(|(index, item)| vendor_json(&table, *item, index))
|
||
.collect();
|
||
let priority_source = if settings.priority.is_empty() {
|
||
"region_default"
|
||
} else {
|
||
"java.vendorPriority"
|
||
};
|
||
let data = json!({
|
||
"region": if domestic { "domestic" } else { "global" },
|
||
"priority_source": priority_source,
|
||
"order": vendor::ids(&order),
|
||
"vendors": rows,
|
||
});
|
||
let mut human = vec![format!(
|
||
"发行版顺序({},来源 {}):{}",
|
||
if domestic { "国内" } else { "国外" },
|
||
if settings.priority.is_empty() {
|
||
"地区默认序"
|
||
} else {
|
||
"java.vendorPriority"
|
||
},
|
||
vendor::ids(&order).join(" → ")
|
||
)];
|
||
for row in &rows {
|
||
human.push(format!(
|
||
" {} {} {} 支持 Java {}",
|
||
row["rank"].as_u64().unwrap_or(0),
|
||
row["vendor"].as_str().unwrap_or("?"),
|
||
row["display"].as_str().unwrap_or(""),
|
||
join_numbers(&row["majors"], "/")
|
||
));
|
||
for source in row["sources"].as_array().into_iter().flatten() {
|
||
if let Some(text) = source.as_str() {
|
||
human.push(format!(" 源:{text}"));
|
||
}
|
||
}
|
||
}
|
||
Ok(Report::new(data, human))
|
||
}
|
||
|
||
/// 一家的 `--json` 形状(源清单按家取,缺表时为空数组而不是报错)。
|
||
fn vendor_json(table: &MirrorTable, item: JdkVendor, index: usize) -> Value {
|
||
let sources: Vec<String> = match item {
|
||
JdkVendor::Temurin => table
|
||
.adoptium
|
||
.iter()
|
||
.filter(|mirror| mirror.enabled)
|
||
.map(|mirror| {
|
||
format!(
|
||
"{}{}",
|
||
mirror.name,
|
||
if mirror.latest_only {
|
||
"(只留最新补丁)"
|
||
} else {
|
||
""
|
||
}
|
||
)
|
||
})
|
||
.chain(std::iter::once(
|
||
"GitHub 官方(国内套 gh-proxy)".to_string(),
|
||
))
|
||
.collect(),
|
||
JdkVendor::Oracle => table
|
||
.oracle_mirrors()
|
||
.map(|mirror| match mirror.speed.trim() {
|
||
"" => format!("{} {}", mirror.name, mirror.base()),
|
||
speed => format!("{} {} {}", mirror.name, mirror.base(), speed),
|
||
})
|
||
.collect(),
|
||
JdkVendor::Microsoft => table
|
||
.microsoft_openjdk()
|
||
.map(|microsoft| {
|
||
let speed = if microsoft.measured_speed.trim().is_empty() {
|
||
"实测约 400 KB/s".to_string()
|
||
} else {
|
||
microsoft.measured_speed.clone()
|
||
};
|
||
vec![format!(
|
||
"aka.ms → VS CDN({speed});无国内镜像、无 GitHub 资产"
|
||
)]
|
||
})
|
||
.unwrap_or_default(),
|
||
JdkVendor::Zulu => table
|
||
.zulu()
|
||
.map(|zulu| vec![format!("{}({})", zulu.api, zulu.measured_speed)])
|
||
.unwrap_or_default(),
|
||
};
|
||
json!({
|
||
"rank": index + 1,
|
||
"vendor": item.id(),
|
||
"display": item.display(),
|
||
"majors": item.hint_majors(),
|
||
"supported": (8..=99u32).filter(|major| item.supports(*major)).collect::<Vec<_>>(),
|
||
"sources": sources,
|
||
})
|
||
}
|
||
|
||
/// JSON 数字数组 → `21/17/11` 这种分隔串。
|
||
fn join_numbers(value: &Value, separator: &str) -> String {
|
||
value
|
||
.as_array()
|
||
.map(|items| {
|
||
items
|
||
.iter()
|
||
.filter_map(Value::as_u64)
|
||
.map(|number| number.to_string())
|
||
.collect::<Vec<_>>()
|
||
.join(separator)
|
||
})
|
||
.unwrap_or_default()
|
||
}
|