Skip to content

Commit 05b9d81

Browse files
feat(api.js): add nodejs-inspired functions in path module (#2310)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
1 parent 1a51006 commit 05b9d81

File tree

12 files changed

+405
-73
lines changed

12 files changed

+405
-73
lines changed

.changes/api-path-module.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"api": patch
3+
---
4+
5+
- Add new nodejs-inspired functions which are `join`, `resolve`, `normalize`, `dirname`, `basename` and `extname`.
6+
- Add `sep` and `delimiter` constants.
7+
- Removed `resolvePath` API, use `resolve` instead.

core/tauri/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ api-all = [
100100
"shell-all",
101101
"os-all",
102102
"dialog-all",
103-
"updater"
103+
"updater",
104+
"path-all"
104105
]
105106
updater = [ "minisign-verify", "base64" ]
106107
menu = [ "tauri-runtime/menu", "tauri-runtime-wry/menu" ]
@@ -117,7 +118,7 @@ fs-create-dir = [ ]
117118
fs-remove-dir = [ ]
118119
fs-remove-file = [ ]
119120
fs-rename-file = [ ]
120-
fs-path = [ ]
121+
path-all = [ ]
121122
window-all = [ ]
122123
window-create = [ ]
123124
shell-all = [ "shell-open", "shell-execute" ]

core/tauri/build.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ fn main() {
2323
fs_remove_dir: { any(fs_all, feature = "fs-remove-dir") },
2424
fs_remove_file: { any(fs_all, feature = "fs-remove-file") },
2525
fs_rename_file: { any(fs_all, feature = "fs-rename-file") },
26-
fs_path: { any(fs_all, feature = "fs-path") },
2726

2827
// window
2928
window_all: { any(api_all, feature = "window-all") },
@@ -54,5 +53,8 @@ fn main() {
5453

5554
// os
5655
os_all: { any(api_all, feature = "os-all") },
56+
57+
// path
58+
path_all: { any(api_all, feature = "path-all") },
5759
}
5860
}

core/tauri/scripts/bundle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/tauri/src/endpoints.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod http;
2525
mod internal;
2626
mod notification;
2727
mod operating_system;
28+
mod path;
2829
mod process;
2930
mod shell;
3031
mod window;
@@ -49,6 +50,7 @@ enum Module {
4950
Process(process::Cmd),
5051
Fs(file_system::Cmd),
5152
Os(operating_system::Cmd),
53+
Path(path::Cmd),
5254
Window(Box<window::Cmd>),
5355
Shell(shell::Cmd),
5456
Event(event::Cmd),
@@ -84,6 +86,12 @@ impl Module {
8486
.and_then(|r| r.json)
8587
.map_err(InvokeError::from)
8688
}),
89+
Self::Path(cmd) => resolver.respond_async(async move {
90+
cmd
91+
.run(config, &package_info)
92+
.and_then(|r| r.json)
93+
.map_err(InvokeError::from)
94+
}),
8795
Self::Os(cmd) => resolver
8896
.respond_async(async move { cmd.run().and_then(|r| r.json).map_err(InvokeError::from) }),
8997
Self::Window(cmd) => resolver.respond_async(async move {

core/tauri/src/endpoints/file_system.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,6 @@ pub enum Cmd {
9393
new_path: PathBuf,
9494
options: Option<FileOperationOptions>,
9595
},
96-
/// The resolve path API
97-
ResolvePath {
98-
path: String,
99-
directory: Option<BaseDirectory>,
100-
},
10196
}
10297

10398
impl Cmd {
@@ -201,13 +196,6 @@ impl Cmd {
201196
Self::RenameFile { .. } => Err(crate::Error::ApiNotAllowlisted(
202197
"fs > renameFile".to_string(),
203198
)),
204-
205-
#[cfg(fs_path)]
206-
Self::ResolvePath { path, directory } => {
207-
resolve_path_handler(&config, package_info, path, directory).map(Into::into)
208-
}
209-
#[cfg(not(fs_path))]
210-
Self::ResolvePath { .. } => Err(crate::Error::ApiNotAllowlisted("fs > path".to_string())),
211199
}
212200
}
213201
}
@@ -405,16 +393,6 @@ pub fn read_binary_file(
405393
.map_err(crate::Error::FailedToExecuteApi)
406394
}
407395

408-
#[cfg(fs_path)]
409-
pub fn resolve_path_handler(
410-
config: &Config,
411-
package_info: &PackageInfo,
412-
path: String,
413-
directory: Option<BaseDirectory>,
414-
) -> crate::Result<PathBuf> {
415-
resolve_path(config, package_info, path, directory).map_err(Into::into)
416-
}
417-
418396
// test webview functionality.
419397
#[cfg(test)]
420398
mod test {

core/tauri/src/endpoints/path.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use super::InvokeResponse;
6+
use crate::{api::path::BaseDirectory, Config, PackageInfo};
7+
use serde::Deserialize;
8+
#[cfg(path_all)]
9+
use std::path::{Path, PathBuf};
10+
use std::sync::Arc;
11+
/// The API descriptor.
12+
#[derive(Deserialize)]
13+
#[serde(tag = "cmd", rename_all = "camelCase")]
14+
pub enum Cmd {
15+
ResolvePath {
16+
path: String,
17+
directory: Option<BaseDirectory>,
18+
},
19+
Resolve {
20+
paths: Vec<String>,
21+
},
22+
Normalize {
23+
path: String,
24+
},
25+
Join {
26+
paths: Vec<String>,
27+
},
28+
Dirname {
29+
path: String,
30+
},
31+
Extname {
32+
path: String,
33+
},
34+
Basename {
35+
path: String,
36+
ext: Option<String>,
37+
},
38+
IsAbsolute {
39+
path: String,
40+
},
41+
}
42+
43+
impl Cmd {
44+
#[allow(unused_variables)]
45+
pub fn run(
46+
self,
47+
config: Arc<Config>,
48+
package_info: &PackageInfo,
49+
) -> crate::Result<InvokeResponse> {
50+
#[cfg(path_all)]
51+
return match self {
52+
Cmd::ResolvePath { directory, path } => {
53+
resolve_path_handler(&config, package_info, path, directory).map(Into::into)
54+
}
55+
Cmd::Resolve { paths } => resolve(paths).map(Into::into),
56+
Cmd::Normalize { path } => normalize(path).map(Into::into),
57+
Cmd::Join { paths } => join(paths).map(Into::into),
58+
Cmd::Dirname { path } => dirname(path).map(Into::into),
59+
Cmd::Extname { path } => extname(path).map(Into::into),
60+
Cmd::Basename { path, ext } => basename(path, ext).map(Into::into),
61+
Cmd::IsAbsolute { path } => Ok(Path::new(&path).is_absolute()).map(Into::into),
62+
};
63+
#[cfg(not(path_all))]
64+
Err(crate::Error::ApiNotAllowlisted("path".into()))
65+
}
66+
}
67+
68+
#[cfg(path_all)]
69+
pub fn resolve_path_handler(
70+
config: &Config,
71+
package_info: &PackageInfo,
72+
path: String,
73+
directory: Option<BaseDirectory>,
74+
) -> crate::Result<PathBuf> {
75+
crate::api::path::resolve_path(config, package_info, path, directory).map_err(Into::into)
76+
}
77+
78+
#[cfg(path_all)]
79+
fn resolve(paths: Vec<String>) -> crate::Result<String> {
80+
// start with the current directory
81+
let mut resolved_path = PathBuf::new().join(".");
82+
83+
for path in paths {
84+
let path_buf = PathBuf::from(path);
85+
86+
// if we encounter an absolute path, we use it as the starting path for next iteration
87+
if path_buf.is_absolute() {
88+
resolved_path = path_buf;
89+
} else {
90+
resolved_path = resolved_path.join(&path_buf);
91+
}
92+
}
93+
94+
normalize(resolved_path.to_string_lossy().to_string())
95+
}
96+
97+
#[cfg(path_all)]
98+
fn normalize(path: String) -> crate::Result<String> {
99+
let path = std::fs::canonicalize(path)?;
100+
let path = path.to_string_lossy().to_string();
101+
102+
// remove `\\\\?\\` on windows, UNC path
103+
#[cfg(target_os = "windows")]
104+
let path = path.replace("\\\\?\\", "");
105+
106+
Ok(path)
107+
}
108+
109+
#[cfg(path_all)]
110+
fn join(paths: Vec<String>) -> crate::Result<String> {
111+
let mut joined_path = PathBuf::new();
112+
for path in paths {
113+
joined_path = joined_path.join(path);
114+
}
115+
normalize(joined_path.to_string_lossy().to_string())
116+
}
117+
118+
#[cfg(path_all)]
119+
fn dirname(path: String) -> crate::Result<String> {
120+
match Path::new(&path).parent() {
121+
Some(path) => Ok(path.to_string_lossy().to_string()),
122+
None => Err(crate::Error::FailedToExecuteApi(crate::api::Error::Path(
123+
"Couldn't get the parent directory".into(),
124+
))),
125+
}
126+
}
127+
128+
#[cfg(path_all)]
129+
fn extname(path: String) -> crate::Result<String> {
130+
match Path::new(&path)
131+
.extension()
132+
.and_then(std::ffi::OsStr::to_str)
133+
{
134+
Some(path) => Ok(path.to_string()),
135+
None => Err(crate::Error::FailedToExecuteApi(crate::api::Error::Path(
136+
"Couldn't get the extension of the file".into(),
137+
))),
138+
}
139+
}
140+
141+
#[cfg(path_all)]
142+
fn basename(path: String, ext: Option<String>) -> crate::Result<String> {
143+
match Path::new(&path)
144+
.file_name()
145+
.and_then(std::ffi::OsStr::to_str)
146+
{
147+
Some(path) => Ok(if let Some(ext) = ext {
148+
path.replace(ext.as_str(), "")
149+
} else {
150+
path.to_string()
151+
}),
152+
None => Err(crate::Error::FailedToExecuteApi(crate::api::Error::Path(
153+
"Couldn't get the basename".into(),
154+
))),
155+
}
156+
}

tooling/api/src/helpers/tauri.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { invoke } from '../tauri'
99
type TauriModule =
1010
| 'App'
1111
| 'Fs'
12+
| 'Path'
1213
| 'Os'
1314
| 'Window'
1415
| 'Shell'

0 commit comments

Comments
 (0)