From 99c43bcff9b579f58b5a7cc59e47b86726528708 Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Fri, 30 Jan 2026 15:34:04 +0000 Subject: [PATCH] Initial upload: gitignore-generator-cli v1.0.0 with CI/CD workflow --- gitignore_generator/cache.py | 181 +++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 gitignore_generator/cache.py diff --git a/gitignore_generator/cache.py b/gitignore_generator/cache.py new file mode 100644 index 0000000..fba674c --- /dev/null +++ b/gitignore_generator/cache.py @@ -0,0 +1,181 @@ +"""Caching system for gitignore patterns.""" + +import json +from datetime import datetime, timedelta +from pathlib import Path +from typing import Optional + +CACHE_DIR = Path.home() / ".cache" / "gitignore-generator" +CACHE_METADATA_FILE = CACHE_DIR / "cache.json" +CACHE_EXPIRY_DAYS = 7 + + +class CacheManager: + """Manages caching of gitignore patterns.""" + + def __init__(self, expiry_days: int = CACHE_EXPIRY_DAYS): + self.expiry_days = expiry_days + self._ensure_cache_dir() + + def _ensure_cache_dir(self) -> None: + """Ensure cache directory exists.""" + CACHE_DIR.mkdir(parents=True, exist_ok=True) + + def _load_metadata(self) -> dict: + """Load cache metadata.""" + if CACHE_METADATA_FILE.exists(): + try: + with open(CACHE_METADATA_FILE, 'r') as f: + return json.load(f) + except (json.JSONDecodeError, IOError): + return {} + return {} + + def _save_metadata(self, metadata: dict) -> None: + """Save cache metadata.""" + with open(CACHE_METADATA_FILE, 'w') as f: + json.dump(metadata, f, indent=2) + + def is_valid(self, key: str) -> bool: + """Check if a cached item is still valid. + + Args: + key: Cache key (technology name) + + Returns: + True if cache is valid and not expired + """ + metadata = self._load_metadata() + if key not in metadata: + return False + + cached_at = datetime.fromisoformat(metadata[key]['cached_at']) + expiry = cached_at + timedelta(days=self.expiry_days) + return datetime.now() < expiry + + def get_age(self, key: str) -> Optional[int]: + """Get age of cached item in days. + + Args: + key: Cache key (technology name) + + Returns: + Age in days or None if not cached + """ + metadata = self._load_metadata() + if key not in metadata: + return None + + cached_at = datetime.fromisoformat(metadata[key]['cached_at']) + delta = datetime.now() - cached_at + return delta.days + + def set(self, key: str, content: str) -> None: + """Cache an item. + + Args: + key: Cache key (technology name) + content: Content to cache + """ + self._ensure_cache_dir() + cache_file = CACHE_DIR / f"{key}.txt" + cache_file.write_text(content) + + metadata = self._load_metadata() + metadata[key] = { + 'cached_at': datetime.now().isoformat(), + 'size': len(content) + } + self._save_metadata(metadata) + + def get(self, key: str) -> Optional[str]: + """Retrieve a cached item. + + Args: + key: Cache key (technology name) + + Returns: + Cached content or None if not found or expired + """ + if not self.is_valid(key): + return None + + cache_file = CACHE_DIR / f"{key}.txt" + if cache_file.exists(): + return cache_file.read_text() + return None + + def invalidate(self, key: str) -> bool: + """Invalidate a cached item. + + Args: + key: Cache key (technology name) + + Returns: + True if item was invalidated + """ + cache_file = CACHE_DIR / f"{key}.txt" + metadata = self._load_metadata() + + if key in metadata: + del metadata[key] + self._save_metadata(metadata) + + if cache_file.exists(): + cache_file.unlink() + return True + return False + + def clear(self) -> int: + """Clear all cached items. + + Returns: + Number of items cleared + """ + metadata = self._load_metadata() + count = len(metadata) + + for key in list(metadata.keys()): + cache_file = CACHE_DIR / f"{key}.txt" + if cache_file.exists(): + cache_file.unlink() + + if CACHE_METADATA_FILE.exists(): + CACHE_METADATA_FILE.unlink() + + return count + + def get_stats(self) -> dict: + """Get cache statistics. + + Returns: + Dictionary with cache statistics + """ + metadata = self._load_metadata() + valid_count = sum(1 for key in metadata if self.is_valid(key)) + expired_count = len(metadata) - valid_count + + return { + 'total_items': len(metadata), + 'valid_items': valid_count, + 'expired_items': expired_count, + 'cache_dir': str(CACHE_DIR) + } + + def cleanup_expired(self) -> int: + """Remove expired cache entries. + + Returns: + Number of entries cleaned up + """ + metadata = self._load_metadata() + to_remove = [key for key in metadata if not self.is_valid(key)] + + for key in to_remove: + cache_file = CACHE_DIR / f"{key}.txt" + if cache_file.exists(): + cache_file.unlink() + del metadata[key] + + self._save_metadata(metadata) + return len(to_remove)