Skip to content

Commit 0f49aa9

Browse files
feat: add setup
Signed-off-by: Oskar Manhart <[email protected]>
1 parent badf5c0 commit 0f49aa9

File tree

12 files changed

+171
-104
lines changed

12 files changed

+171
-104
lines changed

Cargo.lock

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

winapps-cli/src/main.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ fn main() -> Result<()> {
4848
.with_env_filter(EnvFilter::from_default_env())
4949
.init();
5050

51-
let config = Config::load()?;
51+
let config_lock = Config::try_get_lock()?;
52+
let config = config_lock.read();
5253

53-
let client = Freerdp::new(config);
54+
let client = Freerdp::new();
5455
let backend = config.get_backend();
5556

5657
client.check_depends()?;
@@ -61,7 +62,20 @@ fn main() -> Result<()> {
6162
match cli.clone().get_matches().subcommand() {
6263
Some(("setup", _)) => {
6364
info!("Running setup");
64-
todo!()
65+
66+
match inquire::MultiSelect::new("Select apps to link", config.get_available_apps()?)
67+
.prompt_skippable()
68+
.map_err(|e| winapps::Error::Command {
69+
message: "Failed to display selection dialog".into(),
70+
source: e.into(),
71+
})? {
72+
Some(apps) => apps
73+
.into_iter()
74+
.try_for_each(|app| app.link(&mut config_lock.write(), "".into()))?,
75+
None => info!("No apps selected, skipping setup..."),
76+
};
77+
78+
Ok(())
6579
}
6680

6781
Some(("connect", _)) => {

winapps/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ thiserror = "2.0.9"
1717
enum_dispatch = "0.3.13"
1818
base64 = "0.22.1"
1919
regex = { version = "1.11.2", features = [] }
20+
parking_lot = "0.12.5"

winapps/src/backend/container.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate::{command::command, ensure, Backend, Config, Error, Result};
2+
use parking_lot::RwLock;
23
use std::net::{IpAddr, Ipv4Addr};
34
use tracing::debug;
45

56
#[derive(Debug, Clone)]
67
pub struct Container {
7-
config: &'static Config,
8+
config: &'static RwLock<Config>,
89
}
910

1011
impl Container {
@@ -13,29 +14,32 @@ impl Container {
1314
const DEFAULT_COMMAND: &'static str = "docker";
1415
const PODMAN_COMMAND: &'static str = "podman";
1516

16-
pub(crate) fn new(config: &'static Config) -> Self {
17-
Self { config }
17+
pub(crate) fn new() -> Self {
18+
Self {
19+
config: Config::get_lock(),
20+
}
1821
}
1922
}
2023

2124
impl Backend for Container {
2225
fn check_depends(&self) -> Result<()> {
23-
assert!(self.config.container.enable);
26+
let config = self.config.read();
27+
assert!(config.container.enable);
2428

2529
ensure!(
26-
!self.config.container.container_name.is_empty(),
30+
!config.container.container_name.is_empty(),
2731
Error::Config("Container name shouldn't be empty")
2832
);
2933

30-
let command = if self.config.container.enable_podman {
34+
let command = if config.container.enable_podman {
3135
Self::PODMAN_COMMAND
3236
} else {
3337
Self::DEFAULT_COMMAND
3438
};
3539

3640
let state = command!(
3741
r#"{command} ps --all --filter name={} --format {{{{.State}}}}"#,
38-
self.config.container.container_name
42+
config.container.container_name
3943
)?
4044
.with_err("Could not get container status")
4145
.wait_with_output()?;

winapps/src/backend/manual.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,45 @@
1+
use parking_lot::RwLock;
12
use std::{net::IpAddr, str::FromStr};
23

34
use crate::{ensure, Backend, Config, Error, Result};
45

56
#[derive(Debug, Clone)]
67
pub struct Manual {
7-
config: &'static Config,
8+
config: &'static RwLock<Config>,
89
}
910

1011
impl Manual {
11-
pub(crate) fn new(config: &'static Config) -> Self {
12-
Self { config }
12+
pub(crate) fn new() -> Self {
13+
Self {
14+
config: Config::get_lock(),
15+
}
1316
}
1417
}
1518

1619
impl Backend for Manual {
1720
fn check_depends(&self) -> Result<()> {
18-
assert!(self.config.manual.enable);
21+
let config = self.config.read();
22+
23+
assert!(config.manual.enable);
1924
ensure!(
20-
!self.config.manual.host.is_empty(),
25+
!config.manual.host.is_empty(),
2126
Error::Config("Host shouldn't be empty")
2227
);
2328

2429
ensure!(
25-
IpAddr::from_str(&self.config.manual.host).is_ok(),
30+
IpAddr::from_str(&config.manual.host).is_ok(),
2631
Error::Config("manual.host is not a valid IP address")
2732
);
2833

2934
Ok(())
3035
}
3136

3237
fn get_host(&self) -> IpAddr {
38+
let config = self.config.read();
39+
3340
// SAFETY: When the config is read, we check that this is a valid IP
3441
// We assume that the program will never write this field,
3542
// so it should always be valid at this point
36-
IpAddr::from_str(&self.config.manual.host).unwrap()
43+
IpAddr::from_str(&config.manual.host).unwrap()
3744
}
3845
}

winapps/src/backend/mod.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use enum_dispatch::enum_dispatch;
55
use crate::{
66
backend::{container::Container, manual::Manual},
77
command::Command,
8-
config::App,
8+
config::{App, AppKind::Detected},
99
Config, Result,
1010
};
1111

@@ -27,27 +27,26 @@ pub enum Backends {
2727
}
2828

2929
impl Config {
30-
pub fn get_backend(&'static self) -> &'static Backends {
30+
pub fn get_backend(&self) -> &Backends {
3131
self.backend.get_or_init(|| {
3232
match (
3333
self.libvirt.enable,
3434
self.container.enable,
3535
self.manual.enable,
3636
) {
3737
(true, _, _) => todo!(),
38-
(_, true, _) => Container::new(self).into(),
39-
(_, _, true) => Manual::new(self).into(),
38+
(_, true, _) => Container::new().into(),
39+
(_, _, true) => Manual::new().into(),
4040
_ => unreachable!(),
4141
}
4242
})
4343
}
4444

45-
pub fn get_host(&'static self) -> IpAddr {
45+
pub fn get_host(&self) -> IpAddr {
4646
self.get_backend().get_host()
4747
}
4848

49-
#[allow(dead_code)]
50-
fn get_installed_apps(&'static self) -> Result<Vec<App>> {
49+
pub fn get_available_apps(&self) -> Result<Vec<App>> {
5150
let apps = Command::new("C:\\ExtractPrograms.ps1")
5251
.into_remote(self)
5352
.wait_with_output()?
@@ -60,8 +59,7 @@ impl Config {
6059
id: id.to_string(),
6160
name: name.to_string(),
6261
win_exec: path.to_string(),
63-
icon: Some(icon.to_string()),
64-
icon_path: None,
62+
kind: Detected(icon.to_string()),
6563
}),
6664
_ => None,
6765
}

winapps/src/command.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,16 @@ impl FromStr for Command {
5151
}
5252

5353
impl Command {
54-
pub fn new(exec: &'static str) -> Self {
54+
pub fn new<T: Into<String>>(exec: T) -> Self {
5555
Self {
56-
exec: exec.to_string(),
56+
exec: exec.into(),
5757
args: Vec::new(),
5858
error_message: String::from("Error running child command"),
5959
loud: false,
6060
}
6161
}
6262

63-
pub fn into_remote(mut self, config: &'static Config) -> Self {
63+
pub fn into_remote(mut self, config: &Config) -> Self {
6464
let prev = format!("{} {}", self.exec, self.args.join(" "));
6565

6666
self.exec = "sshpass".to_string();

winapps/src/config/apps.rs

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,64 @@
11
use crate::{
2-
config::App,
2+
config::{App, AppKind},
33
dirs::{desktop_dir, icons_dir},
44
Config, Result,
55
};
66
use base64::{prelude::BASE64_STANDARD, Engine};
7-
use std::fs::write;
7+
use std::{fmt::Display, fs::write};
88

99
impl PartialEq for App {
1010
fn eq(&self, other: &Self) -> bool {
1111
self.id == other.id
1212
}
1313
}
1414

15+
impl Display for App {
16+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17+
f.write_fmt(format_args!("{} ({})", self.name, self.win_exec))
18+
}
19+
}
20+
1521
impl App {
16-
/// Panics: If `self.icon_path` is `None`
17-
/// Make sure to write the icon to a file by now
18-
/// and populate `icon_path`
19-
/// This should be normally done by now
20-
fn as_desktop_file(&self, exec: String) -> String {
21-
format!(
22-
"[Desktop Entry]
22+
fn try_as_existing(&mut self) -> Result<&mut Self> {
23+
match self.kind.clone() {
24+
AppKind::Detected(base64) => {
25+
let path = icons_dir()?.join(format!("{}.png", self.id));
26+
write(path.clone(), BASE64_STANDARD.decode(base64)?)?;
27+
28+
self.kind = AppKind::Existing(path);
29+
30+
Ok(self)
31+
}
32+
AppKind::Existing(_) => Ok(self),
33+
}
34+
}
35+
36+
fn try_as_desktop_file(&mut self, exec: String) -> Result<String> {
37+
match &self.kind {
38+
AppKind::Detected(_) => self.try_as_existing()?.try_as_desktop_file(exec),
39+
AppKind::Existing(path) => Ok(format!(
40+
"[Desktop Entry]
2341
Name={}
2442
Exec={exec}
2543
Terminal=false
2644
Type=Application
2745
Icon={}
2846
StartupWMClass={}
2947
Comment={}",
30-
self.name,
31-
self.icon_path.clone().unwrap(),
32-
self.id,
33-
self.name
34-
)
48+
self.name,
49+
path.to_string_lossy(),
50+
self.id,
51+
self.name
52+
)),
53+
}
3554
}
3655

37-
/// Panics: If `self.icon` is `None` and `write_icon` is `true` OR if `self.icon_path` is `None` and `write_icon` is `false` (or if both are `None`)
38-
/// At this point in the program, that shouldn't normally be the case
39-
pub fn link(self, config: &mut Config, exec: String, write_icon: bool) -> Result<()> {
40-
if write_icon {
41-
write(
42-
icons_dir()?.join(format!("{}.png", self.id)),
43-
BASE64_STANDARD.decode(self.icon.clone().unwrap())?,
44-
)?;
45-
}
56+
pub fn link(mut self, config: &mut Config, exec: String) -> Result<()> {
57+
self.try_as_existing()?;
4658

4759
write(
4860
desktop_dir()?.join(format!("{}.desktop", self.id)),
49-
self.as_desktop_file(exec),
61+
self.try_as_desktop_file(exec)?,
5062
)?;
5163

5264
if !config.linked_apps.contains(&self) {

0 commit comments

Comments
 (0)