45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from dataclasses import dataclass
|
|
from typing import Iterable
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class VaultPaths:
|
|
root: str
|
|
notes: str
|
|
attachments: str
|
|
kb: str
|
|
|
|
|
|
class Vault:
|
|
def __init__(self, root_path: str):
|
|
if not root_path:
|
|
raise ValueError("Vault root path must be provided")
|
|
self.root_path = os.path.abspath(root_path)
|
|
self.paths = VaultPaths(
|
|
root=self.root_path,
|
|
notes=os.path.join(self.root_path, "notes"),
|
|
attachments=os.path.join(self.root_path, "attachments"),
|
|
kb=os.path.join(self.root_path, ".kb"),
|
|
)
|
|
|
|
def ensure_structure(self) -> None:
|
|
os.makedirs(self.paths.root, exist_ok=True)
|
|
os.makedirs(self.paths.notes, exist_ok=True)
|
|
os.makedirs(self.paths.attachments, exist_ok=True)
|
|
os.makedirs(self.paths.kb, exist_ok=True)
|
|
|
|
def relpath(self, abs_path: str) -> str:
|
|
return os.path.relpath(abs_path, self.paths.root)
|
|
|
|
def abspath(self, rel_path: str) -> str:
|
|
return os.path.join(self.paths.root, rel_path)
|
|
|
|
def iter_markdown_files(self) -> Iterable[str]:
|
|
"""Yield absolute file paths for all .md files under notes/ recursively."""
|
|
for dirpath, _, filenames in os.walk(self.paths.notes):
|
|
for fn in filenames:
|
|
if fn.lower().endswith(".md"):
|
|
yield os.path.join(dirpath, fn) |