mirror of
https://github.com/safing/portmaster
synced 2025-09-02 10:39:22 +00:00
[desktop] Fix all clippy warning. Add clippy to CI.
This commit is contained in:
parent
9472a1f8f5
commit
4c340f7b70
13 changed files with 208 additions and 167 deletions
19
.github/workflows/tauri.yml
vendored
19
.github/workflows/tauri.yml
vendored
|
@ -34,3 +34,22 @@ jobs:
|
||||||
|
|
||||||
- name: Build tauri project
|
- name: Build tauri project
|
||||||
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +tauri-ci
|
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +tauri-ci
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Linter
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: earthly/actions-setup@v1
|
||||||
|
with:
|
||||||
|
version: v0.8.0
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build tauri project
|
||||||
|
run: earthly --ci --remote-cache=ghcr.io/safing/build-cache --push +tauri-lint
|
||||||
|
|
36
Earthfile
36
Earthfile
|
@ -636,6 +636,42 @@ tauri-release:
|
||||||
BUILD +tauri-build --target="${arch}"
|
BUILD +tauri-build --target="${arch}"
|
||||||
END
|
END
|
||||||
|
|
||||||
|
tauri-lint:
|
||||||
|
FROM +tauri-src
|
||||||
|
|
||||||
|
# Clippy (rust linter) will try to build the project before it runs the linter.
|
||||||
|
# Make sure we have evrything needed to build the project.
|
||||||
|
ARG target="x86_64-unknown-linux-gnu"
|
||||||
|
|
||||||
|
# if we want tauri to create the installer bundles we also need to provide all external binaries
|
||||||
|
# we need to do some magic here because tauri expects the binaries to include the rust target tripple.
|
||||||
|
# We already know that triple because it's a required argument. From that triple, we use +RUST_TO_GO_ARCH_STRING
|
||||||
|
# function from below to parse the triple and guess wich GOOS and GOARCH we need.
|
||||||
|
RUN mkdir /tmp/gobuild
|
||||||
|
RUN mkdir ./binaries
|
||||||
|
|
||||||
|
DO +RUST_TO_GO_ARCH_STRING --rustTarget="${target}"
|
||||||
|
RUN echo "GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM} GO_ARCH_STRING=${GO_ARCH_STRING}"
|
||||||
|
|
||||||
|
# Our tauri app has externalBins configured so tauri will try to embed them when it finished compiling
|
||||||
|
# the app. Make sure we copy portmaster-start and portmaster-core in all architectures supported.
|
||||||
|
# See documentation for externalBins for more information on how tauri searches for the binaries.
|
||||||
|
COPY (+go-build/output --CMDS="portmaster-start portmaster-core" --GOOS="${GOOS}" --GOARCH="${GOARCH}" --GOARM="${GOARM}") /tmp/gobuild
|
||||||
|
|
||||||
|
# Place them in the correct folder with the rust target tripple attached.
|
||||||
|
FOR bin IN $(ls /tmp/gobuild)
|
||||||
|
# ${bin$.*} does not work in SET commands unfortunately so we use a shell
|
||||||
|
# snippet here:
|
||||||
|
RUN set -e ; \
|
||||||
|
dest="./binaries/${bin}-${target}" ; \
|
||||||
|
if [ -z "${bin##*.exe}" ]; then \
|
||||||
|
dest="./binaries/${bin%.*}-${target}.exe" ; \
|
||||||
|
fi ; \
|
||||||
|
cp "/tmp/gobuild/${bin}" "${dest}" ;
|
||||||
|
END
|
||||||
|
DO rust+SET_CACHE_MOUNTS_ENV
|
||||||
|
RUN cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
kext-build:
|
kext-build:
|
||||||
FROM ${rust_builder_image}
|
FROM ${rust_builder_image}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ license = ""
|
||||||
repository = ""
|
repository = ""
|
||||||
default-run = "portmaster"
|
default-run = "portmaster"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.60"
|
rust-version = "1.64"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -80,3 +80,6 @@ ctor = "0.2.6"
|
||||||
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
|
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
|
||||||
# DO NOT REMOVE!!
|
# DO NOT REMOVE!!
|
||||||
custom-protocol = [ "tauri/custom-protocol" ]
|
custom-protocol = [ "tauri/custom-protocol" ]
|
||||||
|
|
||||||
|
[package.metadata.clippy]
|
||||||
|
allow = ["clippy::collapsible_else_if"]
|
|
@ -48,7 +48,7 @@ impl portmaster::Handler for WsHandler {
|
||||||
"main-handler".to_string()
|
"main-handler".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_connect(&mut self, cli: portapi::client::PortAPI) -> () {
|
fn on_connect(&mut self, cli: portapi::client::PortAPI) {
|
||||||
info!("connection established, creating main window");
|
info!("connection established, creating main window");
|
||||||
|
|
||||||
// we successfully connected to Portmaster. Set is_first_connect to false
|
// we successfully connected to Portmaster. Set is_first_connect to false
|
||||||
|
@ -116,11 +116,11 @@ fn show_webview_not_installed_dialog() -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALLBACK_TO_OLD_UI_EXIT_CODE;
|
FALLBACK_TO_OLD_UI_EXIT_CODE
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(_) = tauri::webview_version() {
|
if tauri::webview_version().is_err() {
|
||||||
std::process::exit(show_webview_not_installed_dialog());
|
std::process::exit(show_webview_not_installed_dialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,8 +251,8 @@ fn main() {
|
||||||
.build(tauri::generate_context!())
|
.build(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|
||||||
app.run(|handle, e| match e {
|
app.run(|handle, e| {
|
||||||
RunEvent::WindowEvent { label, event, .. } => {
|
if let RunEvent::WindowEvent { label, event, .. } = e {
|
||||||
if label != "main" {
|
if label != "main" {
|
||||||
// We only have one window at most so any other label is unexpected
|
// We only have one window at most so any other label is unexpected
|
||||||
return;
|
return;
|
||||||
|
@ -266,9 +266,7 @@ fn main() {
|
||||||
//
|
//
|
||||||
// Note: the above javascript does NOT trigger the CloseRequested event so
|
// Note: the above javascript does NOT trigger the CloseRequested event so
|
||||||
// there's no need to handle that case here.
|
// there's no need to handle that case here.
|
||||||
//
|
if let WindowEvent::CloseRequested { api, .. } = event {
|
||||||
match event {
|
|
||||||
WindowEvent::CloseRequested { api, .. } => {
|
|
||||||
debug!(
|
debug!(
|
||||||
"window (label={}) close request received, forwarding to user-interface.",
|
"window (label={}) close request received, forwarding to user-interface.",
|
||||||
label
|
label
|
||||||
|
@ -284,14 +282,6 @@ fn main() {
|
||||||
error!("window was None");
|
error!("window was None");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(vladimir): why was this needed?
|
|
||||||
// RunEvent::ExitRequested { api, .. } => {
|
|
||||||
// api.prevent_exit();
|
|
||||||
// }
|
|
||||||
_ => {}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ pub enum MessageError {
|
||||||
InvalidPayload(#[from] serde_json::Error),
|
InvalidPayload(#[from] serde_json::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Payload defines the payload type and content of a PortAPI message.
|
/// Payload defines the payload type and content of a PortAPI message.
|
||||||
///
|
///
|
||||||
/// For the time being, only JSON payloads (indicated by a prefixed 'J' of the payload content)
|
/// For the time being, only JSON payloads (indicated by a prefixed 'J' of the payload content)
|
||||||
|
@ -36,32 +35,31 @@ pub enum MessageError {
|
||||||
/// appropriate decoding from the `Payload::UNKNOWN` variant.
|
/// appropriate decoding from the `Payload::UNKNOWN` variant.
|
||||||
#[derive(PartialEq, Debug, Clone)]
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
pub enum Payload {
|
pub enum Payload {
|
||||||
JSON(String),
|
Json(String),
|
||||||
UNKNOWN(String),
|
Unknown(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ParseError is returned from `Payload::parse()`.
|
/// ParseError is returned from `Payload::parse()`.
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
JSON(#[from] serde_json::Error),
|
Json(#[from] serde_json::Error),
|
||||||
|
|
||||||
#[error("unknown error while parsing")]
|
#[error("unknown error while parsing")]
|
||||||
UNKNOWN
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Payload {
|
impl Payload {
|
||||||
/// Parse the payload into T.
|
/// Parse the payload into T.
|
||||||
///
|
///
|
||||||
/// Only JSON parsing is supported for now. See [Payload] for more information.
|
/// Only JSON parsing is supported for now. See [Payload] for more information.
|
||||||
pub fn parse<'a, T>(self: &'a Self) -> std::result::Result<T, ParseError>
|
pub fn parse<'a, T>(&'a self) -> std::result::Result<T, ParseError>
|
||||||
where
|
where
|
||||||
T: serde::de::Deserialize<'a> {
|
T: serde::de::Deserialize<'a>,
|
||||||
|
{
|
||||||
match self {
|
match self {
|
||||||
Payload::JSON(blob) => Ok(serde_json::from_str::<T>(blob.as_str())?),
|
Payload::Json(blob) => Ok(serde_json::from_str::<T>(blob.as_str())?),
|
||||||
Payload::UNKNOWN(_) => Err(ParseError::UNKNOWN),
|
Payload::Unknown(_) => Err(ParseError::Unknown),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,10 +75,10 @@ impl std::convert::From<String> for Payload {
|
||||||
|
|
||||||
match first {
|
match first {
|
||||||
Some(c) => match c {
|
Some(c) => match c {
|
||||||
'J' => Payload::JSON(rest),
|
'J' => Payload::Json(rest),
|
||||||
_ => Payload::UNKNOWN(value),
|
_ => Payload::Unknown(value),
|
||||||
},
|
},
|
||||||
None => Payload::UNKNOWN("".to_string())
|
None => Payload::Unknown("".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,10 +87,10 @@ impl std::convert::From<String> for Payload {
|
||||||
impl std::fmt::Display for Payload {
|
impl std::fmt::Display for Payload {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Payload::JSON(payload) => {
|
Payload::Json(payload) => {
|
||||||
write!(f, "J{}", payload)
|
write!(f, "J{}", payload)
|
||||||
},
|
}
|
||||||
Payload::UNKNOWN(payload) => {
|
Payload::Unknown(payload) => {
|
||||||
write!(f, "{}", payload)
|
write!(f, "{}", payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,19 +117,19 @@ pub struct Message {
|
||||||
/// Note that this conversion does not check for invalid messages!
|
/// Note that this conversion does not check for invalid messages!
|
||||||
impl std::convert::From<Message> for String {
|
impl std::convert::From<Message> for String {
|
||||||
fn from(value: Message) -> Self {
|
fn from(value: Message) -> Self {
|
||||||
let mut result = "".to_owned();
|
let mut result = String::new();
|
||||||
|
|
||||||
result.push_str(value.id.to_string().as_str());
|
result.push_str(value.id.to_string().as_str());
|
||||||
result.push_str("|");
|
result.push('|');
|
||||||
result.push_str(&value.cmd);
|
result.push_str(&value.cmd);
|
||||||
|
|
||||||
if let Some(key) = value.key {
|
if let Some(key) = value.key {
|
||||||
result.push_str("|");
|
result.push('|');
|
||||||
result.push_str(key.as_str());
|
result.push_str(key.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(payload) = value.payload {
|
if let Some(payload) = value.payload {
|
||||||
result.push_str("|");
|
result.push('|');
|
||||||
result.push_str(payload.to_string().as_str())
|
result.push_str(payload.to_string().as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,9 +145,9 @@ impl std::str::FromStr for Message {
|
||||||
type Err = MessageError;
|
type Err = MessageError;
|
||||||
|
|
||||||
fn from_str(line: &str) -> Result<Self, Self::Err> {
|
fn from_str(line: &str) -> Result<Self, Self::Err> {
|
||||||
let parts = line.split("|").collect::<Vec<&str>>();
|
let parts = line.split('|').collect::<Vec<&str>>();
|
||||||
|
|
||||||
let id = match parts.get(0) {
|
let id = match parts.first() {
|
||||||
Some(s) => match (*s).parse::<usize>() {
|
Some(s) => match (*s).parse::<usize>() {
|
||||||
Ok(id) => Ok(id),
|
Ok(id) => Ok(id),
|
||||||
Err(_) => Err(MessageError::InvalidID),
|
Err(_) => Err(MessageError::InvalidID),
|
||||||
|
@ -163,18 +161,15 @@ impl std::str::FromStr for Message {
|
||||||
}?
|
}?
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let key = parts.get(2)
|
let key = parts.get(2).map(|key| key.to_string());
|
||||||
.and_then(|key| Some(key.to_string()));
|
let payload: Option<Payload> = parts.get(3).map(|p| p.to_string().into());
|
||||||
|
|
||||||
let payload : Option<Payload> = parts.get(3)
|
Ok(Message {
|
||||||
.and_then(|p| Some(p.to_string().into()));
|
|
||||||
|
|
||||||
return Ok(Message {
|
|
||||||
id,
|
id,
|
||||||
cmd,
|
cmd,
|
||||||
key,
|
key,
|
||||||
payload: payload
|
payload,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,67 +186,79 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn payload_to_string() {
|
fn payload_to_string() {
|
||||||
let p = Payload::JSON("{}".to_string());
|
let p = Payload::Json("{}".to_string());
|
||||||
assert_eq!(p.to_string(), "J{}");
|
assert_eq!(p.to_string(), "J{}");
|
||||||
|
|
||||||
let p = Payload::UNKNOWN("some unknown content".to_string());
|
let p = Payload::Unknown("some unknown content".to_string());
|
||||||
assert_eq!(p.to_string(), "some unknown content");
|
assert_eq!(p.to_string(), "some unknown content");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn payload_from_string() {
|
fn payload_from_string() {
|
||||||
let p: Payload = "J{}".to_string().into();
|
let p: Payload = "J{}".to_string().into();
|
||||||
assert_eq!(p, Payload::JSON("{}".to_string()));
|
assert_eq!(p, Payload::Json("{}".to_string()));
|
||||||
|
|
||||||
let p: Payload = "some unknown content".to_string().into();
|
let p: Payload = "some unknown content".to_string().into();
|
||||||
assert_eq!(p, Payload::UNKNOWN("some unknown content".to_string()));
|
assert_eq!(p, Payload::Unknown("some unknown content".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn payload_parse() {
|
fn payload_parse() {
|
||||||
let p: Payload = "J{\"a\": 100, \"s\": \"string\"}".to_string().into();
|
let p: Payload = "J{\"a\": 100, \"s\": \"string\"}".to_string().into();
|
||||||
|
|
||||||
let t: Test = p.parse()
|
let t: Test = p.parse().expect("Expected payload parsing to work");
|
||||||
.expect("Expected payload parsing to work");
|
|
||||||
|
|
||||||
assert_eq!(t, Test{
|
assert_eq!(
|
||||||
|
t,
|
||||||
|
Test {
|
||||||
a: 100,
|
a: 100,
|
||||||
s: "string".to_string(),
|
s: "string".to_string(),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_message() {
|
fn parse_message() {
|
||||||
let m = "10|insert|some:key|J{}".parse::<Message>()
|
let m = "10|insert|some:key|J{}"
|
||||||
|
.parse::<Message>()
|
||||||
.expect("Expected message to parse");
|
.expect("Expected message to parse");
|
||||||
|
|
||||||
assert_eq!(m, Message{
|
assert_eq!(
|
||||||
|
m,
|
||||||
|
Message {
|
||||||
id: 10,
|
id: 10,
|
||||||
cmd: "insert".to_string(),
|
cmd: "insert".to_string(),
|
||||||
key: Some("some:key".to_string()),
|
key: Some("some:key".to_string()),
|
||||||
payload: Some(Payload::JSON("{}".to_string())),
|
payload: Some(Payload::Json("{}".to_string())),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let m = "1|done".parse::<Message>()
|
let m = "1|done"
|
||||||
|
.parse::<Message>()
|
||||||
.expect("Expected message to parse");
|
.expect("Expected message to parse");
|
||||||
|
|
||||||
assert_eq!(m, Message{
|
assert_eq!(
|
||||||
|
m,
|
||||||
|
Message {
|
||||||
id: 1,
|
id: 1,
|
||||||
cmd: "done".to_string(),
|
cmd: "done".to_string(),
|
||||||
key: None,
|
key: None,
|
||||||
payload: None
|
payload: None
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let m = "".parse::<Message>()
|
let m = "".parse::<Message>().expect_err("Expected parsing to fail");
|
||||||
.expect_err("Expected parsing to fail");
|
if let MessageError::InvalidID = m {
|
||||||
if let MessageError::InvalidID = m {} else {
|
} else {
|
||||||
panic!("unexpected error value: {}", m)
|
panic!("unexpected error value: {}", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = "1".parse::<Message>()
|
let m = "1"
|
||||||
|
.parse::<Message>()
|
||||||
.expect_err("Expected parsing to fail");
|
.expect_err("Expected parsing to fail");
|
||||||
|
|
||||||
if let MessageError::MissingCommand = m {} else {
|
if let MessageError::MissingCommand = m {
|
||||||
|
} else {
|
||||||
panic!("unexpected error value: {}", m)
|
panic!("unexpected error value: {}", m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use serde::*;
|
|
||||||
use super::super::message::Payload;
|
use super::super::message::Payload;
|
||||||
|
use serde::*;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub struct BooleanValue {
|
pub struct BooleanValue {
|
||||||
|
@ -13,6 +13,6 @@ impl TryInto<Payload> for BooleanValue {
|
||||||
fn try_into(self) -> Result<Payload, Self::Error> {
|
fn try_into(self) -> Result<Payload, Self::Error> {
|
||||||
let str = serde_json::to_string(&self)?;
|
let str = serde_json::to_string(&self)?;
|
||||||
|
|
||||||
Ok(Payload::JSON(str))
|
Ok(Payload::Json(str))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -86,7 +86,7 @@ pub fn get_app_info<R: Runtime>(
|
||||||
matching_path,
|
matching_path,
|
||||||
};
|
};
|
||||||
|
|
||||||
if id == "" {
|
if id.is_empty() {
|
||||||
id = uuid::Uuid::new_v4().to_string()
|
id = uuid::Uuid::new_v4().to_string()
|
||||||
}
|
}
|
||||||
let cloned = id.clone();
|
let cloned = id.clone();
|
||||||
|
@ -137,7 +137,7 @@ pub fn get_app_info<R: Runtime>(
|
||||||
pub fn get_service_manager_status<R: Runtime>(window: Window<R>, response_id: String) -> Result {
|
pub fn get_service_manager_status<R: Runtime>(window: Window<R>, response_id: String) -> Result {
|
||||||
let mut id = response_id;
|
let mut id = response_id;
|
||||||
|
|
||||||
if id == "" {
|
if id.is_empty() {
|
||||||
id = uuid::Uuid::new_v4().to_string();
|
id = uuid::Uuid::new_v4().to_string();
|
||||||
}
|
}
|
||||||
let cloned = id.clone();
|
let cloned = id.clone();
|
||||||
|
@ -161,7 +161,7 @@ pub fn get_service_manager_status<R: Runtime>(window: Window<R>, response_id: St
|
||||||
pub fn start_service<R: Runtime>(window: Window<R>, response_id: String) -> Result {
|
pub fn start_service<R: Runtime>(window: Window<R>, response_id: String) -> Result {
|
||||||
let mut id = response_id;
|
let mut id = response_id;
|
||||||
|
|
||||||
if id == "" {
|
if id.is_empty() {
|
||||||
id = uuid::Uuid::new_v4().to_string();
|
id = uuid::Uuid::new_v4().to_string();
|
||||||
}
|
}
|
||||||
let cloned = id.clone();
|
let cloned = id.clone();
|
||||||
|
|
|
@ -32,14 +32,13 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use serde;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tauri::{AppHandle, Emitter, Manager, Runtime};
|
use tauri::{AppHandle, Emitter, Manager, Runtime};
|
||||||
|
|
||||||
const PORTMASTER_BASE_URL: &'static str = "http://127.0.0.1:817/api/v1/";
|
const PORTMASTER_BASE_URL: &str = "http://127.0.0.1:817/api/v1/";
|
||||||
|
|
||||||
pub trait Handler {
|
pub trait Handler {
|
||||||
fn on_connect(&mut self, cli: PortAPI) -> ();
|
fn on_connect(&mut self, cli: PortAPI);
|
||||||
fn on_disconnect(&mut self);
|
fn on_disconnect(&mut self);
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
}
|
}
|
||||||
|
@ -81,10 +80,7 @@ impl<R: Runtime> PortmasterInterface<R> {
|
||||||
let map = self.state.lock();
|
let map = self.state.lock();
|
||||||
|
|
||||||
if let Ok(map) = map {
|
if let Ok(map) = map {
|
||||||
match map.get(&key) {
|
map.get(&key).cloned()
|
||||||
Some(value) => Some(value.clone()),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -129,11 +125,8 @@ impl<R: Runtime> PortmasterInterface<R> {
|
||||||
|
|
||||||
/// Returns the current portapi client.
|
/// Returns the current portapi client.
|
||||||
pub fn get_api(&self) -> Option<PortAPI> {
|
pub fn get_api(&self) -> Option<PortAPI> {
|
||||||
if let Ok(mut api) = self.api.lock() {
|
if let Ok(api) = self.api.lock() {
|
||||||
match &mut *api {
|
(*api).clone()
|
||||||
Some(api) => Some(api.clone()),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,13 @@ pub async fn notification_handler(cli: PortAPI) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if this action has already been acted on
|
// Skip if this action has already been acted on
|
||||||
if n.selected_action_id != "" {
|
if n.selected_action_id.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
show_notification(&cli, key, n).await;
|
show_notification(&cli, key, n).await;
|
||||||
}
|
}
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
ParseError::JSON(err) => {
|
ParseError::Json(err) => {
|
||||||
error!("failed to parse notification: {}", err);
|
error!("failed to parse notification: {}", err);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -81,7 +81,7 @@ pub async fn show_notification(cli: &PortAPI, key: String, n: Notification) {
|
||||||
let _ = cli_clone
|
let _ = cli_clone
|
||||||
.request(Request::Update(
|
.request(Request::Update(
|
||||||
key,
|
key,
|
||||||
Payload::JSON(
|
Payload::Json(
|
||||||
json!({
|
json!({
|
||||||
"SelectedActionID": value
|
"SelectedActionID": value
|
||||||
})
|
})
|
||||||
|
@ -125,7 +125,7 @@ pub async fn show_notification(cli: &PortAPI, key: String, n: Notification) {
|
||||||
let _ = cli
|
let _ = cli
|
||||||
.request(Request::Update(
|
.request(Request::Update(
|
||||||
key,
|
key,
|
||||||
Payload::JSON(
|
Payload::Json(
|
||||||
json!({
|
json!({
|
||||||
"SelectedActionID": value
|
"SelectedActionID": value
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl From<std::process::Output> for ServiceManagerError {
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|s| !s.trim().is_empty())
|
.filter(|s| !s.trim().is_empty())
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| format!("Failed to run `systemctl`"));
|
.unwrap_or_else(|| "Failed to run `systemctl`".to_string());
|
||||||
|
|
||||||
ServiceManagerError::Other(output.status, msg)
|
ServiceManagerError::Other(output.status, msg)
|
||||||
}
|
}
|
||||||
|
@ -231,11 +231,11 @@ fn trim_newline(s: &mut String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sudo_cmd() -> std::result::Result<SudoCommand, std::io::Error> {
|
fn get_sudo_cmd() -> std::result::Result<SudoCommand, std::io::Error> {
|
||||||
if let Ok(_) = fs::metadata("/usr/bin/pkexec") {
|
if fs::metadata("/usr/bin/pkexec").is_ok() {
|
||||||
return Ok(SudoCommand::Pkexec);
|
return Ok(SudoCommand::Pkexec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(_) = fs::metadata("/usr/bin/gksudo") {
|
if fs::metadata("/usr/bin/gksudo").is_ok() {
|
||||||
return Ok(SudoCommand::Gksu);
|
return Ok(SudoCommand::Gksu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,13 +46,11 @@ enum IconColor {
|
||||||
static CURRENT_ICON_COLOR: RwLock<IconColor> = RwLock::new(IconColor::Red);
|
static CURRENT_ICON_COLOR: RwLock<IconColor> = RwLock::new(IconColor::Red);
|
||||||
pub static USER_THEME: RwLock<dark_light::Mode> = RwLock::new(dark_light::Mode::Default);
|
pub static USER_THEME: RwLock<dark_light::Mode> = RwLock::new(dark_light::Mode::Default);
|
||||||
|
|
||||||
lazy_static! {
|
static SPN_STATUS: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
||||||
static ref SPN_STATUS: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
static SPN_BUTTON: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
||||||
static ref SPN_BUTTON: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
static GLOBAL_STATUS: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
||||||
static ref GLOBAL_STATUS: Mutex<Option<MenuItem<Wry>>> = Mutex::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
const PM_TRAY_ICON_ID: &'static str = "pm_icon";
|
const PM_TRAY_ICON_ID: &str = "pm_icon";
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
|
|
||||||
|
@ -64,9 +62,9 @@ fn get_theme_mode() -> dark_light::Mode {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_green_icon() -> &'static [u8] {
|
fn get_green_icon() -> &'static [u8] {
|
||||||
const LIGHT_GREEN_ICON: &'static [u8] =
|
const LIGHT_GREEN_ICON: &[u8] =
|
||||||
include_bytes!("../../../../assets/data/icons/pm_light_green_64.png");
|
include_bytes!("../../../../assets/data/icons/pm_light_green_64.png");
|
||||||
const DARK_GREEN_ICON: &'static [u8] =
|
const DARK_GREEN_ICON: &[u8] =
|
||||||
include_bytes!("../../../../assets/data/icons/pm_dark_green_64.png");
|
include_bytes!("../../../../assets/data/icons/pm_dark_green_64.png");
|
||||||
|
|
||||||
match get_theme_mode() {
|
match get_theme_mode() {
|
||||||
|
@ -76,9 +74,9 @@ fn get_green_icon() -> &'static [u8] {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_blue_icon() -> &'static [u8] {
|
fn get_blue_icon() -> &'static [u8] {
|
||||||
const LIGHT_BLUE_ICON: &'static [u8] =
|
const LIGHT_BLUE_ICON: &[u8] =
|
||||||
include_bytes!("../../../../assets/data/icons/pm_light_blue_64.png");
|
include_bytes!("../../../../assets/data/icons/pm_light_blue_64.png");
|
||||||
const DARK_BLUE_ICON: &'static [u8] =
|
const DARK_BLUE_ICON: &[u8] =
|
||||||
include_bytes!("../../../../assets/data/icons/pm_dark_blue_64.png");
|
include_bytes!("../../../../assets/data/icons/pm_dark_blue_64.png");
|
||||||
match get_theme_mode() {
|
match get_theme_mode() {
|
||||||
dark_light::Mode::Light => DARK_BLUE_ICON,
|
dark_light::Mode::Light => DARK_BLUE_ICON,
|
||||||
|
@ -87,20 +85,27 @@ fn get_blue_icon() -> &'static [u8] {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_red_icon() -> &'static [u8] {
|
fn get_red_icon() -> &'static [u8] {
|
||||||
const LIGHT_RED_ICON: &'static [u8] =
|
const LIGHT_RED_ICON: &[u8] =
|
||||||
include_bytes!("../../../../assets/data/icons/pm_light_red_64.png");
|
include_bytes!("../../../../assets/data/icons/pm_light_red_64.png");
|
||||||
const DARK_RED_ICON: &'static [u8] =
|
const DARK_RED_ICON: &'static [u8] =
|
||||||
include_bytes!("../../../../assets/data/icons/pm_dark_red_64.png");
|
include_bytes!("../../../../assets/data/icons/pm_dark_red_64.png");
|
||||||
match get_theme_mode() {
|
match get_theme_mode() {
|
||||||
|
const DARK_RED_ICON: &'static [u8] =
|
||||||
|
include_bytes!("../../../../assets/data/icons/pm_dark_red_64.png");
|
||||||
|
let mode = dark_light::detect();
|
||||||
|
match mode {
|
||||||
|
const DARK_RED_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_red_64.png");
|
||||||
|
let mode = dark_light::detect();
|
||||||
|
match mode {
|
||||||
dark_light::Mode::Light => DARK_RED_ICON,
|
dark_light::Mode::Light => DARK_RED_ICON,
|
||||||
_ => LIGHT_RED_ICON,
|
_ => LIGHT_RED_ICON,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_yellow_icon() -> &'static [u8] {
|
fn get_yellow_icon() -> &'static [u8] {
|
||||||
const LIGHT_YELLOW_ICON: &'static [u8] =
|
const LIGHT_YELLOW_ICON: &[u8] =
|
||||||
include_bytes!("../../../../assets/data/icons/pm_light_yellow_64.png");
|
include_bytes!("../../../../assets/data/icons/pm_light_yellow_64.png");
|
||||||
const DARK_YELLOW_ICON: &'static [u8] =
|
const DARK_YELLOW_ICON: &[u8] =
|
||||||
include_bytes!("../../../../assets/data/icons/pm_dark_yellow_64.png");
|
include_bytes!("../../../../assets/data/icons/pm_dark_yellow_64.png");
|
||||||
match get_theme_mode() {
|
match get_theme_mode() {
|
||||||
dark_light::Mode::Light => DARK_YELLOW_ICON,
|
dark_light::Mode::Light => DARK_YELLOW_ICON,
|
||||||
|
@ -257,32 +262,30 @@ pub fn setup_tray_menu(
|
||||||
button_state,
|
button_state,
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
if let MouseButton::Left = button {
|
if let (MouseButton::Left, MouseButtonState::Down) = (button, button_state) {
|
||||||
if let MouseButtonState::Down = button_state {
|
|
||||||
let _ = open_window(tray.app_handle());
|
let _ = open_window(tray.app_handle());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.build(app)?;
|
.build(app)?;
|
||||||
Ok(icon)
|
Ok(icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_icon(icon: AppIcon, subsystems: HashMap<String, Subsystem>, spn_status: String) {
|
pub fn update_icon(icon: AppIcon, subsystems: HashMap<String, Subsystem>, spn_status: String) {
|
||||||
// iterate over the subsytems and check if there's a module failure
|
// iterate over the subsystems and check if there's a module failure
|
||||||
let failure = subsystems
|
let failure = subsystems.values().map(|s| &s.module_status).fold(
|
||||||
.values()
|
(subsystem::FAILURE_NONE, "".to_string()),
|
||||||
.into_iter()
|
|mut acc, s| {
|
||||||
.map(|s| &s.module_status)
|
|
||||||
.fold((subsystem::FAILURE_NONE, "".to_string()), |mut acc, s| {
|
|
||||||
for m in s {
|
for m in s {
|
||||||
if m.failure_status > acc.0 {
|
if m.failure_status > acc.0 {
|
||||||
acc = (m.failure_status, m.failure_msg.clone())
|
acc = (m.failure_status, m.failure_msg.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acc
|
acc
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
#[allow(clippy::collapsible_else_if)]
|
||||||
if failure.0 == subsystem::FAILURE_NONE {
|
if failure.0 == subsystem::FAILURE_NONE {
|
||||||
if let Some(global_status) = &mut *(GLOBAL_STATUS.lock().unwrap()) {
|
if let Some(global_status) = &mut *(GLOBAL_STATUS.lock().unwrap()) {
|
||||||
_ = global_status.set_text("Status: Secured");
|
_ = global_status.set_text("Status: Secured");
|
||||||
|
@ -405,7 +408,7 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
update_icon(icon.clone(), subsystems.clone(), spn_status.clone());
|
update_icon(icon.clone(), subsystems.clone(), spn_status.clone());
|
||||||
},
|
},
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
ParseError::JSON(err) => {
|
ParseError::Json(err) => {
|
||||||
error!("failed to parse subsystem: {}", err);
|
error!("failed to parse subsystem: {}", err);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -437,7 +440,7 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
update_icon(icon.clone(), subsystems.clone(), spn_status.clone());
|
update_icon(icon.clone(), subsystems.clone(), spn_status.clone());
|
||||||
},
|
},
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
ParseError::JSON(err) => {
|
ParseError::Json(err) => {
|
||||||
error!("failed to parse spn status value: {}", err)
|
error!("failed to parse spn status value: {}", err)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -466,7 +469,7 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
update_spn_ui_state(value.value.unwrap_or(false));
|
update_spn_ui_state(value.value.unwrap_or(false));
|
||||||
},
|
},
|
||||||
Err(err) => match err {
|
Err(err) => match err {
|
||||||
ParseError::JSON(err) => {
|
ParseError::Json(err) => {
|
||||||
error!("failed to parse config value: {}", err)
|
error!("failed to parse config value: {}", err)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -6,9 +6,8 @@ use tauri::{
|
||||||
|
|
||||||
use crate::{portmaster::PortmasterExt, traymenu};
|
use crate::{portmaster::PortmasterExt, traymenu};
|
||||||
|
|
||||||
const LIGHT_PM_ICON: &'static [u8] =
|
const LIGHT_PM_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_light_512.png");
|
||||||
include_bytes!("../../../../assets/data/icons/pm_light_512.png");
|
const DARK_PM_ICON: &[u8] = include_bytes!("../../../../assets/data/icons/pm_dark_512.png");
|
||||||
const DARK_PM_ICON: &'static [u8] = include_bytes!("../../../../assets/data/icons/pm_dark_512.png");
|
|
||||||
|
|
||||||
/// Either returns the existing "main" window or creates a new one.
|
/// Either returns the existing "main" window or creates a new one.
|
||||||
///
|
///
|
||||||
|
@ -54,7 +53,7 @@ pub fn create_main_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||||
set_window_icon(&window);
|
set_window_icon(&window);
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if let Ok(_) = std::env::var("TAURI_SHOW_IMMEDIATELY") {
|
if std::env::var("TAURI_SHOW_IMMEDIATELY").is_ok() {
|
||||||
debug!("[tauri] TAURI_SHOW_IMMEDIATELY is set, opening window");
|
debug!("[tauri] TAURI_SHOW_IMMEDIATELY is set, opening window");
|
||||||
|
|
||||||
if let Err(err) = window.show() {
|
if let Err(err) = window.show() {
|
||||||
|
@ -92,7 +91,7 @@ pub fn close_splash_window(app: &AppHandle) -> Result<()> {
|
||||||
let _ = window.hide();
|
let _ = window.hide();
|
||||||
return window.destroy();
|
return window.destroy();
|
||||||
}
|
}
|
||||||
return Err(tauri::Error::WindowNotFound);
|
Err(tauri::Error::WindowNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide_splash_window(app: &AppHandle) -> Result<()> {
|
pub fn hide_splash_window(app: &AppHandle) -> Result<()> {
|
||||||
|
|
|
@ -18,7 +18,6 @@ use std::{
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use dirs;
|
|
||||||
use ini::{Ini, ParseOption};
|
use ini::{Ini, ParseOption};
|
||||||
|
|
||||||
static mut GTK_DEFAULT_THEME: Option<*mut GtkIconTheme> = None;
|
static mut GTK_DEFAULT_THEME: Option<*mut GtkIconTheme> = None;
|
||||||
|
@ -146,7 +145,7 @@ pub fn get_app_info(process_info: ProcessInfo) -> Result<AppInfo> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(process_info.exec_path, None);
|
.insert(process_info.exec_path, None);
|
||||||
|
|
||||||
Err(Error::new(ErrorKind::NotFound, format!("failed to find app info")).into())
|
Err(Error::new(ErrorKind::NotFound, "failed to find app info".to_string()).into())
|
||||||
} else {
|
} else {
|
||||||
// sort matches by length
|
// sort matches by length
|
||||||
matches.sort_by(|a, b| a.1.cmp(&b.1));
|
matches.sort_by(|a, b| a.1.cmp(&b.1));
|
||||||
|
@ -178,7 +177,7 @@ pub fn get_app_info(process_info: ProcessInfo) -> Result<AppInfo> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::new(ErrorKind::NotFound, format!("failed to find app info")).into())
|
Err(Error::new(ErrorKind::NotFound, "failed to find app info".to_string()).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +335,7 @@ fn try_get_app_info(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.len() > 0 {
|
if !result.is_empty() {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::new(ErrorKind::NotFound, "no matching .desktop files found").into())
|
Err(Error::new(ErrorKind::NotFound, "no matching .desktop files found").into())
|
||||||
|
@ -393,7 +392,7 @@ fn get_icon_as_png_dataurl(name: &str, size: i8) -> Result<(String, String)> {
|
||||||
// - network
|
// - network
|
||||||
//
|
//
|
||||||
name_without_ext
|
name_without_ext
|
||||||
.split("-")
|
.split('-')
|
||||||
.for_each(|part| icons.push(part));
|
.for_each(|part| icons.push(part));
|
||||||
|
|
||||||
for name in icons {
|
for name in icons {
|
||||||
|
@ -554,15 +553,7 @@ mod tests {
|
||||||
matching_path: bin.clone(),
|
matching_path: bin.clone(),
|
||||||
pid: 0,
|
pid: 0,
|
||||||
})
|
})
|
||||||
.expect(
|
.unwrap_or_else(|_| panic!("expected to find app info for {} ({})", bin, cmd));
|
||||||
format!(
|
|
||||||
"expected to find app info for {} ({})",
|
|
||||||
bin,
|
|
||||||
cmd.to_string()
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let empty_string = String::from("");
|
let empty_string = String::from("");
|
||||||
|
|
||||||
// just make sure all fields are populated
|
// just make sure all fields are populated
|
||||||
|
|
Loading…
Add table
Reference in a new issue