mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-20 05:54:01 +00:00
Require folder selection for SMFS onboarding
This commit is contained in:
parent
f3889806a4
commit
9ff3bbfac6
6 changed files with 321 additions and 14 deletions
|
|
@ -24,6 +24,7 @@ import {
|
|||
updateDesktopOnboardingStatus,
|
||||
} from "@/lib/onboarding"
|
||||
import {
|
||||
chooseSmfsMountPath,
|
||||
getDefaultSmfsContainerTag,
|
||||
getSmfsState,
|
||||
mountSmfs,
|
||||
|
|
@ -340,6 +341,9 @@ function FilesystemStep({
|
|||
}) {
|
||||
const [tag, setTag] = useState("sm_fs_desktop")
|
||||
const [status, setStatus] = useState<SmfsStatus | null>(null)
|
||||
const [selectedMountPath, setSelectedMountPath] = useState<string | null>(
|
||||
null,
|
||||
)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
|
@ -353,7 +357,16 @@ function FilesystemStep({
|
|||
getSmfsState(),
|
||||
])
|
||||
setTag(defaultTag)
|
||||
setStatus(statuses[0] ?? null)
|
||||
const nextStatus = statuses[0] ?? null
|
||||
setStatus(nextStatus)
|
||||
if (
|
||||
nextStatus?.mountPath &&
|
||||
(nextStatus.mountPathConfigured ||
|
||||
nextStatus.state === "mounted" ||
|
||||
nextStatus.state === "external")
|
||||
) {
|
||||
setSelectedMountPath(nextStatus.mountPath)
|
||||
}
|
||||
} catch (err) {
|
||||
setError(formatUnknownError(err, "Could not load filesystem status"))
|
||||
} finally {
|
||||
|
|
@ -369,7 +382,12 @@ function FilesystemStep({
|
|||
setError(null)
|
||||
setBusy(true)
|
||||
try {
|
||||
setStatus(await mountSmfs(tag))
|
||||
if (!selectedMountPath) {
|
||||
throw new Error("Choose a folder before mounting SMFS")
|
||||
}
|
||||
const nextStatus = await mountSmfs(tag, selectedMountPath)
|
||||
setStatus(nextStatus)
|
||||
setSelectedMountPath(nextStatus.mountPath)
|
||||
} catch (err) {
|
||||
setError(formatUnknownError(err, "Could not mount memory folder"))
|
||||
} finally {
|
||||
|
|
@ -377,7 +395,25 @@ function FilesystemStep({
|
|||
}
|
||||
}
|
||||
|
||||
async function chooseFolder() {
|
||||
setError(null)
|
||||
setBusy(true)
|
||||
try {
|
||||
const path = await chooseSmfsMountPath()
|
||||
if (path) {
|
||||
setSelectedMountPath(path)
|
||||
}
|
||||
} catch (err) {
|
||||
setError(formatUnknownError(err, "Could not choose mount folder"))
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
}
|
||||
|
||||
const mounted = status?.state === "mounted" || status?.state === "external"
|
||||
const mountPath =
|
||||
selectedMountPath ?? (status?.mountPathConfigured ? status.mountPath : null)
|
||||
const canMount = Boolean(mountPath) && !loading && !busy
|
||||
|
||||
return (
|
||||
<section className="mx-auto w-full max-w-4xl space-y-6">
|
||||
|
|
@ -410,8 +446,11 @@ function FilesystemStep({
|
|||
|
||||
<div className="mt-auto flex items-center justify-between gap-4 pt-8">
|
||||
<FilesystemStatus mounted={mounted} loading={loading} />
|
||||
<p className="truncate font-mono font-medium text-[#737373] text-[13px]">
|
||||
{status?.tag ?? tag}
|
||||
<p
|
||||
className="truncate font-mono font-medium text-[#737373] text-[13px]"
|
||||
title={mountPath ?? undefined}
|
||||
>
|
||||
{mountPath ? compactPath(mountPath) : "No folder selected"}
|
||||
</p>
|
||||
</div>
|
||||
{error ? <p className="mt-4 text-red-300 text-sm">{error}</p> : null}
|
||||
|
|
@ -438,16 +477,25 @@ function FilesystemStep({
|
|||
Mount folder
|
||||
</p>
|
||||
<p className="mt-2 line-clamp-2 font-medium text-[#9A9AA2] text-[14px] leading-[1.45]">
|
||||
Create the local memory folder for this Mac
|
||||
Choose or create the local folder SMFS should mount into
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="insideOut"
|
||||
onClick={mounted ? onContinue : mount}
|
||||
disabled={loading || busy}
|
||||
onClick={chooseFolder}
|
||||
disabled={loading || busy || mounted}
|
||||
className="mt-auto h-10 w-full rounded-full font-medium text-[#FAFAFA] text-[15px]"
|
||||
>
|
||||
{mountPath ? "Change folder" : "Choose folder"}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="insideOut"
|
||||
onClick={mounted ? onContinue : mount}
|
||||
disabled={!mounted && !canMount}
|
||||
className="mt-3 h-10 w-full rounded-full font-medium text-[#FAFAFA] text-[15px]"
|
||||
>
|
||||
{busy || loading ? (
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
|
|
@ -842,6 +890,16 @@ function NovaBackground() {
|
|||
)
|
||||
}
|
||||
|
||||
function compactPath(path: string) {
|
||||
if (path.startsWith("/Users/")) {
|
||||
const parts = path.split("/")
|
||||
if (parts.length > 3) {
|
||||
return `~/${parts.slice(3).join("/")}`
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
function formatUnknownError(error: unknown, fallback: string) {
|
||||
return error instanceof Error
|
||||
? error.message
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export type SmfsStatus = {
|
|||
| "missing-binary"
|
||||
| "error"
|
||||
mountPath: string
|
||||
mountPathConfigured: boolean
|
||||
ownedByApp: boolean
|
||||
profileAvailable: boolean
|
||||
lastSync?: string | null
|
||||
|
|
@ -38,8 +39,12 @@ export function getSmfsState() {
|
|||
return invoke<SmfsStatus[]>("smfs_state")
|
||||
}
|
||||
|
||||
export function mountSmfs(tag?: string) {
|
||||
return invoke<SmfsStatus>("smfs_mount", { tag })
|
||||
export function chooseSmfsMountPath() {
|
||||
return invoke<string | null>("smfs_choose_mount_path")
|
||||
}
|
||||
|
||||
export function mountSmfs(tag?: string, mountPath?: string) {
|
||||
return invoke<SmfsStatus>("smfs_mount", { tag, mountPath })
|
||||
}
|
||||
|
||||
export function unmountSmfs(tag?: string) {
|
||||
|
|
|
|||
163
apps/desktop/src-tauri/Cargo.lock
generated
163
apps/desktop/src-tauri/Cargo.lock
generated
|
|
@ -47,6 +47,28 @@ version = "1.0.102"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39"
|
||||
dependencies = [
|
||||
"async-fs",
|
||||
"async-net",
|
||||
"enumflags2",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"rand",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"url",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.2"
|
||||
|
|
@ -85,6 +107,17 @@ dependencies = [
|
|||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-fs"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"blocking",
|
||||
"futures-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "2.6.0"
|
||||
|
|
@ -114,6 +147,17 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-net"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
|
||||
dependencies = [
|
||||
"async-io",
|
||||
"blocking",
|
||||
"futures-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.5.0"
|
||||
|
|
@ -777,6 +821,15 @@ dependencies = [
|
|||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2"
|
||||
version = "0.8.2"
|
||||
|
|
@ -824,6 +877,12 @@ dependencies = [
|
|||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "dpi"
|
||||
version = "0.1.2"
|
||||
|
|
@ -2572,6 +2631,12 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pollster"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.5"
|
||||
|
|
@ -2924,6 +2989,30 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfd"
|
||||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed"
|
||||
dependencies = [
|
||||
"ashpd",
|
||||
"block2",
|
||||
"dispatch2",
|
||||
"js-sys",
|
||||
"log",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation",
|
||||
"pollster",
|
||||
"raw-window-handle",
|
||||
"urlencoding",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
|
|
@ -3083,6 +3172,12 @@ dependencies = [
|
|||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
|
@ -3445,6 +3540,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"keyring",
|
||||
"reqwest 0.12.28",
|
||||
"rfd",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
|
|
@ -4361,6 +4457,12 @@ dependencies = [
|
|||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "urlpattern"
|
||||
version = "0.3.0"
|
||||
|
|
@ -4531,6 +4633,66 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
"rustix",
|
||||
"scoped-tls",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
version = "0.31.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"rustix",
|
||||
"wayland-backend",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.32.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.31.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
|
||||
dependencies = [
|
||||
"dlib",
|
||||
"log",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.102"
|
||||
|
|
@ -5413,6 +5575,7 @@ dependencies = [
|
|||
"endi",
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"url",
|
||||
"winnow 1.0.3",
|
||||
"zvariant_derive",
|
||||
"zvariant_utils",
|
||||
|
|
|
|||
|
|
@ -26,3 +26,4 @@ tauri-plugin-deep-link = "2"
|
|||
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
|
||||
url = "2"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
rfd = "0.15"
|
||||
|
|
|
|||
|
|
@ -136,8 +136,17 @@ fn smfs_state(app: tauri::AppHandle) -> Result<Vec<smfs::SmfsStatus>, String> {
|
|||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn smfs_mount(app: tauri::AppHandle, tag: Option<String>) -> Result<smfs::SmfsStatus, String> {
|
||||
smfs::mount(&app, tag)
|
||||
fn smfs_choose_mount_path() -> Result<Option<String>, String> {
|
||||
smfs::choose_mount_path()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn smfs_mount(
|
||||
app: tauri::AppHandle,
|
||||
tag: Option<String>,
|
||||
mount_path: Option<String>,
|
||||
) -> Result<smfs::SmfsStatus, String> {
|
||||
smfs::mount(&app, tag, mount_path)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
|
@ -232,6 +241,7 @@ pub fn run() {
|
|||
spotlight_get_shortcut,
|
||||
spotlight_set_shortcut,
|
||||
smfs_state,
|
||||
smfs_choose_mount_path,
|
||||
smfs_mount,
|
||||
smfs_unmount,
|
||||
smfs_sync,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ pub struct SmfsStatus {
|
|||
tag: String,
|
||||
state: SmfsState,
|
||||
mount_path: String,
|
||||
mount_path_configured: bool,
|
||||
owned_by_app: bool,
|
||||
profile_available: bool,
|
||||
last_sync: Option<String>,
|
||||
|
|
@ -96,11 +97,32 @@ pub fn state(app: &AppHandle) -> Result<Vec<SmfsStatus>, String> {
|
|||
Ok(vec![status_for_tag(app, DEFAULT_CONTAINER_TAG)?])
|
||||
}
|
||||
|
||||
pub fn mount(app: &AppHandle, tag: Option<String>) -> Result<SmfsStatus, String> {
|
||||
pub fn choose_mount_path() -> Result<Option<String>, String> {
|
||||
Ok(rfd::FileDialog::new()
|
||||
.set_title("Choose SMFS mount folder")
|
||||
.pick_folder()
|
||||
.map(|path| path_to_string(&path)))
|
||||
}
|
||||
|
||||
pub fn mount(
|
||||
app: &AppHandle,
|
||||
tag: Option<String>,
|
||||
mount_path: Option<String>,
|
||||
) -> Result<SmfsStatus, String> {
|
||||
let tag = normalize_tag(tag)?;
|
||||
let token = auth::get_token()?.ok_or_else(|| "No stored token".to_string())?;
|
||||
let mount_path = expected_mount_path(app, &tag)?;
|
||||
let (configured_path, _) = configured_mount_path(app, &tag)?;
|
||||
let mount_path = match mount_path {
|
||||
Some(path) => normalize_mount_path(path)?,
|
||||
None => configured_path,
|
||||
};
|
||||
fs::create_dir_all(&mount_path).map_err(|error| error.to_string())?;
|
||||
if !mount_path.is_dir() {
|
||||
return Err(format!(
|
||||
"{} is not a directory",
|
||||
path_to_string(&mount_path)
|
||||
));
|
||||
}
|
||||
|
||||
let status = status_for_tag(app, &tag)?;
|
||||
match status.state {
|
||||
|
|
@ -242,7 +264,7 @@ pub fn profile(app: &AppHandle, tag: Option<String>) -> Result<SmfsProfile, Stri
|
|||
fn status_for_tag(app: &AppHandle, tag: &str) -> Result<SmfsStatus, String> {
|
||||
validate_tag(tag)?;
|
||||
|
||||
let expected_mount_path = expected_mount_path(app, tag)?;
|
||||
let (expected_mount_path, mount_path_configured) = configured_mount_path(app, tag)?;
|
||||
let binary_path = smfs_bin(app);
|
||||
let binary_path_string = path_to_string(&binary_path);
|
||||
let output = match run_smfs(app, ["status", tag, "--json"]) {
|
||||
|
|
@ -252,6 +274,7 @@ fn status_for_tag(app: &AppHandle, tag: &str) -> Result<SmfsStatus, String> {
|
|||
tag: tag.to_string(),
|
||||
state: SmfsState::MissingBinary,
|
||||
mount_path: path_to_string(&expected_mount_path),
|
||||
mount_path_configured,
|
||||
owned_by_app: false,
|
||||
profile_available: false,
|
||||
last_sync: None,
|
||||
|
|
@ -264,6 +287,7 @@ fn status_for_tag(app: &AppHandle, tag: &str) -> Result<SmfsStatus, String> {
|
|||
tag: tag.to_string(),
|
||||
state: SmfsState::Error,
|
||||
mount_path: path_to_string(&expected_mount_path),
|
||||
mount_path_configured,
|
||||
owned_by_app: false,
|
||||
profile_available: false,
|
||||
last_sync: None,
|
||||
|
|
@ -291,6 +315,7 @@ fn status_for_tag(app: &AppHandle, tag: &str) -> Result<SmfsStatus, String> {
|
|||
.unwrap_or_else(|| expected_mount_path.clone());
|
||||
let owned_by_app = is_app_owned_mount(app, tag, &active_mount_path);
|
||||
let profile_available = active_mount_path.join("profile.md").exists();
|
||||
let mount_path_configured = owned_by_app || mount_path_configured;
|
||||
let state = if is_degraded(&parsed) {
|
||||
SmfsState::Degraded
|
||||
} else if owned_by_app {
|
||||
|
|
@ -303,6 +328,7 @@ fn status_for_tag(app: &AppHandle, tag: &str) -> Result<SmfsStatus, String> {
|
|||
tag: tag.to_string(),
|
||||
state,
|
||||
mount_path: path_to_string(&active_mount_path),
|
||||
mount_path_configured,
|
||||
owned_by_app,
|
||||
profile_available,
|
||||
last_sync: first_string(&parsed, &[&["lastSync"], &["last_sync"]]),
|
||||
|
|
@ -320,6 +346,7 @@ fn status_for_tag(app: &AppHandle, tag: &str) -> Result<SmfsStatus, String> {
|
|||
tag: tag.to_string(),
|
||||
state: SmfsState::Unmounted,
|
||||
mount_path: path_to_string(&expected_mount_path),
|
||||
mount_path_configured,
|
||||
owned_by_app: false,
|
||||
profile_available: false,
|
||||
last_sync: None,
|
||||
|
|
@ -332,6 +359,7 @@ fn status_for_tag(app: &AppHandle, tag: &str) -> Result<SmfsStatus, String> {
|
|||
tag: tag.to_string(),
|
||||
state: SmfsState::Error,
|
||||
mount_path: path_to_string(&expected_mount_path),
|
||||
mount_path_configured,
|
||||
owned_by_app: false,
|
||||
profile_available: false,
|
||||
last_sync: None,
|
||||
|
|
@ -367,7 +395,49 @@ fn validate_tag(tag: &str) -> Result<(), String> {
|
|||
}
|
||||
}
|
||||
|
||||
fn normalize_mount_path(path: String) -> Result<PathBuf, String> {
|
||||
let trimmed = path.trim();
|
||||
if trimmed.is_empty() {
|
||||
return Err("Choose a folder before mounting SMFS".to_string());
|
||||
}
|
||||
|
||||
let path = shellexpand_path(trimmed);
|
||||
if path.exists() && !path.is_dir() {
|
||||
return Err(format!("{} is not a directory", path_to_string(&path)));
|
||||
}
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
fn shellexpand_path(path: &str) -> PathBuf {
|
||||
if path == "~" {
|
||||
if let Some(home) = env::var_os("HOME") {
|
||||
return PathBuf::from(home);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rest) = path.strip_prefix("~/") {
|
||||
if let Some(home) = env::var_os("HOME") {
|
||||
return PathBuf::from(home).join(rest);
|
||||
}
|
||||
}
|
||||
|
||||
PathBuf::from(path)
|
||||
}
|
||||
|
||||
fn configured_mount_path(app: &AppHandle, tag: &str) -> Result<(PathBuf, bool), String> {
|
||||
if let Some(marker) = read_ownership_marker(app, tag) {
|
||||
return Ok((PathBuf::from(marker.mount_path), true));
|
||||
}
|
||||
|
||||
Ok((default_mount_path(app, tag)?, false))
|
||||
}
|
||||
|
||||
fn expected_mount_path(app: &AppHandle, tag: &str) -> Result<PathBuf, String> {
|
||||
configured_mount_path(app, tag).map(|(path, _)| path)
|
||||
}
|
||||
|
||||
fn default_mount_path(app: &AppHandle, tag: &str) -> Result<PathBuf, String> {
|
||||
Ok(app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue