refactor(app): implement ChoiceHolder
abstraction
This commit is contained in:
parent
b94972e183
commit
10e0c5a0d3
6 changed files with 268 additions and 36 deletions
80
Cargo.lock
generated
80
Cargo.lock
generated
|
@ -873,6 +873,35 @@ dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
"time",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie_store"
|
||||||
|
version = "0.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
|
||||||
|
dependencies = [
|
||||||
|
"cookie",
|
||||||
|
"document-features",
|
||||||
|
"idna",
|
||||||
|
"indexmap 2.9.0",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"time",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
@ -2981,6 +3010,7 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"ureq",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"windows 0.61.1",
|
"windows 0.61.1",
|
||||||
|
@ -3379,7 +3409,9 @@ version = "0.23.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
|
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-webpki",
|
"rustls-webpki",
|
||||||
"subtle",
|
"subtle",
|
||||||
|
@ -4300,6 +4332,39 @@ version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ureq"
|
||||||
|
version = "3.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7a3e9af6113ecd57b8c63d3cd76a385b2e3881365f1f489e54f49801d0c83ea"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"cookie_store",
|
||||||
|
"flate2",
|
||||||
|
"log",
|
||||||
|
"percent-encoding",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"ureq-proto",
|
||||||
|
"utf-8",
|
||||||
|
"webpki-roots",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ureq-proto"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"http",
|
||||||
|
"httparse",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
|
@ -4312,6 +4377,12 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf-8"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf16_iter"
|
name = "utf16_iter"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
@ -4618,6 +4689,15 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.26.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "weezl"
|
name = "weezl"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
|
|
|
@ -47,3 +47,4 @@ uuid = { version = "1.16.0", features = ["serde"] }
|
||||||
url = { version = "2.5.4", features = ["serde"] }
|
url = { version = "2.5.4", features = ["serde"] }
|
||||||
langtag = { version = "0.4.0", features = ["serde"] }
|
langtag = { version = "0.4.0", features = ["serde"] }
|
||||||
serde_with = "3.12.0"
|
serde_with = "3.12.0"
|
||||||
|
ureq = { version = "3.0.11", features = ["json"] }
|
||||||
|
|
61
src/app.rs
61
src/app.rs
|
@ -1,21 +1,23 @@
|
||||||
use std::{fmt::Display, string::ToString, sync::Arc};
|
use std::{fmt::Display, string::ToString, sync::Arc};
|
||||||
|
|
||||||
use arch::Arch;
|
use arch::{Arch, ArchList};
|
||||||
use choice::ChoiceOption;
|
use choice::ChoiceOption;
|
||||||
use egui::{Galley, Rect, Ui};
|
use egui::{Galley, Rect, Ui};
|
||||||
|
use versions::ChoiceHolder;
|
||||||
|
|
||||||
pub(crate) mod arch;
|
pub(crate) mod arch;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub(crate) mod choice;
|
pub(crate) mod choice;
|
||||||
|
mod versions;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
env_setup_level: EnvSetupLevel,
|
env_setup_level: EnvSetupLevel,
|
||||||
preview_channel: bool,
|
preview_channel: bool,
|
||||||
msvc_version: String,
|
msvc: ChoiceHolder<String, Vec<String>>,
|
||||||
sdk_version: String,
|
sdk: ChoiceHolder<String, Vec<String>>,
|
||||||
host_arch: String,
|
host_arch: ChoiceHolder<Arch, ArchList>,
|
||||||
target_arch: String,
|
target_arch: ChoiceHolder<Arch, ArchList>,
|
||||||
install_path: String,
|
install_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,16 +25,15 @@ impl Default for App {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
env_setup_level: EnvSetupLevel::default(),
|
env_setup_level: EnvSetupLevel::default(),
|
||||||
preview_channel: bool::default(),
|
preview_channel: false,
|
||||||
msvc_version: String::default(),
|
msvc: ChoiceHolder::new(ChoiceOption::MsvcVersion(choices!("2019", "2022"))).unwrap(),
|
||||||
sdk_version: String::default(),
|
sdk: ChoiceHolder::new(ChoiceOption::SdkVersion(choices!("10.0.22621.0"))).unwrap(),
|
||||||
host_arch: Arch::default().to_string(),
|
host_arch: ChoiceHolder::new(ChoiceOption::HostArch(ArchList::default())).unwrap(),
|
||||||
target_arch: Arch::default().to_string(),
|
target_arch: ChoiceHolder::new(ChoiceOption::TargetArch(ArchList::default())).unwrap(),
|
||||||
install_path: String::default(),
|
install_path: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq)]
|
||||||
pub enum EnvSetupLevel {
|
pub enum EnvSetupLevel {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -69,21 +70,21 @@ impl App {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selection_menu(
|
fn selection_menu<T: ToString + From<String> + PartialEq, V: Into<Vec<String>>>(
|
||||||
selected: &mut String,
|
selected: &mut T,
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
width: f32,
|
width: f32,
|
||||||
component: ChoiceOption<Vec<String>>,
|
component: ChoiceOption<V>,
|
||||||
) {
|
) {
|
||||||
let name = component.to_string();
|
let name = component.to_string();
|
||||||
|
|
||||||
ui.label(name);
|
ui.label(name);
|
||||||
egui::ComboBox::from_id_salt(&component)
|
egui::ComboBox::from_id_salt(&component)
|
||||||
.width(width)
|
.width(width)
|
||||||
.selected_text(selected.clone()) // FIXME
|
.selected_text(selected.to_string()) // FIXME
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
component.options().iter().for_each(|opt| {
|
component.options().iter().for_each(|opt| {
|
||||||
ui.selectable_value(selected, opt.to_string(), opt.to_string());
|
ui.selectable_value(selected.into(), opt.clone().into(), opt.to_string());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -186,19 +187,21 @@ impl eframe::App for App {
|
||||||
ui.add_space(15.0);
|
ui.add_space(15.0);
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let width_per_choice = ctx.screen_rect().width() / 4.75;
|
let width_per_choice = ctx.screen_rect().width() / 4.75;
|
||||||
#[rustfmt::skip]
|
macro_rules! selection_menus {
|
||||||
let choices = vec![
|
($($choice:expr),*) => {
|
||||||
(&mut self.msvc_version, ChoiceOption::MsvcVersion(choices!("12.3", "12.4", "12.4+extra"))),
|
$(ui.vertical(|ui| {
|
||||||
(&mut self.sdk_version, ChoiceOption::SdkVersion(choices!("12.3", "12.4", "12.4+extra"))),
|
let x = $choice.available.clone();
|
||||||
(&mut self.host_arch, ChoiceOption::HostArch(choices!(Arch::X86, Arch::X64))),
|
Self::selection_menu($choice.selected_mut(), ui, width_per_choice, x)
|
||||||
(&mut self.target_arch, ChoiceOption::TargetArch(choices!(Arch::X86, Arch::X64))),
|
});)*
|
||||||
];
|
};
|
||||||
|
|
||||||
for (selection, choice) in choices {
|
|
||||||
ui.vertical(|ui| {
|
|
||||||
Self::selection_menu(selection, ui, width_per_choice, choice.clone())
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selection_menus![
|
||||||
|
self.msvc,
|
||||||
|
self.sdk,
|
||||||
|
self.host_arch,
|
||||||
|
self.target_arch
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.add_space(25.0);
|
ui.add_space(25.0);
|
||||||
|
|
|
@ -1,22 +1,80 @@
|
||||||
use std::env::consts;
|
use std::{env::consts, ops::Deref, str::FromStr};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum_macros::{Display, EnumString};
|
use strum_macros::{Display, EnumString};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, EnumString, Display, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Copy, EnumString, Display, Deserialize, Serialize, PartialEq)]
|
||||||
#[strum(serialize_all = "lowercase")]
|
#[strum(serialize_all = "lowercase")]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Arch {
|
pub enum Arch {
|
||||||
X86,
|
X86,
|
||||||
|
#[strum(to_string = "x64", serialize = "x86_64")]
|
||||||
X64,
|
X64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Arch {
|
||||||
|
pub fn all() -> [Arch; 2] {
|
||||||
|
match Arch::default() {
|
||||||
|
Arch::X86 => [Arch::X86, Arch::X64],
|
||||||
|
Arch::X64 => [Arch::X64, Arch::X86],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Arch {
|
impl Default for Arch {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
match consts::ARCH {
|
Self::from_str(consts::ARCH).unwrap()
|
||||||
"x86" => Self::X86,
|
}
|
||||||
"x86_64" => Self::X64,
|
}
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
impl From<String> for Arch {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
Self::from_str(s.as_str()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for Arch {
|
||||||
|
fn into(self) -> String {
|
||||||
|
self.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ArchList(pub Vec<Arch>);
|
||||||
|
|
||||||
|
impl From<ArchList> for Vec<String> {
|
||||||
|
fn from(archs: ArchList) -> Vec<String> {
|
||||||
|
archs.0.into_iter().map(|arch| arch.to_string()).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<String>> for ArchList {
|
||||||
|
fn from(archs: Vec<String>) -> ArchList {
|
||||||
|
ArchList(archs.into_iter().map(|arch| arch.into()).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Arch>> for ArchList {
|
||||||
|
fn from(archs: Vec<Arch>) -> ArchList {
|
||||||
|
ArchList(archs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ArchList {
|
||||||
|
type Target = Vec<Arch>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Vec<Arch>> for ArchList {
|
||||||
|
fn into(self) -> Vec<Arch> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ArchList {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Arch::all().to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -40,3 +40,11 @@ impl<T: Into<Vec<String>>> Hash for ChoiceOption<T> {
|
||||||
self.to_string().hash(state);
|
self.to_string().hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Vec<String>>> IntoIterator for ChoiceOption<T> {
|
||||||
|
type Item = String;
|
||||||
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.options().into_iter()
|
||||||
|
}
|
||||||
|
}
|
82
src/app/versions.rs
Executable file
82
src/app/versions.rs
Executable file
|
@ -0,0 +1,82 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
arch::{Arch, ArchList},
|
||||||
|
choice::ChoiceOption,
|
||||||
|
};
|
||||||
|
|
||||||
|
use color_eyre::{Result, eyre::eyre};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ChoiceHolder<T, V: Into<Vec<T>>>
|
||||||
|
where
|
||||||
|
Vec<String>: From<V>,
|
||||||
|
{
|
||||||
|
pub available: ChoiceOption<V>,
|
||||||
|
pub selected: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Clone, V: Clone + Into<Vec<T>>> ChoiceHolder<T, V>
|
||||||
|
where
|
||||||
|
Vec<String>: From<V>,
|
||||||
|
T: From<String>,
|
||||||
|
{
|
||||||
|
pub fn new(available: ChoiceOption<V>) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
available: available.clone(),
|
||||||
|
selected: Into::<T>::into(
|
||||||
|
available
|
||||||
|
.options()
|
||||||
|
.first()
|
||||||
|
.ok_or(eyre!("Available options must not be an empty Vec"))?
|
||||||
|
.clone(),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.selected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Following code is very ugly, consider using a macro
|
||||||
|
|
||||||
|
impl Into<ChoiceHolder<String, Vec<String>>> for ChoiceHolder<Arch, ArchList> {
|
||||||
|
fn into(self) -> ChoiceHolder<String, Vec<String>> {
|
||||||
|
match self.available {
|
||||||
|
ChoiceOption::HostArch(archs) => ChoiceHolder::new(ChoiceOption::HostArch(
|
||||||
|
archs.clone().iter().map(|arch| arch.to_string()).collect(),
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
ChoiceOption::TargetArch(archs) => ChoiceHolder::new(ChoiceOption::TargetArch(
|
||||||
|
archs.clone().iter().map(|arch| arch.to_string()).collect(),
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ChoiceHolder<String, Vec<String>>> for ChoiceHolder<Arch, ArchList> {
|
||||||
|
fn from(value: ChoiceHolder<String, Vec<String>>) -> Self {
|
||||||
|
match value.available {
|
||||||
|
ChoiceOption::HostArch(archs) => ChoiceHolder::new(ChoiceOption::HostArch(
|
||||||
|
archs
|
||||||
|
.iter()
|
||||||
|
.map(|arch| Arch::from_str(arch.as_str()).unwrap())
|
||||||
|
.collect::<Vec<Arch>>()
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
ChoiceOption::TargetArch(archs) => ChoiceHolder::new(ChoiceOption::TargetArch(
|
||||||
|
archs
|
||||||
|
.iter()
|
||||||
|
.map(|arch| Arch::from_str(arch.as_str()).unwrap())
|
||||||
|
.collect::<Vec<Arch>>()
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue