Skip to content

出力パスのディレクトリ保証の実装を変更する#126

Merged
sidepelican merged 1 commit intomainfrom
fine-dir-assume
Sep 18, 2024
Merged

出力パスのディレクトリ保証の実装を変更する#126
sidepelican merged 1 commit intomainfrom
fine-dir-assume

Conversation

@omochi
Copy link
Owner

@omochi omochi commented Sep 18, 2024

現状と課題

現在 outputDirectory/ を付け足しています。
この変更は #117 で実装されていました。

コメントによるとディレクトリであることを保証したいようですが、
これは Foundation.URL の取り扱いとしては変です。

例えば以下のように末尾スラッシュをつけると、さらにそこにファイルを結合した時に、
パス文字列にダブルスラッシュが出現してしまいます。

let dir = URL(fileURLWithPath: "dir").appending(component: "/")
let file = dir.appendingPathComponent("main.swift")
print(file.path) 
// => /Users/omochi/temp/dir//main.swift

実際、C2TSの動作ログで、ダブルスラッシュが含まれるパスが表示されてしまいます。

そもそも URL 型は、「そのパスがディレクトリかどうか」というフラグを内部で保持しています。
これは、 init時に isDirectory 引数で指定することができます。
引数を省略した場合は、ファイルシステムに問い合わせてディレクトリの実在性に基づいて設定されます。

この属性は、相対パスを解決する時などに動作に影響があります。
例えばウェブブラウザが home/index.html を表示しているときに、
login.html と書かれたリンクを踏んだ時、解決されるURLに影響します。
index.html がファイルであれば、現在居るのは home ディレクトリなので、
home/login.html が宛先になります。
もし index.html がディレクトリであれば、
現在いるのは home/index.html ディレクトリなので(拡張子から考えて不自然ですが)
home/index.html/login.html が宛先になります。

print(
    URL(
        fileURLWithPath: "login.html",
        relativeTo: URL(fileURLWithPath: "home/index.html", isDirectory: false)
    ).path
)
// => /Users/omochi/temp/home/login.html

print(
    URL(
        fileURLWithPath: "login.html",
        relativeTo: URL(fileURLWithPath: "home/index.html", isDirectory: true)
    ).path
)
// => /Users/omochi/temp/home/index.html/login.html

この relativeTo に指定したURLは baseURL プロパティとして保持されていて、
initで指定しなかった場合は暗黙にカレントディレクトリになっています。

path を使うと結合結果が得られますが、 relativePath を使うと自身のパス部分だけが得られます。

C2TSではこの relativePath を活用しているので、
relativeTo, baseURL, isDirectory も合わせて正しく使いたいです。

また URL はディレクトリを表す末尾スラッシュはパス表現としては含めず、
パスの結合操作などの際に必要に応じてスラッシュを挿入します。

ただしfileスキームのURL形式で表示する場合は、ディレクトリならば末尾スラッシュをつけます。

let file = URL(fileURLWithPath: "file", isDirectory: false)
print(file.path) // => /Users/omochi/temp/file
print(file.absoluteString) // => file:///Users/omochi/temp/file

let dir = URL(fileURLWithPath: "dir", isDirectory: true)
print(dir.path) // => /Users/omochi/temp/dir
print(dir.absoluteString) // => file:///Users/omochi/temp/dir/

自分で末尾スラッシュのパスをつけてしまうと、
pathComponents などの返す結果もちょっと変になります。
URL形式だとそれだけでダブルスラッシュになってしまいます。

let dir = URL(fileURLWithPath: "dir").appendingPathComponent("/")
print(dir.pathComponents)
// => ["/", "Users", "omochi", "temp", "dir", "/"]
print(dir.absoluteURL)
// => file:///Users/omochi/temp/dir//

これは相対パスの解決処理などに影響する可能性もあります。

修正

initで受けた outputDirectory を操作して、 isDirectorytrueURL を作り直し、それを保持するようにします。

他の案

initで受けた outputDirectoryhasDirectoryPath をみて false だったら例外を投げることを検討しました。
互換性の問題が出そうなのと、この仕様を知らないと難しいので微妙かなと思いました。

@omochi omochi requested a review from sidepelican September 18, 2024 05:23
Copy link
Collaborator

@sidepelican sidepelican left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど。確かにここはディレクトリパスを保証したかっただけかつ/がpathComponentsとして扱われると気づいてなくて、/をつけることでディレクトリ扱いにしたつもりになってそうですね。
isDirectoryフラグがそのように機能していることは気づいてませんでした。勉強になります

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants