相対パスを絶対パスに変換する

Recipe ID: fs-023

現在のカレントディレクトリ(通常はアプリケーションの実行ディレクトリ)を基準とした相対パスを、絶対パスに変換する方法を解説します。
Tauri では @tauri-apps/api/pathresolve 関数を使用します。

使用方法

@tauri-apps/api/path から resolve をインポートして使用します。

import { resolve } from '@tauri-apps/api/path';

// カレントディレクトリに対する './data/file.txt' の絶対パスを取得
const absPath = await resolve('data', 'file.txt');

resolve 関数は、引数として渡されたパスセグメントを結合し、それを絶対パスとして解決します。

1. フロントエンドから作成する (TypeScript)

@tauri-apps/api/pathresolve 関数を使用します。

アプリケーションのリソースパスを解決する

resourceDir と組み合わせて、リソースファイルへの絶対パスを作成します。
resolvejoin と似ていますが、最終的に絶対パスを生成する点が異なります。

import { resolve, resourceDir } from '@tauri-apps/api/path';

async function getResourcePath() {
    try {
        const resourceBase = await resourceDir();
        
        // リソースディレクトリ配下の 'assets/icon.png' への絶対パス
        const iconPath = await resolve(resourceBase, 'assets', 'icon.png');
        
        console.log('Icon Path:', iconPath);
        return iconPath;
    } catch (err) {
        console.error('Resolution failed:', err);
    }
}

複数のパスセグメントからの解決

import { resolve } from '@tauri-apps/api/path';

async function resolvePaths() {
    // 例: /User/foo と bar を解決 -> /User/foo/bar
    // 先頭が絶対パスであれば、そこを基準に解決されます
    
    // 注意: Tauri の resolve の挙動は Node.js の path.resolve と似ていますが、
    // ブラウザ環境や Rust バックエンドとの通信を含むため、
    // 非同期関数 (Promise) であることに注意してください。
    
    const absolutePath = await resolve('/usr/local', 'bin', 'myapp');
    console.log(absolutePath); 
    // Output: /usr/local/bin/myapp (macOS/Linux)
}

2. バックエンドから作成する (Rust)

Rust の fs::canonicalize はパスを解決して絶対パスを返しますが、ファイルが存在する必要があります
存在しないパスの解決(純粋なパス計算)を行いたい場合は、絶対パスを持つベースパス(例: app_local_data_dir)に対して join するのが一般的です(join はベースが絶対パスなら結果も絶対パスになります)。

存在するパスの解決(絶対パス化)

use std::fs;

#[tauri::command]
fn resolve_path(path: String) -> Result<String, String> {
    fs::canonicalize(&path)
        .map(|p| p.to_string_lossy().into_owned())
        .map_err(|e| e.to_string())
}

ベースパスからの解決(存在チェックなし)

use std::path::{Path, PathBuf};

#[tauri::command]
fn resolve_relative(base: String, relative: String) -> String {
    // base が絶対パスであれば、結果も絶対パスになります
    Path::new(&base).join(relative).to_string_lossy().into_owned()
}

補足

  • resolve: 引数を右から左へ処理していき、最初に絶対パスが見つかったところまでのパスを結合して返します。絶対パスが見つからない場合はカレントディレクトリをプレフィックスとして使用します。
  • Rustでの挙動: Rust の std::fs::canonicalize はシンボリックリンクも解決し、ファイルシステム上の実体パス(絶対パス)を返します。