Skip to content

Commit

Permalink
Respect XDG_CACHE_HOME and useNSFileManager for cache paths
Browse files Browse the repository at this point in the history
  • Loading branch information
liby committed Dec 28, 2024
1 parent f0ec205 commit 3b5bc77
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 45 deletions.
87 changes: 55 additions & 32 deletions src/os/macos.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,43 +25,23 @@ pub fn appSupportDir(
alloc: Allocator,
sub_path: []const u8,
) AppSupportDirError![]u8 {
comptime assert(builtin.target.isDarwin());

const NSFileManager = objc.getClass("NSFileManager").?;
const manager = NSFileManager.msgSend(
objc.Object,
objc.sel("defaultManager"),
.{},
);

const url = manager.msgSend(
objc.Object,
objc.sel("URLForDirectory:inDomain:appropriateForURL:create:error:"),
.{
NSSearchPathDirectory.NSApplicationSupportDirectory,
NSSearchPathDomainMask.NSUserDomainMask,
@as(?*anyopaque, null),
true,
@as(?*anyopaque, null),
},
);

// I don't think this is possible but just in case.
if (url.value == null) return error.AppleAPIFailed;

// Get the UTF-8 string from the URL.
const path = url.getProperty(objc.Object, "path");
const c_str = path.getProperty(?[*:0]const u8, "UTF8String") orelse
return error.AppleAPIFailed;
const app_support_dir = std.mem.sliceTo(c_str, 0);

return try std.fs.path.join(alloc, &.{
app_support_dir,
return try makeCommonPath(alloc, .NSApplicationSupportDirectory, &.{
build_config.bundle_id,
sub_path,
});
}

pub const CacheDirError = Allocator.Error || error{AppleAPIFailed};

/// Return the path to the system cache directory with the given sub path joined.
/// This allocates the result using the given allocator.
pub fn cacheDir(
alloc: Allocator,
sub_path: []const u8,
) CacheDirError![]u8 {
return try makeCommonPath(alloc, .NSCachesDirectory, &.{sub_path});
}

pub const SetQosClassError = error{
// The thread can't have its QoS class changed usually because
// a different pthread API was called that makes it an invalid
Expand Down Expand Up @@ -110,9 +90,52 @@ pub const NSOperatingSystemVersion = extern struct {
};

pub const NSSearchPathDirectory = enum(c_ulong) {
NSCachesDirectory = 13,
NSApplicationSupportDirectory = 14,
};

pub const NSSearchPathDomainMask = enum(c_ulong) {
NSUserDomainMask = 1,
};

fn makeCommonPath(
alloc: Allocator,
directory: NSSearchPathDirectory,
sub_paths: []const []const u8,
) (error{AppleAPIFailed} || Allocator.Error)![]u8 {
comptime assert(builtin.target.isDarwin());

const NSFileManager = objc.getClass("NSFileManager").?;
const manager = NSFileManager.msgSend(
objc.Object,
objc.sel("defaultManager"),
.{},
);

const url = manager.msgSend(
objc.Object,
objc.sel("URLForDirectory:inDomain:appropriateForURL:create:error:"),
.{
directory,
NSSearchPathDomainMask.NSUserDomainMask,
@as(?*anyopaque, null),
true,
@as(?*anyopaque, null),
},
);

if (url.value == null) return error.AppleAPIFailed;

const path = url.getProperty(objc.Object, "path");
const c_str = path.getProperty(?[*:0]const u8, "UTF8String") orelse
return error.AppleAPIFailed;
const base_dir = std.mem.sliceTo(c_str, 0);

// Create a new array with base_dir as the first element
var paths = try alloc.alloc([]const u8, sub_paths.len + 1);
paths[0] = base_dir;
@memcpy(paths[1..], sub_paths);
defer alloc.free(paths);

return try std.fs.path.join(alloc, paths);
}
23 changes: 10 additions & 13 deletions src/os/xdg.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const posix = std.posix;
const homedir = @import("homedir.zig");
const macos = @import("macos.zig");

pub const Options = struct {
/// Subdirectories to join to the base. This avoids extra allocations
Expand All @@ -30,22 +31,17 @@ pub fn config(alloc: Allocator, opts: Options) ![]u8 {

/// Get the XDG cache directory. The returned value is allocated.
pub fn cache(alloc: Allocator, opts: Options) ![]u8 {
// On macOS we should use ~/Library/Caches instead of ~/.cache
if (builtin.os.tag == .macos) {
// Get our home dir if not provided
const home = if (opts.home) |h| h else blk: {
var buf: [1024]u8 = undefined;
break :blk try homedir.home(&buf) orelse return error.NoHomeDir;
};

if (posix.getenv("XDG_CACHE_HOME")) |env| {
return try std.fs.path.join(alloc, &[_][]const u8{
home,
"Library",
"Caches",
env,
opts.subdir orelse "",
});
}

if (builtin.os.tag == .macos) {
return try macos.cacheDir(alloc, opts.subdir orelse "");
}

return try dir(alloc, opts, .{
.env = "XDG_CACHE_HOME",
.windows_env = "LOCALAPPDATA",
Expand Down Expand Up @@ -170,7 +166,8 @@ test "cache directory paths" {
{
const cache_path = try cache(alloc, .{ .home = mock_home });
defer alloc.free(cache_path);
try testing.expectEqualStrings("/Users/test/Library/Caches", cache_path);
// We don't test the exact path since it comes from NSFileManager
try testing.expect(std.mem.indexOf(u8, cache_path, "Caches") != null);
}

// Test with subdir
Expand All @@ -180,7 +177,7 @@ test "cache directory paths" {
.subdir = "ghostty",
});
defer alloc.free(cache_path);
try testing.expectEqualStrings("/Users/test/Library/Caches/ghostty", cache_path);
try testing.expect(std.mem.indexOf(u8, cache_path, "Caches/ghostty") != null);
}
}

Expand Down

0 comments on commit 3b5bc77

Please sign in to comment.