feat(path): add Rust convert_file_src function

Adds a Rust equivalent of the JavaScript convertFileSrc() function,
allowing backend code to convert device file paths to URLs that can
be loaded by the webview.

This is useful when processing file paths in Rust (e.g., markdown
parsers) that need to generate URLs for the webview to load assets.

The function:
- URL encodes the file path
- Uses platform-appropriate scheme (http:// on Windows/Android,
  custom protocol on other platforms)
- Supports custom protocols via optional parameter

Fixes #12022

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ddmoney420 2026-01-29 13:26:40 -07:00
parent 32576120fd
commit b7639e978e

View File

@ -7,6 +7,56 @@ use std::{
str::FromStr,
};
/// Convert a device file path to a URL that can be loaded by the webview.
///
/// Note that `asset:` and `http://asset.localhost` must be added to
/// [`app.security.csp`](https://v2.tauri.app/reference/config/#csp-1) in `tauri.conf.json`.
///
/// Additionally, `"enable": true` must be added to
/// [`app.security.assetProtocol`](https://v2.tauri.app/reference/config/#assetprotocolconfig)
/// in `tauri.conf.json` and its access scope must be defined on the `scope` array.
///
/// # Arguments
///
/// * `file_path` - The file path to convert.
/// * `protocol` - The protocol to use. Defaults to `"asset"`. You only need to set this
/// when using a custom protocol.
///
/// # Example
///
/// ```rust,no_run
/// use tauri::path::convert_file_src;
///
/// let path = "/path/to/video.mp4";
/// let asset_url = convert_file_src(path, None);
/// // asset_url can now be used as src in the webview
/// ```
///
/// # Returns
///
/// A URL string that can be used as source on the webview.
pub fn convert_file_src<P: AsRef<Path>>(file_path: P, protocol: Option<&str>) -> String {
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
let protocol = protocol.unwrap_or("asset");
let path = file_path.as_ref().to_string_lossy();
// URL encode the path (similar to JavaScript's encodeURIComponent)
let encoded_path = utf8_percent_encode(&path, NON_ALPHANUMERIC).to_string();
// On Windows and Android, use http:// scheme with .localhost
// On other platforms, use custom protocol scheme directly
#[cfg(any(windows, target_os = "android"))]
{
format!("http://{}.localhost/{}", protocol, encoded_path)
}
#[cfg(not(any(windows, target_os = "android")))]
{
format!("{}://localhost/{}", protocol, encoded_path)
}
}
use crate::Runtime;
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize};
@ -364,7 +414,7 @@ fn resolve_path<R: Runtime>(
#[cfg(test)]
mod test {
use super::SafePathBuf;
use super::{convert_file_src, SafePathBuf};
use quickcheck::{Arbitrary, Gen};
use std::path::PathBuf;
@ -378,4 +428,36 @@ mod test {
Box::new(self.0.shrink().map(SafePathBuf))
}
}
#[test]
fn test_convert_file_src_default_protocol() {
let url = convert_file_src("/path/to/file.mp4", None);
#[cfg(any(windows, target_os = "android"))]
assert!(url.starts_with("http://asset.localhost/"));
#[cfg(not(any(windows, target_os = "android")))]
assert!(url.starts_with("asset://localhost/"));
// Path should be URL encoded
assert!(url.contains("%2F")); // "/" encoded
}
#[test]
fn test_convert_file_src_custom_protocol() {
let url = convert_file_src("/path/to/file.mp4", Some("custom"));
#[cfg(any(windows, target_os = "android"))]
assert!(url.starts_with("http://custom.localhost/"));
#[cfg(not(any(windows, target_os = "android")))]
assert!(url.starts_with("custom://localhost/"));
}
#[test]
fn test_convert_file_src_special_chars() {
let url = convert_file_src("/path/with spaces/file.mp4", None);
// Spaces should be encoded
assert!(url.contains("%20"));
}
}