diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..c041fca --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[run] +omit = + rose/__main__.py + setup.py diff --git a/.gitignore b/.gitignore index efa7e12..71c5d74 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ __pycache__ .mypy_cache .coverage result +db.sqlite3 +htmlcov diff --git a/README.md b/README.md index 5ef0480..a62c45c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ # rose Rose is a Linux music library manager. + +## Configuration + +Rose reads its configuration from `${XDG_CONFIG_HOME:-$HOME/.config}/rose/config.toml`. + +The configuration parameters, with examples, are: + +```toml +# The directory containing the music to manage. +music_source_dir = "~/.music-src" +# The directory to mount the library's virtual filesystem on. +fuse_mount_dir = "~/music" +``` diff --git a/flake.nix b/flake.nix index 3b3c3f7..6631b4e 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,7 @@ click fuse mutagen + yoyo-migrations ]; dev-deps = with python.pkgs; [ black diff --git a/rose/base_error.py b/rose/base_error.py new file mode 100644 index 0000000..2bcb6c1 --- /dev/null +++ b/rose/base_error.py @@ -0,0 +1,2 @@ +class RoseError(Exception): + pass diff --git a/rose/config.py b/rose/config.py new file mode 100644 index 0000000..335dc9b --- /dev/null +++ b/rose/config.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import os +from dataclasses import dataclass +from pathlib import Path + +import tomllib + +from rose.base_error import RoseError + +CONFIG_HOME = Path(os.environ.get("XDG_CONFIG_HOME", os.environ["HOME"] + "/.config")) +CONFIG_PATH = CONFIG_HOME / "rose" / "config.toml" + + +class ConfigNotFoundError(RoseError): + pass + + +class MissingConfigKeyError(RoseError): + pass + + +@dataclass +class Config: + music_source_dir: Path + fuse_mount_dir: Path + + @classmethod + def read(cls, config_path_override: Path | None = None) -> Config: + cfgpath = config_path_override or CONFIG_PATH + try: + with cfgpath.open("rb") as fp: + data = tomllib.load(fp) + except FileNotFoundError as e: + raise ConfigNotFoundError(f"Configuration file not found ({CONFIG_PATH})") from e + + try: + return cls( + music_source_dir=Path(data["music_source_dir"]).expanduser(), + fuse_mount_dir=Path(data["fuse_mount_dir"]).expanduser(), + ) + except KeyError as e: + raise MissingConfigKeyError(f"Missing key in configuration file: {e}") from e diff --git a/rose/config_test.py b/rose/config_test.py new file mode 100644 index 0000000..7f16c24 --- /dev/null +++ b/rose/config_test.py @@ -0,0 +1,39 @@ +import os +import tempfile +from pathlib import Path + +import pytest + +from rose.config import Config, ConfigNotFoundError, MissingConfigKeyError + + +def test_config() -> None: + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) / "config.toml" + with path.open("w") as fp: + fp.write( + """\ + music_source_dir = "~/.music-src" + fuse_mount_dir = "~/music" + """ + ) + + c = Config.read(config_path_override=path) + assert str(c.music_source_dir) == os.environ["HOME"] + "/.music-src" + assert str(c.fuse_mount_dir) == os.environ["HOME"] + "/music" + + +def test_config_not_found() -> None: + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) / "config.toml" + with pytest.raises(ConfigNotFoundError): + Config.read(config_path_override=path) + + +def test_config_missing_key() -> None: + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) / "config.toml" + path.touch() + with pytest.raises(MissingConfigKeyError) as excinfo: + Config.read(config_path_override=path) + assert str(excinfo.value) == "Missing key in configuration file: 'music_source_dir'" diff --git a/rose/hello_test.py b/rose/hello_test.py deleted file mode 100644 index acba087..0000000 --- a/rose/hello_test.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_hello(): - assert 1 == 1 diff --git a/setup.py b/setup.py index 17f6236..1252648 100644 --- a/setup.py +++ b/setup.py @@ -13,5 +13,6 @@ "click", "fuse-python", "mutagen", + "yoyo-migrations", ], ) diff --git a/yoyo.ini b/yoyo.ini new file mode 100644 index 0000000..bd04938 --- /dev/null +++ b/yoyo.ini @@ -0,0 +1,8 @@ +# This is for development use only. + +[DEFAULT] +sources = migrations +migration_table = _yoyo_migration +batch_mode = off +verbosity = 0 +database = sqlite:///db.sqlite3