Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document Emitter trait method panics #12444

Merged
merged 29 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c15b285
refactor: move fn assert_event_name_is_valid() to the only module usi…
sftse Jan 13, 2025
93e27d7
fix: use macro so that panic points to the precise source location
sftse Jan 13, 2025
76236b3
refactor: default trait methods on Emitter trait to reduce boilerplate
sftse Jan 19, 2025
13a13cb
docs: document requirements for event names
sftse Jan 13, 2025
1153ff6
refactor: move panic closer to user-facing code
sftse Jan 19, 2025
b26986f
add .changes file
sftse Jan 20, 2025
a86fcc8
chore: cleanup imports
sftse Jan 21, 2025
d10ae60
feat: extend Error enum by new IllegalEventName variant
sftse Jan 22, 2025
8e9b31e
refactor: make EventName take a generic
sftse Jan 21, 2025
ed80b00
refactor: replace assert with unwrapping checked constructor
sftse Jan 21, 2025
9129ce8
refactor: make EmitArgs constructor take EventName
sftse Jan 21, 2025
82b0fb8
refactor: delete superfluous function argument
sftse Jan 21, 2025
e2bfa93
refactor: finish threading EventName through function calls
sftse Jan 21, 2025
1b26b87
refactor: functions need not be pub
sftse Jan 21, 2025
4f057d1
refactor: move EventName checks as far up the callchain as possible
sftse Jan 21, 2025
246ff4e
refactor: move EventName to the event module
sftse Jan 22, 2025
92d4830
refactor: expand functions covered by EventName to fn emit_to_webview()
sftse Jan 22, 2025
e84c81e
refactor: replace Deref use with direct method
sftse Jan 22, 2025
62a2262
refactor: pull event name out of EmitArgs
sftse Jan 22, 2025
fb6f2df
fixup! refactor: move EventName to the event module
sftse Jan 23, 2025
5760c3b
refactor: remove unnecessary serialization call
sftse Jan 23, 2025
d36be9f
refactor: do not need Clone bounds and ownership of payload to serialize
sftse Jan 23, 2025
0b9147a
refactor: remove unnecessary to_string() before serializing
sftse Jan 23, 2025
389c129
refactor: make fields private
sftse Jan 23, 2025
bdb733a
refactor: EmitArgs should keep EventName invariants
sftse Jan 23, 2025
7294a93
remove unnecessary arg in internal emit/emit_filter
amrbashir Jan 24, 2025
d32da91
Resue EventName in internal listeners instead of EventName type alias…
amrbashir Jan 24, 2025
94343f9
unneeded change file
amrbashir Jan 24, 2025
5126966
licenses header
amrbashir Jan 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions crates/tauri-cli/src/helpers/flock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,15 +327,11 @@ mod sys {
}

pub(super) fn error_contended(err: &Error) -> bool {
err
.raw_os_error()
.map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32)
err.raw_os_error() == Some(ERROR_LOCK_VIOLATION as i32)
}

pub(super) fn error_unsupported(err: &Error) -> bool {
err
.raw_os_error()
.map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32)
err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32)
}

pub(super) fn unlock(file: &File) -> Result<()> {
Expand Down
83 changes: 6 additions & 77 deletions crates/tauri/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
sealed::{ManagerBase, RuntimeOrDispatch},
utils::{config::Config, Env},
webview::PageLoadPayload,
Context, DeviceEventFilter, Emitter, EventLoopMessage, Listener, Manager, Monitor, Result,
Context, DeviceEventFilter, Emitter, EventLoopMessage, EventName, Listener, Manager, Monitor,
Runtime, Scopes, StateManager, Theme, Webview, WebviewWindowBuilder, Window,
};

Expand All @@ -38,7 +38,6 @@ use tauri_runtime::{
};
use tauri_utils::{assets::AssetsIter, PackageInfo};

use serde::Serialize;
use std::{
borrow::Cow,
collections::HashMap,
Expand Down Expand Up @@ -929,7 +928,8 @@ macro_rules! shared_app_impl {
where
F: Fn(Event) + Send + 'static,
{
self.manager.listen(event.into(), EventTarget::App, handler)
let event = EventName::new(event.into()).unwrap();
self.manager.listen(event, EventTarget::App, handler)
}

/// Listen to an event on this app only once.
Expand All @@ -939,7 +939,8 @@ macro_rules! shared_app_impl {
where
F: FnOnce(Event) + Send + 'static,
{
self.manager.once(event.into(), EventTarget::App, handler)
let event = EventName::new(event.into()).unwrap();
self.manager.once(event, EventTarget::App, handler)
}

/// Unlisten to an event on this app.
Expand All @@ -966,79 +967,7 @@ macro_rules! shared_app_impl {
}
}

impl<R: Runtime> Emitter<R> for $app {
/// Emits an event to all [targets](EventTarget).
///
/// # Examples
/// ```
/// use tauri::Emitter;
///
/// #[tauri::command]
/// fn synchronize(app: tauri::AppHandle) {
/// // emits the synchronized event to all webviews
/// app.emit("synchronized", ());
/// }
/// ```
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
self.manager.emit(event, payload)
}

/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to all listeners
/// app.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// app.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone,
{
self.manager.emit_to(target, event, payload)
}

/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool,
{
self.manager.emit_filter(event, payload, filter)
}
}
impl<R: Runtime> Emitter<R> for $app {}
};
}

Expand Down
3 changes: 3 additions & 0 deletions crates/tauri/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ pub enum Error {
/// Bad `__TAURI_INVOKE_KEY__` value received in ipc message.
#[error("bad __TAURI_INVOKE_KEY__ value received in ipc message")]
InvokeKey,
/// Illegal event name.
#[error("only alphanumeric, '-', '/', ':', '_' permitted for event names: {0:?}")]
IllegalEventName(String),
}

impl From<getrandom::Error> for Error {
Expand Down
70 changes: 70 additions & 0 deletions crates/tauri/src/event/event_name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use serde::{Deserialize, Deserializer};

/// Checks if an event name is valid.
fn is_event_name_valid(event: &str) -> bool {
event
.chars()
.all(|c| c.is_alphanumeric() || c == '-' || c == '/' || c == ':' || c == '_')
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct EventName<S = String>(S);

impl Copy for EventName<&str> {}

impl<S: AsRef<str>> EventName<S> {
pub(crate) fn new(s: S) -> crate::Result<EventName<S>> {
if !is_event_name_valid(s.as_ref()) {
return Err(crate::Error::IllegalEventName(s.as_ref().to_string()));
}
Ok(EventName(s))
}

pub(crate) fn as_str_event(&self) -> EventName<&str> {
EventName(self.0.as_ref())
}

pub(crate) fn as_str(&self) -> &str {
self.0.as_ref()
}
}

impl<S: std::fmt::Display> std::fmt::Display for EventName<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

impl EventName<&'static str> {
// this convenience method is for using in const contexts to discharge the preconditions
// &'static prevents using this function accidentally with dynamically built string slices
pub(crate) const fn from_str(s: &'static str) -> EventName<&'static str> {
EventName(s)
}
}

impl EventName<&str> {
pub fn into_owned(self) -> EventName {
EventName(self.0.to_string())
}
}

impl<'de> Deserialize<'de> for EventName {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let event_id = String::deserialize(deserializer)?;
if is_event_name_valid(&event_id) {
Ok(EventName(event_id))
} else {
Err(serde::de::Error::custom(
"Event name must include only alphanumeric characters, `-`, `/`, `:` and `_`.",
))
}
}
}
Loading
Loading