From 848bc668e96743242130c9945004c99111e19b6e Mon Sep 17 00:00:00 2001 From: "Sean T. Allen" Date: Sat, 28 Feb 2026 18:33:44 -0500 Subject: [PATCH] Conform Pony source to style guide Fix all pony-lint violations across all Pony source files: line-length, blank-lines, assignment-indent, array-literal-format, comment-spacing, dot-spacing, indentation, lambda-spacing, file-naming, package-docstring, call-argument-format, public-docstring, and control-structure-alignment. File renames per the file-naming rule: - windows_codepages.pony -> windows_code_pages.pony - simple-example.pony -> main.pony Add a pony-lint CI workflow that triggers on PRs touching *.pony files to enforce the style guide going forward. --- .github/workflows/pony-lint.yml | 24 + CLAUDE.md | 6 +- appdirs/_test.pony | 787 ++++++++++++++------ appdirs/appdirs.pony | 273 ++++--- appdirs/known_folders.pony | 190 +++-- appdirs/windows_code_pages.pony | 9 + appdirs/windows_codepages.pony | 7 - examples/simple-example/main.pony | 119 +++ examples/simple-example/simple-example.pony | 59 -- examples/simple-example/simple_example.pony | 5 + 10 files changed, 1020 insertions(+), 459 deletions(-) create mode 100644 .github/workflows/pony-lint.yml create mode 100644 appdirs/windows_code_pages.pony delete mode 100644 appdirs/windows_codepages.pony create mode 100644 examples/simple-example/main.pony delete mode 100644 examples/simple-example/simple-example.pony create mode 100644 examples/simple-example/simple_example.pony diff --git a/.github/workflows/pony-lint.yml b/.github/workflows/pony-lint.yml new file mode 100644 index 0000000..1f4bb8e --- /dev/null +++ b/.github/workflows/pony-lint.yml @@ -0,0 +1,24 @@ +name: pony-lint + +on: + pull_request: + paths: + - '**/*.pony' + +concurrency: + group: pony-lint-${{ github.ref }} + cancel-in-progress: true + +permissions: + packages: read + +jobs: + pony-lint: + name: Lint Pony source + runs-on: ubuntu-latest + container: + image: ghcr.io/ponylang/shared-docker-ci-standard-builder:release + steps: + - uses: actions/checkout@v4.1.1 + - name: Lint + run: pony-lint diff --git a/CLAUDE.md b/CLAUDE.md index dbcb6de..52021f0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,19 +16,19 @@ Uses `corral` for dependency management (fetched automatically by the Makefile). - `appdirs/appdirs.pony` - Core `AppDirs` class and public API - `appdirs/known_folders.pony` - Windows FFI for `SHGetKnownFolderPath` -- `appdirs/windows_codepages.pony` - Windows codepage constants +- `appdirs/windows_code_pages.pony` - Windows codepage constants - `appdirs/_test.pony` - PonyTest unit tests - `examples/simple-example/` - Example usage ## Architecture -Single main class `AppDirs` with constructor parameters (`env_vars`, `app_name`, optional `app_author`, `app_version`, `roaming`, `osx_as_unix`) and eight public methods returning platform-appropriate directory paths. Platform logic is handled via `ifdef` compile-time conditionals. All public methods are partial (`?`) — they raise errors when the home directory is unavailable. +Single main class `AppDirs` with constructor parameters (`env_vars`, `app_name`, optional `app_author`, `app_version`, `roaming`, `osx_as_unix`) and eight public methods returning platform-appropriate directory paths. Platform logic is handled via a `_Platform` match type with `ifdef` used only at construction time. All public methods are partial (`?`) — they raise errors when the home directory is unavailable. Windows support uses FFI to `SHGetKnownFolderPath`. macOS defaults to `~/Library/...` paths but can be switched to XDG via `osx_as_unix`. Linux/Unix uses XDG base directory spec with standard fallbacks. ## Conventions -- Follows [Pony standard library style guide](https://github.com/ponylang/ponyc/blob/main/STYLE_GUIDE.md) (80-column line limit) +- Follows [Pony standard library style guide](https://github.com/ponylang/ponyc/blob/main/STYLE_GUIDE.md) (80-column line limit), enforced by `pony-lint` in CI - Test-file types annotated with `\nodoc\` - Tests are platform-conditional using `ifdef` directives - Squash merge only; PR titles become CHANGELOG entries diff --git a/appdirs/_test.pony b/appdirs/_test.pony index 30d71bd..89c3456 100644 --- a/appdirs/_test.pony +++ b/appdirs/_test.pony @@ -14,11 +14,11 @@ actor \nodoc\ Main is TestList test(_AppDirsUnixXDGVarsTest) test(_AppDirsOsxAsUnixTest) -primitive \nodoc\ _ExpectError +primitive \nodoc\ _ExpectError -type _ErrorOr[T] is ( T | _ExpectError ) +type _ErrorOr[T] is (T | _ExpectError) -class \nodoc\ _ExpectedAppDirs +class \nodoc\ _ExpectedAppDirs let home_dir: _ErrorOr[String] let user_data_dir: _ErrorOr[String] let site_data_dirs: _ErrorOr[Array[String] val] @@ -36,7 +36,8 @@ class \nodoc\ _ExpectedAppDirs site_config_dirs': _ErrorOr[Array[String] val], user_cache_dir': _ErrorOr[String], user_state_dir': _ErrorOr[String], - user_log_dir': _ErrorOr[String]) => + user_log_dir': _ErrorOr[String]) + => home_dir = home_dir' user_data_dir = user_data_dir' site_data_dirs = site_data_dirs' @@ -46,61 +47,104 @@ class \nodoc\ _ExpectedAppDirs user_state_dir = user_state_dir' user_log_dir = user_log_dir' - - -primitive \nodoc\ _AppDirsTestUtil - fun test(app_dirs: AppDirs, expected: _ExpectedAppDirs, h: TestHelper, loc: SourceLoc = __loc) ? => +primitive \nodoc\ _AppDirsTestUtil + fun test( + app_dirs: AppDirs, + expected: _ExpectedAppDirs, + h: TestHelper, + loc: SourceLoc = __loc) ? + => match expected.home_dir | _ExpectError => - h.assert_error({()? => app_dirs.user_home_dir()? } where loc=loc) + h.assert_error( + {()? => app_dirs.user_home_dir()? } + where loc = loc) | let expect: String => - h.assert_eq[String](expect, app_dirs.user_home_dir()? where loc=loc) + h.assert_eq[String]( + expect, + app_dirs.user_home_dir()? + where loc = loc) end match expected.user_data_dir | _ExpectError => - h.assert_error({()? => app_dirs.user_data_dir()? } where loc=loc) + h.assert_error( + {()? => app_dirs.user_data_dir()? } + where loc = loc) | let expect: String => - h.assert_eq[String](expect, app_dirs.user_data_dir()? where loc=loc) + h.assert_eq[String]( + expect, + app_dirs.user_data_dir()? + where loc = loc) end match expected.site_data_dirs | _ExpectError => - h.assert_error({()? => app_dirs.site_data_dirs()? } where loc=loc) + h.assert_error( + {()? => app_dirs.site_data_dirs()? } + where loc = loc) | let expect: Array[String] val => - h.assert_array_eq[String](expect, app_dirs.site_data_dirs()? where loc=loc) + h.assert_array_eq[String]( + expect, + app_dirs.site_data_dirs()? + where loc = loc) end match expected.user_config_dir | _ExpectError => - h.assert_error({()? => app_dirs.user_config_dir()? } where loc=loc) + h.assert_error( + {()? => app_dirs.user_config_dir()? } + where loc = loc) | let expect: String => - h.assert_eq[String](expect, app_dirs.user_config_dir()? where loc=loc) + h.assert_eq[String]( + expect, + app_dirs.user_config_dir()? + where loc = loc) end match expected.site_config_dirs | _ExpectError => - h.assert_error({()? => app_dirs.site_config_dirs()? } where loc=loc) + h.assert_error( + {()? => app_dirs.site_config_dirs()? } + where loc = loc) | let expect: Array[String] val => - h.assert_array_eq[String](expect, app_dirs.site_config_dirs()? where loc=loc) + h.assert_array_eq[String]( + expect, + app_dirs.site_config_dirs()? + where loc = loc) end match expected.user_cache_dir | _ExpectError => - h.assert_error({()? => app_dirs.user_cache_dir()? } where loc=loc) + h.assert_error( + {()? => app_dirs.user_cache_dir()? } + where loc = loc) | let expect: String => - h.assert_eq[String](expect, app_dirs.user_cache_dir()? where loc=loc) + h.assert_eq[String]( + expect, + app_dirs.user_cache_dir()? + where loc = loc) end match expected.user_state_dir | _ExpectError => - h.assert_error({()? => app_dirs.user_state_dir()? } where loc=loc) + h.assert_error( + {()? => app_dirs.user_state_dir()? } + where loc = loc) | let expect: String => - h.assert_eq[String](expect, app_dirs.user_state_dir()? where loc=loc) + h.assert_eq[String]( + expect, + app_dirs.user_state_dir()? + where loc = loc) end match expected.user_log_dir | _ExpectError => - h.assert_error({()? => app_dirs.user_log_dir()? } where loc=loc) + h.assert_error( + {()? => app_dirs.user_log_dir()? } + where loc = loc) | let expect: String => - h.assert_eq[String](expect, app_dirs.user_log_dir()? where loc=loc) + h.assert_eq[String]( + expect, + app_dirs.user_log_dir()? + where loc = loc) end fun test_home(): String => @@ -110,299 +154,572 @@ primitive \nodoc\ _AppDirsTestUtil "/home/ed" end - class \nodoc\ _AppDirsDefaultsTest is UnitTest fun name(): String => "appdirs/defaults" fun apply(h: TestHelper) ? => - let env_vars = [ - as String: "HOME=" + _AppDirsTestUtil.test_home() - ] - let app_dirs = AppDirs(env_vars, "appdirs") + let env_vars = + [ as String: + "HOME=" + _AppDirsTestUtil.test_home() + ] + let app_dirs = + AppDirs(env_vars, "appdirs") let expected = ifdef osx then _ExpectedAppDirs( - where home_dir' = "/Users/ed", - user_data_dir' = "/Users/ed/Library/Application Support/appdirs", - site_data_dirs' = ["/Library/Application Support/appdirs"], - user_config_dir' = "/Users/ed/Library/Preferences/appdirs", - site_config_dirs' = ["/Library/Preferences/appdirs"], - user_cache_dir' = "/Users/ed/Library/Caches/appdirs", - user_state_dir' = "/Users/ed/Library/Application Support/appdirs", - user_log_dir' = "/Users/ed/Library/Logs/appdirs") + where + home_dir' = "/Users/ed", + user_data_dir' = + "/Users/ed/Library" + + "/Application Support/appdirs", + site_data_dirs' = + [ "/Library" + + "/Application Support/appdirs" + ], + user_config_dir' = + "/Users/ed/Library" + + "/Preferences/appdirs", + site_config_dirs' = + ["/Library/Preferences/appdirs"], + user_cache_dir' = + "/Users/ed/Library/Caches/appdirs", + user_state_dir' = + "/Users/ed/Library" + + "/Application Support/appdirs", + user_log_dir' = + "/Users/ed/Library/Logs/appdirs") elseif windows then - let user_name = EnvVars(h.env.vars)("USERNAME")? // hack for getting the username + // hack for getting the username + let user_name = + EnvVars(h.env.vars)("USERNAME")? // TODO: only tested on windows 10 + let u: String val = + "C:\\Users\\" + user_name _ExpectedAppDirs( - where home_dir' = "C:\\Users\\" + user_name, - user_data_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - site_data_dirs' = ["C:\\ProgramData\\appdirs"], - user_config_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - site_config_dirs' = ["C:\\ProgramData\\appdirs"], - user_cache_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\Cache", - user_state_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - user_log_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs") + where + home_dir' = u, + user_data_dir' = + u + "\\AppData\\Local\\appdirs", + site_data_dirs' = + ["C:\\ProgramData\\appdirs"], + user_config_dir' = + u + "\\AppData\\Local\\appdirs", + site_config_dirs' = + ["C:\\ProgramData\\appdirs"], + user_cache_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\Cache", + user_state_dir' = + u + "\\AppData\\Local\\appdirs", + user_log_dir' = + u + "\\AppData\\Local\\appdirs") else _ExpectedAppDirs( - where home_dir' = "/home/ed", - user_data_dir' = "/home/ed/.local/share/appdirs", - site_data_dirs' = ["/usr/local/share/appdirs"; "/usr/share/appdirs"], - user_config_dir' = "/home/ed/.config/appdirs", - site_config_dirs' = ["/etc/xdg/appdirs"], - user_cache_dir' = "/home/ed/.cache/appdirs", - user_state_dir' = "/home/ed/.local/state/appdirs", - user_log_dir' = "/home/ed/.cache/appdirs/log") + where + home_dir' = "/home/ed", + user_data_dir' = + "/home/ed/.local/share/appdirs", + site_data_dirs' = + [ "/usr/local/share/appdirs" + "/usr/share/appdirs" + ], + user_config_dir' = + "/home/ed/.config/appdirs", + site_config_dirs' = + ["/etc/xdg/appdirs"], + user_cache_dir' = + "/home/ed/.cache/appdirs", + user_state_dir' = + "/home/ed/.local/state/appdirs", + user_log_dir' = + "/home/ed/.cache/appdirs/log") end - _AppDirsTestUtil.test(app_dirs, expected, h)? + _AppDirsTestUtil.test( + app_dirs, expected, h)? class \nodoc\ _AppDirsVersionTest is UnitTest fun name(): String => "appdirs/version" + fun apply(h: TestHelper) ? => - let env_vars = [ - as String: "HOME=" + _AppDirsTestUtil.test_home() - ] - let app_dirs = AppDirs(env_vars, "appdirs" where app_version="0.4") + let env_vars = + [ as String: + "HOME=" + _AppDirsTestUtil.test_home() + ] + let app_dirs = + AppDirs( + env_vars, "appdirs" + where app_version = "0.4") let expected = ifdef osx then _ExpectedAppDirs( - where home_dir' = "/Users/ed", - user_data_dir' = "/Users/ed/Library/Application Support/appdirs/0.4", - site_data_dirs' = ["/Library/Application Support/appdirs/0.4"], - user_config_dir' = "/Users/ed/Library/Preferences/appdirs/0.4", - site_config_dirs' = ["/Library/Preferences/appdirs/0.4"], - user_cache_dir' = "/Users/ed/Library/Caches/appdirs/0.4", - user_state_dir' = "/Users/ed/Library/Application Support/appdirs/0.4", - user_log_dir' = "/Users/ed/Library/Logs/appdirs/0.4") + where + home_dir' = "/Users/ed", + user_data_dir' = + "/Users/ed/Library" + + "/Application Support" + + "/appdirs/0.4", + site_data_dirs' = + [ "/Library" + + "/Application Support" + + "/appdirs/0.4" + ], + user_config_dir' = + "/Users/ed/Library" + + "/Preferences/appdirs/0.4", + site_config_dirs' = + [ "/Library" + + "/Preferences/appdirs/0.4" + ], + user_cache_dir' = + "/Users/ed/Library" + + "/Caches/appdirs/0.4", + user_state_dir' = + "/Users/ed/Library" + + "/Application Support" + + "/appdirs/0.4", + user_log_dir' = + "/Users/ed/Library" + + "/Logs/appdirs/0.4") elseif windows then - let user_name = EnvVars(h.env.vars)("USERNAME")? // hack for getting the username + // hack for getting the username + let user_name = + EnvVars(h.env.vars)("USERNAME")? + let u: String val = + "C:\\Users\\" + user_name _ExpectedAppDirs( - where home_dir' = "C:\\Users\\" + user_name, - user_data_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\0.4", - site_data_dirs' = ["C:\\ProgramData\\appdirs\\0.4"], - user_config_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\0.4", - site_config_dirs' = ["C:\\ProgramData\\appdirs\\0.4"], - user_cache_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\Cache\\0.4", - user_state_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\0.4", - user_log_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\0.4") + where + home_dir' = u, + user_data_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\0.4", + site_data_dirs' = + ["C:\\ProgramData\\appdirs\\0.4"], + user_config_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\0.4", + site_config_dirs' = + ["C:\\ProgramData\\appdirs\\0.4"], + user_cache_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\Cache\\0.4", + user_state_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\0.4", + user_log_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\0.4") else _ExpectedAppDirs( - where home_dir' = "/home/ed", - user_data_dir' = "/home/ed/.local/share/appdirs/0.4", - site_data_dirs' = ["/usr/local/share/appdirs/0.4"; "/usr/share/appdirs/0.4"], - user_config_dir' = "/home/ed/.config/appdirs/0.4", - site_config_dirs' = ["/etc/xdg/appdirs/0.4"], - user_cache_dir' = "/home/ed/.cache/appdirs/0.4", - user_state_dir' = "/home/ed/.local/state/appdirs/0.4", - user_log_dir' = "/home/ed/.cache/appdirs/0.4/log") + where + home_dir' = "/home/ed", + user_data_dir' = + "/home/ed/.local/share/appdirs/0.4", + site_data_dirs' = + [ "/usr/local/share/appdirs/0.4" + "/usr/share/appdirs/0.4" + ], + user_config_dir' = + "/home/ed/.config/appdirs/0.4", + site_config_dirs' = + ["/etc/xdg/appdirs/0.4"], + user_cache_dir' = + "/home/ed/.cache/appdirs/0.4", + user_state_dir' = + "/home/ed/.local/state/appdirs/0.4", + user_log_dir' = + "/home/ed/.cache/appdirs/0.4/log") end - _AppDirsTestUtil.test(app_dirs, expected, h)? + _AppDirsTestUtil.test( + app_dirs, expected, h)? -class \nodoc\_AppDirsNoHomeTest is UnitTest +class \nodoc\ _AppDirsNoHomeTest is UnitTest fun name(): String => "appdirs/no_home" fun apply(h: TestHelper) ? => let env_vars: Array[String] val = [] - let app_dirs = AppDirs(env_vars, "appdirs") + let app_dirs = + AppDirs(env_vars, "appdirs") let expected = ifdef osx then _ExpectedAppDirs( - where home_dir' = _ExpectError, - user_data_dir' = _ExpectError, - site_data_dirs' = ["/Library/Application Support/appdirs"], - user_config_dir' = _ExpectError, - site_config_dirs' = ["/Library/Preferences/appdirs"], - user_cache_dir' = _ExpectError, - user_state_dir' = _ExpectError, - user_log_dir' = _ExpectError) + where + home_dir' = _ExpectError, + user_data_dir' = _ExpectError, + site_data_dirs' = + [ "/Library" + + "/Application Support/appdirs" + ], + user_config_dir' = _ExpectError, + site_config_dirs' = + ["/Library/Preferences/appdirs"], + user_cache_dir' = _ExpectError, + user_state_dir' = _ExpectError, + user_log_dir' = _ExpectError) elseif windows then // no impact for windows (afaik) - let user_name = EnvVars(h.env.vars)("USERNAME")? // hack for getting the username + // hack for getting the username + let user_name = + EnvVars(h.env.vars)("USERNAME")? + let u: String val = + "C:\\Users\\" + user_name _ExpectedAppDirs( - where home_dir' = "C:\\Users\\" + user_name, - user_data_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - site_data_dirs' = ["C:\\ProgramData\\appdirs"], - user_config_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - site_config_dirs' = ["C:\\ProgramData\\appdirs"], - user_cache_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\Cache", - user_state_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - user_log_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs") + where + home_dir' = u, + user_data_dir' = + u + "\\AppData\\Local\\appdirs", + site_data_dirs' = + ["C:\\ProgramData\\appdirs"], + user_config_dir' = + u + "\\AppData\\Local\\appdirs", + site_config_dirs' = + ["C:\\ProgramData\\appdirs"], + user_cache_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\Cache", + user_state_dir' = + u + "\\AppData\\Local\\appdirs", + user_log_dir' = + u + "\\AppData\\Local\\appdirs") else _ExpectedAppDirs( - where home_dir' = _ExpectError, - user_data_dir' = _ExpectError, - site_data_dirs' = ["/usr/local/share/appdirs"; "/usr/share/appdirs"], - user_config_dir' = _ExpectError, - site_config_dirs' = ["/etc/xdg/appdirs"], - user_cache_dir' = _ExpectError, - user_state_dir' = _ExpectError, - user_log_dir' = _ExpectError) + where + home_dir' = _ExpectError, + user_data_dir' = _ExpectError, + site_data_dirs' = + [ "/usr/local/share/appdirs" + "/usr/share/appdirs" + ], + user_config_dir' = _ExpectError, + site_config_dirs' = + ["/etc/xdg/appdirs"], + user_cache_dir' = _ExpectError, + user_state_dir' = _ExpectError, + user_log_dir' = _ExpectError) end - _AppDirsTestUtil.test(app_dirs, expected, h)? + _AppDirsTestUtil.test( + app_dirs, expected, h)? class \nodoc\ _AppDirsAppAuthorTest is UnitTest fun name(): String => "appdirs/app_author" fun apply(h: TestHelper) ? => - let env_vars = [ - as String: "HOME=" + _AppDirsTestUtil.test_home() - ] - let app_dirs = AppDirs(env_vars, "appdirs", "Matthias Wahl") + let env_vars = + [ as String: + "HOME=" + _AppDirsTestUtil.test_home() + ] + let app_dirs = + AppDirs(env_vars, "appdirs", "Matthias Wahl") let expected = ifdef osx then _ExpectedAppDirs( - where home_dir' = "/Users/ed", - user_data_dir' = "/Users/ed/Library/Application Support/appdirs", - site_data_dirs' = ["/Library/Application Support/appdirs"], - user_config_dir' = "/Users/ed/Library/Preferences/appdirs", - site_config_dirs' = ["/Library/Preferences/appdirs"], - user_cache_dir' = "/Users/ed/Library/Caches/appdirs", - user_state_dir' = "/Users/ed/Library/Application Support/appdirs", - user_log_dir' = "/Users/ed/Library/Logs/appdirs") + where + home_dir' = "/Users/ed", + user_data_dir' = + "/Users/ed/Library" + + "/Application Support/appdirs", + site_data_dirs' = + [ "/Library" + + "/Application Support/appdirs" + ], + user_config_dir' = + "/Users/ed/Library" + + "/Preferences/appdirs", + site_config_dirs' = + ["/Library/Preferences/appdirs"], + user_cache_dir' = + "/Users/ed/Library/Caches/appdirs", + user_state_dir' = + "/Users/ed/Library" + + "/Application Support/appdirs", + user_log_dir' = + "/Users/ed/Library/Logs/appdirs") elseif windows then - let user_name = EnvVars(h.env.vars)("USERNAME")? // hack for getting the username + // hack for getting the username + let user_name = + EnvVars(h.env.vars)("USERNAME")? + let u: String val = + "C:\\Users\\" + user_name _ExpectedAppDirs( - where home_dir' = "C:\\Users\\" + user_name, - user_data_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\Matthias Wahl\\appdirs", - site_data_dirs' = ["C:\\ProgramData\\Matthias Wahl\\appdirs"], - user_config_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\Matthias Wahl\\appdirs", - site_config_dirs' = ["C:\\ProgramData\\Matthias Wahl\\appdirs"], - user_cache_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\Matthias Wahl\\appdirs\\Cache", - user_state_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\Matthias Wahl\\appdirs", - user_log_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\Matthias Wahl\\appdirs") + where + home_dir' = u, + user_data_dir' = + u + + "\\AppData\\Local" + + "\\Matthias Wahl\\appdirs", + site_data_dirs' = + [ "C:\\ProgramData" + + "\\Matthias Wahl\\appdirs" + ], + user_config_dir' = + u + + "\\AppData\\Local" + + "\\Matthias Wahl\\appdirs", + site_config_dirs' = + [ "C:\\ProgramData" + + "\\Matthias Wahl\\appdirs" + ], + user_cache_dir' = + u + + "\\AppData\\Local" + + "\\Matthias Wahl" + + "\\appdirs\\Cache", + user_state_dir' = + u + + "\\AppData\\Local" + + "\\Matthias Wahl\\appdirs", + user_log_dir' = + u + + "\\AppData\\Local" + + "\\Matthias Wahl\\appdirs") else _ExpectedAppDirs( - where home_dir' = "/home/ed", - user_data_dir' = "/home/ed/.local/share/appdirs", - site_data_dirs' = ["/usr/local/share/appdirs"; "/usr/share/appdirs"], - user_config_dir' = "/home/ed/.config/appdirs", - site_config_dirs' = ["/etc/xdg/appdirs"], - user_cache_dir' = "/home/ed/.cache/appdirs", - user_state_dir' = "/home/ed/.local/state/appdirs", - user_log_dir' = "/home/ed/.cache/appdirs/log") + where + home_dir' = "/home/ed", + user_data_dir' = + "/home/ed/.local/share/appdirs", + site_data_dirs' = + [ "/usr/local/share/appdirs" + "/usr/share/appdirs" + ], + user_config_dir' = + "/home/ed/.config/appdirs", + site_config_dirs' = + ["/etc/xdg/appdirs"], + user_cache_dir' = + "/home/ed/.cache/appdirs", + user_state_dir' = + "/home/ed/.local/state/appdirs", + user_log_dir' = + "/home/ed/.cache/appdirs/log") end - _AppDirsTestUtil.test(app_dirs, expected, h)? - + _AppDirsTestUtil.test( + app_dirs, expected, h)? class \nodoc\ _AppDirsWindowsRoamingTest is UnitTest fun name(): String => "appdirs/roaming" fun apply(h: TestHelper) ? => - let env_vars = [ - as String: "HOME=" + _AppDirsTestUtil.test_home() - ] - let app_dirs = AppDirs(env_vars, "appdirs" where roaming = true) + let env_vars = + [ as String: + "HOME=" + _AppDirsTestUtil.test_home() + ] + let app_dirs = + AppDirs( + env_vars, "appdirs" + where roaming = true) let expected = ifdef osx then _ExpectedAppDirs( - where home_dir' = "/Users/ed", - user_data_dir' = "/Users/ed/Library/Application Support/appdirs", - site_data_dirs' = ["/Library/Application Support/appdirs"], - user_config_dir' = "/Users/ed/Library/Preferences/appdirs", - site_config_dirs' = ["/Library/Preferences/appdirs"], - user_cache_dir' = "/Users/ed/Library/Caches/appdirs", - user_state_dir' = "/Users/ed/Library/Application Support/appdirs", - user_log_dir' = "/Users/ed/Library/Logs/appdirs") + where + home_dir' = "/Users/ed", + user_data_dir' = + "/Users/ed/Library" + + "/Application Support/appdirs", + site_data_dirs' = + [ "/Library" + + "/Application Support/appdirs" + ], + user_config_dir' = + "/Users/ed/Library" + + "/Preferences/appdirs", + site_config_dirs' = + ["/Library/Preferences/appdirs"], + user_cache_dir' = + "/Users/ed/Library/Caches/appdirs", + user_state_dir' = + "/Users/ed/Library" + + "/Application Support/appdirs", + user_log_dir' = + "/Users/ed/Library/Logs/appdirs") elseif windows then - // this is valid for windows 7 and newer (tested with windows 10) - let user_name = EnvVars(h.env.vars)("USERNAME")? // hack for getting the username + // valid for windows 7 and newer + // (tested with windows 10) + // hack for getting the username + let user_name = + EnvVars(h.env.vars)("USERNAME")? + let u: String val = + "C:\\Users\\" + user_name _ExpectedAppDirs( - where home_dir' = "C:\\Users\\" + user_name, - user_data_dir' = "C:\\Users\\" + user_name + "\\AppData\\Roaming\\appdirs", - site_data_dirs' = ["C:\\ProgramData\\appdirs"], - user_config_dir' = "C:\\Users\\" + user_name + "\\AppData\\Roaming\\appdirs", - site_config_dirs' = ["C:\\ProgramData\\appdirs"], - user_cache_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\Cache", - user_state_dir' = "C:\\Users\\" + user_name + "\\AppData\\Roaming\\appdirs", - user_log_dir' = "C:\\Users\\" + user_name + "\\AppData\\Roaming\\appdirs") + where + home_dir' = u, + user_data_dir' = + u + "\\AppData\\Roaming\\appdirs", + site_data_dirs' = + ["C:\\ProgramData\\appdirs"], + user_config_dir' = + u + "\\AppData\\Roaming\\appdirs", + site_config_dirs' = + ["C:\\ProgramData\\appdirs"], + user_cache_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\Cache", + user_state_dir' = + u + "\\AppData\\Roaming\\appdirs", + user_log_dir' = + u + "\\AppData\\Roaming\\appdirs") else _ExpectedAppDirs( - where home_dir' = "/home/ed", - user_data_dir' = "/home/ed/.local/share/appdirs", - site_data_dirs' = ["/usr/local/share/appdirs"; "/usr/share/appdirs"], - user_config_dir' = "/home/ed/.config/appdirs", - site_config_dirs' = ["/etc/xdg/appdirs"], - user_cache_dir' = "/home/ed/.cache/appdirs", - user_state_dir' = "/home/ed/.local/state/appdirs", - user_log_dir' = "/home/ed/.cache/appdirs/log") + where + home_dir' = "/home/ed", + user_data_dir' = + "/home/ed/.local/share/appdirs", + site_data_dirs' = + [ "/usr/local/share/appdirs" + "/usr/share/appdirs" + ], + user_config_dir' = + "/home/ed/.config/appdirs", + site_config_dirs' = + ["/etc/xdg/appdirs"], + user_cache_dir' = + "/home/ed/.cache/appdirs", + user_state_dir' = + "/home/ed/.local/state/appdirs", + user_log_dir' = + "/home/ed/.cache/appdirs/log") end - _AppDirsTestUtil.test(app_dirs, expected, h)? - + _AppDirsTestUtil.test( + app_dirs, expected, h)? class \nodoc\ _AppDirsUnixXDGVarsTest is UnitTest fun name(): String => "appdirs/xdg_vars" fun apply(h: TestHelper) ? => - let env_vars = [ - "HOME=" + _AppDirsTestUtil.test_home() - "XDG_DATA_HOME=~/.my_own/data" - "XDG_DATA_DIRS=/etc/bla/:/blubb" - "XDG_CONFIG_HOME=/home/ed/.my_config" - "XDG_CONFIG_DIRS=/config/a b c" - "XDG_CACHE_HOME=~/.kache" - "XDG_STATE_HOME=~/~/~/.state" - ] - let app_dirs = AppDirs(env_vars, "appdirs") + let env_vars = + [ "HOME=" + _AppDirsTestUtil.test_home() + "XDG_DATA_HOME=~/.my_own/data" + "XDG_DATA_DIRS=/etc/bla/:/blubb" + "XDG_CONFIG_HOME=/home/ed/.my_config" + "XDG_CONFIG_DIRS=/config/a b c" + "XDG_CACHE_HOME=~/.kache" + "XDG_STATE_HOME=~/~/~/.state" + ] + let app_dirs = + AppDirs(env_vars, "appdirs") let expected = ifdef osx then _ExpectedAppDirs( - where home_dir' = "/Users/ed", - user_data_dir' = "/Users/ed/Library/Application Support/appdirs", - site_data_dirs' = ["/Library/Application Support/appdirs"], - user_config_dir' = "/Users/ed/Library/Preferences/appdirs", - site_config_dirs' = ["/Library/Preferences/appdirs"], - user_cache_dir' = "/Users/ed/Library/Caches/appdirs", - user_state_dir' = "/Users/ed/Library/Application Support/appdirs", - user_log_dir' = "/Users/ed/Library/Logs/appdirs") + where + home_dir' = "/Users/ed", + user_data_dir' = + "/Users/ed/Library" + + "/Application Support/appdirs", + site_data_dirs' = + [ "/Library" + + "/Application Support/appdirs" + ], + user_config_dir' = + "/Users/ed/Library" + + "/Preferences/appdirs", + site_config_dirs' = + ["/Library/Preferences/appdirs"], + user_cache_dir' = + "/Users/ed/Library/Caches/appdirs", + user_state_dir' = + "/Users/ed/Library" + + "/Application Support/appdirs", + user_log_dir' = + "/Users/ed/Library/Logs/appdirs") elseif windows then // no impact on windows AFAIK - let user_name = EnvVars(h.env.vars)("USERNAME")? // hack for getting the username + // hack for getting the username + let user_name = + EnvVars(h.env.vars)("USERNAME")? + let u: String val = + "C:\\Users\\" + user_name _ExpectedAppDirs( - where home_dir' = "C:\\Users\\" + user_name, - user_data_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - site_data_dirs' = ["C:\\ProgramData\\appdirs"], - user_config_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - site_config_dirs' = ["C:\\ProgramData\\appdirs"], - user_cache_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs\\Cache", - user_state_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs", - user_log_dir' = "C:\\Users\\" + user_name + "\\AppData\\Local\\appdirs") + where + home_dir' = u, + user_data_dir' = + u + "\\AppData\\Local\\appdirs", + site_data_dirs' = + ["C:\\ProgramData\\appdirs"], + user_config_dir' = + u + "\\AppData\\Local\\appdirs", + site_config_dirs' = + ["C:\\ProgramData\\appdirs"], + user_cache_dir' = + u + + "\\AppData\\Local" + + "\\appdirs\\Cache", + user_state_dir' = + u + "\\AppData\\Local\\appdirs", + user_log_dir' = + u + "\\AppData\\Local\\appdirs") else _ExpectedAppDirs( - where home_dir' = "/home/ed", - user_data_dir' = "/home/ed/.my_own/data/appdirs", - site_data_dirs' = ["/etc/bla/appdirs"; "/blubb/appdirs"], - user_config_dir' = "/home/ed/.my_config/appdirs", - site_config_dirs' = ["/config/a b c/appdirs"], - user_cache_dir' = "/home/ed/.kache/appdirs", - user_state_dir' = "/home/ed/home/ed/home/ed/.state/appdirs", - user_log_dir' = "/home/ed/.kache/appdirs/log") + where + home_dir' = "/home/ed", + user_data_dir' = + "/home/ed/.my_own/data/appdirs", + site_data_dirs' = + [ "/etc/bla/appdirs" + "/blubb/appdirs" + ], + user_config_dir' = + "/home/ed/.my_config/appdirs", + site_config_dirs' = + ["/config/a b c/appdirs"], + user_cache_dir' = + "/home/ed/.kache/appdirs", + user_state_dir' = + "/home/ed/home/ed/home/ed" + + "/.state/appdirs", + user_log_dir' = + "/home/ed/.kache/appdirs/log") end - _AppDirsTestUtil.test(app_dirs, expected, h)? + _AppDirsTestUtil.test( + app_dirs, expected, h)? class \nodoc\ _AppDirsOsxAsUnixTest is UnitTest fun name(): String => "appdirs/osx_as_unix" - fun apply(h: TestHelper) ? => - let env_vars = [ - "HOME=" + _AppDirsTestUtil.test_home() - "XDG_DATA_HOME=~/.my_own/data" - "XDG_DATA_DIRS=/etc/bla/:/blubb" - "XDG_CONFIG_HOME=/Users/ed/.my_config" - "XDG_CONFIG_DIRS=/config/a b c" - "XDG_CACHE_HOME=~/.kache" - "XDG_STATE_HOME=~/~/~/.state" - ] - let app_dirs = AppDirs(env_vars, "appdirs" where osx_as_unix = true) + fun apply(h: TestHelper) ? => + let env_vars = + [ "HOME=" + _AppDirsTestUtil.test_home() + "XDG_DATA_HOME=~/.my_own/data" + "XDG_DATA_DIRS=/etc/bla/:/blubb" + "XDG_CONFIG_HOME=/Users/ed/.my_config" + "XDG_CONFIG_DIRS=/config/a b c" + "XDG_CACHE_HOME=~/.kache" + "XDG_STATE_HOME=~/~/~/.state" + ] + + let app_dirs = + AppDirs( + env_vars, "appdirs" + where osx_as_unix = true) ifdef osx then - let expected = _ExpectedAppDirs( - where home_dir' = _AppDirsTestUtil.test_home(), - user_data_dir' = "/Users/ed/.my_own/data/appdirs", - site_data_dirs' = ["/etc/bla/appdirs"; "/blubb/appdirs"], - user_config_dir' = "/Users/ed/.my_config/appdirs", - site_config_dirs' = ["/config/a b c/appdirs"], - user_cache_dir' = "/Users/ed/.kache/appdirs", - user_state_dir' = "/Users/ed/Users/ed/Users/ed/.state/appdirs", - user_log_dir' = "/Users/ed/.kache/appdirs/log") - _AppDirsTestUtil.test(app_dirs, expected, h)? + let expected = + _ExpectedAppDirs( + where + home_dir' = + _AppDirsTestUtil.test_home(), + user_data_dir' = + "/Users/ed/.my_own/data/appdirs", + site_data_dirs' = + [ "/etc/bla/appdirs" + "/blubb/appdirs" + ], + user_config_dir' = + "/Users/ed/.my_config/appdirs", + site_config_dirs' = + ["/config/a b c/appdirs"], + user_cache_dir' = + "/Users/ed/.kache/appdirs", + user_state_dir' = + "/Users/ed/Users/ed/Users/ed" + + "/.state/appdirs", + user_log_dir' = + "/Users/ed/.kache/appdirs/log") + _AppDirsTestUtil.test( + app_dirs, expected, h)? end - - diff --git a/appdirs/appdirs.pony b/appdirs/appdirs.pony index abfb584..d47f578 100644 --- a/appdirs/appdirs.pony +++ b/appdirs/appdirs.pony @@ -1,9 +1,12 @@ """ # AppDirs -Library for detecting platform specific user directories e.g. for data, config, cache, logs. +Library for detecting platform specific user +directories e.g. for data, config, cache, logs. -Most stuff is copied from the python library [appdirs](https://github.com/ActiveState/appdirs) from ActiveState. +Most stuff is copied from the python library +[appdirs](https://github.com/ActiveState/appdirs) +from ActiveState. """ use "files" use "cli" // for EnvVars @@ -15,12 +18,20 @@ primitive _OsxAsUnix primitive _Unix primitive _Windows -type _Platform is (_Osx | _OsxAsUnix | _Unix | _Windows) +type _Platform is + (_Osx | _OsxAsUnix | _Unix | _Windows) primitive Paths + """ + Utility for joining sequences of path segments. + """ fun join(paths: ReadSeq[String]): String => + """ + Join a sequence of path segments into a single path. + """ Iter[String](paths.values()) - .fold[String]("", {(acc, cur) => Path.join(acc, cur)}) + .fold[String]( + "", {(acc, cur) => Path.join(acc, cur) }) type _Maybe[T] is (T | None) @@ -32,6 +43,15 @@ primitive _Opt end class AppDirs + """ + Detects platform-specific user directories for + data, config, cache, logs, and state. + + Provides methods that return the appropriate + directory paths for the current platform (Windows, + macOS, or Linux/Unix) based on the application name + and optional author, version, and roaming settings. + """ let _home: _Maybe[String] let _env_vars: Map[String, String] val let _app_name: String @@ -49,15 +69,20 @@ class AppDirs osx_as_unix: Bool = false) => """ - An AppDirs instance derives the platform specific directories - from the process environment variables. - It will return appdirs for the user of the current process. + An AppDirs instance derives the platform specific + directories from the process environment variables. + It will return appdirs for the user of the current + process. Creating appdirs requires: - - passing environment variables, only on unix and osx systems. - - a name of the app you want to get directories for - - optionally: a name of the app author (be it a company or a person) - - optionally: a version of your app, if you want to separate directories also by version + - passing environment variables, only on unix + and osx systems. + - a name of the app you want to get directories + for + - optionally: a name of the app author (be it a + company or a person) + - optionally: a version of your app, if you want + to separate directories also by version """ _env_vars = EnvVars(env_vars) _app_name = app_name @@ -65,34 +90,50 @@ class AppDirs _app_version = app_version _roaming = roaming - _platform = ifdef windows then - _Windows - elseif osx then - if osx_as_unix then _OsxAsUnix else _Osx end - else - _Unix - end + _platform = + ifdef windows then + _Windows + elseif osx then + if osx_as_unix then + _OsxAsUnix + else + _Osx + end + else + _Unix + end - _home = ifdef windows then - try KnownFolders(KnownFolderIds.profile())? end - else - try _env_vars("HOME")? end - end + _home = + ifdef windows then + try KnownFolders(KnownFolderIds.profile())? end + else + try _env_vars("HOME")? end + end fun _expand_user(path: String): String ? => - (_home as String).join(path.split_by("~").values()) + (_home as String) + .join(path.split_by("~").values()) fun user_home_dir(): String ? => + """ + Return the user's home directory path. + """ _home as String fun user_data_dir(): String ? => """ - Returns the full path to the user-specific data dir for this application. + Return the full path to the user-specific data + dir for this application. """ let os_specific_dir = match _platform | _Osx => - Paths.join([_home as String; "Library"; "Application Support"; _app_name]) + Paths.join( + [ _home as String + "Library" + "Application Support" + _app_name + ]) | _OsxAsUnix | _Unix => _unix_user_data_dir()? | _Windows => @@ -103,11 +144,11 @@ class AppDirs else KnownFolderIds.app_data_local() end - Paths.join([ - KnownFolders(folder_id)? - _Opt.get[String](_app_author, "") - _app_name - ]) + Paths.join( + [ KnownFolders(folder_id)? + _Opt.get[String](_app_author, "") + _app_name + ]) else "" end @@ -121,65 +162,91 @@ class AppDirs let base_path = _env_vars.get_or_else( "XDG_DATA_HOME", - Paths.join([_home as String; ".local"; "share"])) + Paths.join( + [_home as String; ".local"; "share"])) + // expand '~' to the user's home directory Path.join( - _expand_user(base_path)?, // make sure any '~' get replaced by the users home directory + _expand_user(base_path)?, _app_name) fun site_data_dirs(): Array[String] val ? => """ - Returns an array of full paths to the user-shared data dirs for this application. + Return an array of full paths to the user-shared + data dirs for this application. """ let os_specific_dirs: Array[String] iso = match _platform | _Osx => - recover [Path.join("/Library/Application Support", _app_name)] end + recover + [ Path.join( + "/Library/Application Support", + _app_name) + ] + end | _OsxAsUnix | _Unix => _unix_site_data_dirs()? | _Windows => ifdef windows then recover - [ - Paths.join([ - KnownFolders(KnownFolderIds.program_data())? - _Opt.get[String](_app_author, "") - _app_name - ]) - ] end + [ Paths.join( + [ KnownFolders( + KnownFolderIds.program_data())? + _Opt.get[String](_app_author, "") + _app_name + ]) + ] + end else recover Array[String] end end end match _app_version | let v: String => - for i in Range[USize](0, os_specific_dirs.size()) do - os_specific_dirs(i)? = Path.join(os_specific_dirs(i)?, v) + for i in Range[USize]( + 0, os_specific_dirs.size()) + do + os_specific_dirs(i)? = + Path.join(os_specific_dirs(i)?, v) end end os_specific_dirs - fun _unix_site_data_dirs(): Array[String] iso^ ? => + fun _unix_site_data_dirs() + : Array[String] iso^ ? + => let data_dirs: Array[String] iso = try - Path.split_list(_env_vars("XDG_DATA_DIRS")?) + Path.split_list( + _env_vars("XDG_DATA_DIRS")?) else // fallback default dirs - recover iso ["/usr/local/share"; "/usr/share"] end + recover iso + ["/usr/local/share"; "/usr/share"] + end end - for idx in Range[USize](0, data_dirs.size()) do - data_dirs(idx)? = Path.join(data_dirs(idx)?, _app_name) + for idx in Range[USize]( + 0, data_dirs.size()) + do + data_dirs(idx)? = + Path.join(data_dirs(idx)?, _app_name) end consume data_dirs fun user_config_dir(): String ? => """ - Return full path to the user-specific config dir for this application. + Return full path to the user-specific config dir + for this application. """ let os_specific_dir = match _platform | _Osx => - Paths.join([_home as String; "Library"; "Preferences"; _app_name]) + Paths.join( + [ _home as String + "Library" + "Preferences" + _app_name + ]) | _OsxAsUnix | _Unix => _unix_user_config_dir()? | _Windows => @@ -189,7 +256,8 @@ class AppDirs // apply version match _app_version | None => os_specific_dir - | let v: String => Path.join(os_specific_dir, v) + | let v: String => + Path.join(os_specific_dir, v) end fun _unix_user_config_dir(): String ? => @@ -203,12 +271,18 @@ class AppDirs fun site_config_dirs(): Array[String] val ? => """ - Return full path to the user-shared config dirs for this application. + Return full path to the user-shared config dirs + for this application. """ let os_specific_dirs: Array[String] iso = match _platform | _Osx => - recover [Path.join("/Library/Preferences", _app_name)] end + recover + [ Path.join( + "/Library/Preferences", + _app_name) + ] + end | _OsxAsUnix | _Unix => _unix_site_config_dirs()? | _Windows => @@ -217,46 +291,62 @@ class AppDirs match _app_version | let v: String => - for i in Range[USize](0, os_specific_dirs.size()) do - os_specific_dirs(i)? = Path.join(os_specific_dirs(i)?, v) + for i in Range[USize]( + 0, os_specific_dirs.size()) + do + os_specific_dirs(i)? = + Path.join(os_specific_dirs(i)?, v) end end os_specific_dirs - fun _unix_site_config_dirs(): Array[String] iso^ ? => + fun _unix_site_config_dirs() + : Array[String] iso^ ? + => let config_dirs: Array[String] iso = try - Path.split_list(_env_vars("XDG_CONFIG_DIRS")?) + Path.split_list( + _env_vars("XDG_CONFIG_DIRS")?) else // fallback default dirs recover iso ["/etc/xdg"] end end - for idx in Range[USize](0, config_dirs.size()) do - config_dirs(idx)? = Path.join(config_dirs(idx)?, _app_name) + for idx in Range[USize]( + 0, config_dirs.size()) + do + config_dirs(idx)? = + Path.join(config_dirs(idx)?, _app_name) end consume config_dirs fun user_cache_dir(): String ? => """ - Return full path to the user-specific cache dir for this application. + Return full path to the user-specific cache dir + for this application. """ let os_specific_dir = match _platform | _Osx => - Paths.join([_home as String; "Library"; "Caches"; _app_name]) + Paths.join( + [ _home as String + "Library" + "Caches" + _app_name + ]) | _OsxAsUnix | _Unix => _unix_user_cache_dir()? | _Windows => ifdef windows then let known_folder: String val = - KnownFolders(KnownFolderIds.app_data_local())? - Paths.join([ - known_folder - _Opt.get[String](_app_author, "") - _app_name - "Cache" - ]) + KnownFolders( + KnownFolderIds.app_data_local())? + Paths.join( + [ known_folder + _Opt.get[String](_app_author, "") + _app_name + "Cache" + ]) else "" end @@ -268,14 +358,18 @@ class AppDirs fun _unix_user_cache_dir(): String ? => Path.join( - _expand_user(_env_vars.get_or_else("XDG_CACHE_HOME", "~/.cache"))?, + _expand_user( + _env_vars.get_or_else( + "XDG_CACHE_HOME", "~/.cache"))?, _app_name) fun user_state_dir(): String ? => """ - Return full path to the user-specific state dir for this application. + Return full path to the user-specific state dir + for this application. - See https://wiki.debian.org/XDGBaseDirectorySpecification#state + See: + https://wiki.debian.org/XDGBaseDirectorySpecification#state """ match _platform | _Osx | _Windows => @@ -285,28 +379,29 @@ class AppDirs end fun _unix_user_state_dir(): String ? => - Paths.join([ - _expand_user( - _env_vars.get_or_else( - "XDG_STATE_HOME", - "~/.local/state"))? - _app_name - _Opt.get[String](_app_version, "") - ]) + Paths.join( + [ _expand_user( + _env_vars.get_or_else( + "XDG_STATE_HOME", + "~/.local/state"))? + _app_name + _Opt.get[String](_app_version, "") + ]) fun user_log_dir(): String ? => """ - Return full path to the user-specific log dir for this application. + Return full path to the user-specific log dir + for this application. """ match _platform | _Osx => - Paths.join([ - _home as String - "Library" - "Logs" - _app_name - _Opt.get[String](_app_version, "") - ]) + Paths.join( + [ _home as String + "Library" + "Logs" + _app_name + _Opt.get[String](_app_version, "") + ]) | _OsxAsUnix | _Unix => _unix_user_log_dir()? | _Windows => @@ -317,5 +412,3 @@ class AppDirs Path.join( user_cache_dir()?, "log") - - diff --git a/appdirs/known_folders.pony b/appdirs/known_folders.pony index a3fc2ad..7ada622 100644 --- a/appdirs/known_folders.pony +++ b/appdirs/known_folders.pony @@ -2,64 +2,107 @@ use "lib:shell32" if windows use "lib:ole32" if windows use "debug" -use @SHGetKnownFolderPath[U32](rfid: Pointer[U8] tag, flags: U32, - token: Pointer[U32], path: Pointer[Pointer[U16]]) if windows -use @WideCharToMultiByte[I32](code_page: U32, flags: U32, char_str: Pointer[U16], - char_str_size: I32, multi_byte_str: Pointer[U8] tag, multi_byte_str_size: I32, - default_char: Pointer[U8], used_default_char: Pointer[U8]) if windows -use @CoTaskMemFree[None](pv: Pointer[U16] tag) if windows +use @SHGetKnownFolderPath[U32]( + rfid: Pointer[U8] tag, flags: U32, + token: Pointer[U32], + path: Pointer[Pointer[U16]]) if windows +use @WideCharToMultiByte[I32]( + code_page: U32, flags: U32, + char_str: Pointer[U16], + char_str_size: I32, + multi_byte_str: Pointer[U8] tag, + multi_byte_str_size: I32, + default_char: Pointer[U8], + used_default_char: Pointer[U8]) if windows +use @CoTaskMemFree[None]( + pv: Pointer[U16] tag) if windows primitive KnownFolderIds """ Known folder ids as described in: https://docs.microsoft.com/en-ca/windows/desktop/shell/knownfolderid - functions return the little endian byte values of the folderid GUIDs + + Functions return the little endian byte values of the + folderid GUIDs. """ - fun profile(): Array[U8] val => - """ - The user's profile folder. A typical path is C:\Users\username. - Applications should not create files or folders at this level; - they should put their data under the locations referred to by CSIDL_APPDATA or CSIDL_LOCAL_APPDATA. - However, if you are creating a new Known Folder the profile root referred to by CSIDL_PROFILE is appropriate. + fun profile(): Array[U8] val => + """ + The user's profile folder. + A typical path is C:\Users\username. + Applications should not create files or folders at + this level; they should put their data under the + locations referred to by CSIDL_APPDATA or + CSIDL_LOCAL_APPDATA. + However, if you are creating a new Known Folder + the profile root referred to by CSIDL_PROFILE + is appropriate. - FOLDERID_Profile - 5E6C858F-0E22-4760-9AFE-EA3317B67173 - """ - [as U8: 0x8f; 0x85; 0x6c; 0x5e; 0x22; 0x0e; 0x60; 0x47; 0x9A; 0xFE; 0xEA; 0x33; 0x17; 0xB6; 0x71; 0x73] + FOLDERID_Profile + 5E6C858F-0E22-4760-9AFE-EA3317B67173 + """ + [ as U8: + 0x8f; 0x85; 0x6c; 0x5e + 0x22; 0x0e; 0x60; 0x47 + 0x9A; 0xFE; 0xEA; 0x33 + 0x17; 0xB6; 0x71; 0x73 + ] - fun app_data_roaming(): Array[U8] val => - """ - The file system directory that serves as a common repository for application-specific data. - A typical path is C:\Documents and Settings\username\Application Data. + fun app_data_roaming(): Array[U8] val => + """ + The file system directory that serves as a common + repository for application-specific data. + A typical path is + C:\Documents and Settings\username\Application Data. - KNOWNFOLDERID: FOLDERID_RoamingAppData - GUID: 3EB685DB-65F9-4CF6-A03A-E3EF65729F3D - """ - [as U8: 0xdb; 0x85; 0xb6; 0x3e; 0xf9; 0x65; 0xf6; 0x4c; 0xa0; 0x3a; 0xe3; 0xef; 0x65; 0x72; 0x9f; 0x3d] + KNOWNFOLDERID: FOLDERID_RoamingAppData + GUID: 3EB685DB-65F9-4CF6-A03A-E3EF65729F3D + """ + [ as U8: + 0xdb; 0x85; 0xb6; 0x3e + 0xf9; 0x65; 0xf6; 0x4c + 0xa0; 0x3a; 0xe3; 0xef + 0x65; 0x72; 0x9f; 0x3d + ] - fun app_data_local(): Array[U8] val => - """ - The file system directory that serves as a data repository for local (nonroaming) applications. - A typical path is C:\Documents and Settings\username\Local Settings\Application Data. + fun app_data_local(): Array[U8] val => + """ + The file system directory that serves as a data + repository for local (nonroaming) applications. + A typical path is C:\Documents and + Settings\username\Local Settings\Application Data. - FOLDERID_LocalAppData - F1B32785-6FBA-4FCF-9D55-7B8E7F157091 - """ - [as U8: 0x85; 0x27; 0xb3; 0xf1; 0xba; 0x6f; 0xcf; 0x4f; 0x9d; 0x55; 0x7b; 0x8e; 0x7f; 0x15; 0x70; 0x91] + FOLDERID_LocalAppData + F1B32785-6FBA-4FCF-9D55-7B8E7F157091 + """ + [ as U8: + 0x85; 0x27; 0xb3; 0xf1 + 0xba; 0x6f; 0xcf; 0x4f + 0x9d; 0x55; 0x7b; 0x8e + 0x7f; 0x15; 0x70; 0x91 + ] - fun program_data(): Array[U8] val => - """ - The file system directory that contains application data for all users. - A typical path is C:\Documents and Settings\All Users\Application Data. - This folder is used for application data that is not user specific. - For example, an application can store a spell-check dictionary, - a database of clip art, or a log file in the CSIDL_COMMON_APPDATA folder. - This information will not roam and is available to anyone using the computer. + fun program_data(): Array[U8] val => + """ + The file system directory that contains application + data for all users. A typical path is + C:\Documents and Settings\All + Users\Application Data. This folder is used for + application data that is not user specific. For + example, an application can store a spell-check + dictionary, a database of clip art, or a log file + in the CSIDL_COMMON_APPDATA folder. This + information will not roam and is available to anyone + using the computer. - FOLDERID_ProgramData - 62AB5D82-FDC1-4DC3-A9DD-070D1D495D97 - """ - [as U8: 0x82; 0x5d; 0xab; 0x62; 0xc1; 0xfd; 0xc3; 0x4d; 0xa9; 0xdd; 0x07; 0x0d; 0x1d; 0x49; 0x5d; 0x97] + FOLDERID_ProgramData + 62AB5D82-FDC1-4DC3-A9DD-070D1D495D97 + """ + [ as U8: + 0x82; 0x5d; 0xab; 0x62 + 0xc1; 0xfd; 0xc3; 0x4d + 0xa9; 0xdd; 0x07; 0x0d + 0x1d; 0x49; 0x5d; 0x97 + ] primitive KnownFolders """ @@ -68,6 +111,12 @@ primitive KnownFolders https://docs.microsoft.com/en-ca/windows/desktop/shell/known-folders """ fun apply(folderid: Array[U8] val): String iso^ ? => + """ + Look up a Windows known folder path by its GUID. + + Returns the folder path as a UTF-8 string. Raises + an error if the folder cannot be resolved. + """ ifdef not windows then compile_error "known folders only supported on windows" else @@ -75,42 +124,53 @@ primitive KnownFolders var path_pointer: Pointer[U16] = Pointer[U16] let result: U32 = @SHGetKnownFolderPath( - folderid.cpointer(), // REFKNOWNFOLDERID - U32(0), // retrieval flags -- no flags - Pointer[U32], // some strange handle, pass NULL + folderid.cpointer(), + // no retrieval flags + U32(0), + // token handle, pass NULL + Pointer[U32], addressof path_pointer ) if result != 0 then - Debug("Error getting known folder path: " + result.string()) - error + Debug( + "Error getting known folder path: " + + result.string()) + error end - // extract path from path_pointer - let bytes_necessary: I32 = @WideCharToMultiByte( + // first call to get required buffer size + let bytes_necessary: I32 = + @WideCharToMultiByte( WindowsCodePages.utf8(), U32(0), path_pointer, - I32(-1), // length of path pointer, -1 for null terminated + // -1 = null-terminated input + I32(-1), + Pointer[U8].create(), + // 0 = return required size + I32(0), Pointer[U8].create(), - I32(0), // indicating we want the required bytes back - Pointer[U8].create(), // NULL - Pointer[U8].create() // NULL - ) - let utf8_path: Array[U8] iso = recover Array[U8](bytes_necessary.usize()) end + Pointer[U8].create() + ) + let utf8_path: Array[U8] iso = + recover Array[U8](bytes_necessary.usize()) end utf8_path.undefined(bytes_necessary.usize()) - let convert_result: I32 = @WideCharToMultiByte( + // second call to perform conversion + let convert_result: I32 = + @WideCharToMultiByte( WindowsCodePages.utf8(), U32(0), path_pointer, - I32(-1), // length of path pointer, -1 for null terminated + // -1 = null-terminated input + I32(-1), utf8_path.cpointer(), utf8_path.size().i32(), - Pointer[U8].create(), // NULL - Pointer[U8].create() // NULL - ) + Pointer[U8].create(), + Pointer[U8].create() + ) if convert_result == 0 then - Debug("error converting from wchar to utf8.") - error + Debug("error converting from wchar to utf8.") + error end @CoTaskMemFree(path_pointer) diff --git a/appdirs/windows_code_pages.pony b/appdirs/windows_code_pages.pony new file mode 100644 index 0000000..07148a4 --- /dev/null +++ b/appdirs/windows_code_pages.pony @@ -0,0 +1,9 @@ +primitive WindowsCodePages + """ + Identifiers for Windows codepages for converting + from one encoding to another. + + See: + https://docs.microsoft.com/en-us/windows/desktop/Intl/code-page-identifiers + """ + fun utf8(): U32 => 65001 diff --git a/appdirs/windows_codepages.pony b/appdirs/windows_codepages.pony deleted file mode 100644 index 0f40170..0000000 --- a/appdirs/windows_codepages.pony +++ /dev/null @@ -1,7 +0,0 @@ -primitive WindowsCodePages - """ - identifiers/constant for windows codepages for converting from one encoding to another. - - See: https://docs.microsoft.com/en-us/windows/desktop/Intl/code-page-identifiers - """ - fun utf8(): U32 => 65001 diff --git a/examples/simple-example/main.pony b/examples/simple-example/main.pony new file mode 100644 index 0000000..6bb35d1 --- /dev/null +++ b/examples/simple-example/main.pony @@ -0,0 +1,119 @@ +use "../../appdirs" + +actor Main + new create(env: Env) => + let app_name = "selftest" + print( + AppDirs(env.vars, app_name), + "With Defaults", + env.out) + print( + AppDirs( + env.vars, + app_name, + "Matthias Wahl"), + "With App-Author", + env.out) + print( + AppDirs( + env.vars, + app_name, + "Matthias Wahl", + "0.1"), + "With App-Author and Version", + env.out) + print( + AppDirs(env.vars, app_name, None, "0.1"), + "With Version", + env.out) + print( + AppDirs( + env.vars, + app_name, + "Matthias Wahl", + "0.2" + where roaming = true), + "With Roaming", + env.out) + + fun print( + app_dirs: AppDirs, + title: String, + out: OutStream) + => + """ + Print all directory paths for the given AppDirs + instance under a titled heading. + """ + let title_len = title.size() + let heading = + String.from_array( + recover val + Array[U8].init('=', title_len) + end) + out.print("") + out.print(heading) + out.print(title) + out.print(heading) + out .> write("home = ") + .> write( + try app_dirs.user_home_dir()? + else "ERROR" + end) + .> write("\n") + out .> write("user_data = ") + .> write( + try app_dirs.user_data_dir()? + else "ERROR" + end) + .> write("\n") + out.print("site_data = ") + try + for sd_dir in + app_dirs.site_data_dirs()?.values() + do + out.print("\t" + sd_dir) + end + else + out.print("\tERROR") + end + + out .> write("user_config = ") + .> write( + try app_dirs.user_config_dir()? + else "ERROR" + end) + .> write("\n") + + out.print("site_config = ") + try + for sc_dir in + app_dirs.site_config_dirs()?.values() + do + out.print("\t" + sc_dir) + end + else + out.print("\tERROR") + end + + out .> write("user_cache = ") + .> write( + try app_dirs.user_cache_dir()? + else "ERROR" + end) + .> write("\n") + + out .> write("user_state = ") + .> write( + try app_dirs.user_state_dir()? + else "ERROR" + end) + .> write("\n") + + out .> write("user_log = ") + .> write( + try app_dirs.user_log_dir()? + else "ERROR" + end) + .> write("\n") + out.print("") diff --git a/examples/simple-example/simple-example.pony b/examples/simple-example/simple-example.pony deleted file mode 100644 index ae286d4..0000000 --- a/examples/simple-example/simple-example.pony +++ /dev/null @@ -1,59 +0,0 @@ -use "../../appdirs" - -actor Main - new create(env: Env) => - let app_name = "selftest" - print(AppDirs(env.vars, app_name), "With Defaults", env.out) - print(AppDirs(env.vars, app_name, "Matthias Wahl"), "With App-Author", env.out) - print(AppDirs(env.vars, app_name, "Matthias Wahl", "0.1"), "With App-Author and Version", env.out) - print(AppDirs(env.vars, app_name, None, "0.1"), "With Version", env.out) - print(AppDirs(env.vars, app_name, "Matthias Wahl", "0.2" where roaming=true), "With Roaming", env.out) - - fun print(app_dirs: AppDirs, title: String, out: OutStream) => - let title_len = title.size() - let heading = String.from_array(recover val Array[U8].init('=', title_len) end) - out.print("") - out.print(heading) - out.print(title) - out.print(heading) - out.>write("home = ") - .>write(try app_dirs.user_home_dir()? else "ERROR" end) - .>write("\n") - out.>write("user_data = ") - .>write(try app_dirs.user_data_dir()? else "ERROR" end) - .>write("\n") - out.print("site_data = ") - try - for sd_dir in app_dirs.site_data_dirs()?.values() do - out.print("\t" + sd_dir) - end - else - out.print("\tERROR") - end - - out.>write("user_config = ") - .>write(try app_dirs.user_config_dir()? else "ERROR" end) - .>write("\n") - - out.print("site_config = ") - try - for sc_dir in app_dirs.site_config_dirs()?.values() do - out.print("\t" + sc_dir) - end - else - out.print("\tERROR") - end - - out.>write("user_cache = ") - .>write(try app_dirs.user_cache_dir()? else "ERROR" end) - .>write("\n") - - out.>write("user_state = ") - .>write(try app_dirs.user_state_dir()? else "ERROR" end) - .>write("\n") - - out.>write("user_log = ") - .>write(try app_dirs.user_log_dir()? else "ERROR" end) - .>write("\n") - out.print("") - diff --git a/examples/simple-example/simple_example.pony b/examples/simple-example/simple_example.pony new file mode 100644 index 0000000..011541b --- /dev/null +++ b/examples/simple-example/simple_example.pony @@ -0,0 +1,5 @@ +""" +Demonstrates basic usage of the AppDirs library by +printing all platform-specific directory paths for +various configuration combinations. +"""