import os from pathlib import Path import typer ROOT_FOLDER = "/home/kodjo/Dev/MyDocManager/src/frontend/node_modules/@sicons" MAX_SIZE = 2000000 import re def pascal_to_snake(name: str) -> str: """Convert a PascalCase or CamelCase string to snake_case.""" # Insert underscore before capital letters (except the first one) s1 = re.sub(r'(.)([A-Z][a-z]+)', r'\1_\2', name) # Handle consecutive capital letters (like 'HTTPServer' -> 'http_server') s2 = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', s1) return s2.lower() app = typer.Typer() def list_sources(source_folder: str): return os.listdir(source_folder) def list_icons_from_source(source_folder: str, source: str): res = [] for f in os.listdir(f"{source_folder}/{source}"): if f.endswith(".svg"): res.append(f) return res def read_content(source_folder: str, source: str, file_name: str): with open(f"{source_folder}/{source}/{file_name}", "r") as f: return f.read().strip() def get_dir_size(path: str | Path) -> int: p = Path(path) if p.is_file(): return p.stat().st_size elif p.is_dir(): return sum(f.stat().st_size for f in p.rglob('*') if f.is_file()) else: raise FileNotFoundError(f"Path not found: {path}") def sizeof_fmt(num, suffix="B"): for unit in ["", "K", "M", "G", "T"]: if abs(num) < 1024.0: return f"{num:3.1f}{unit}{suffix}" num /= 1024.0 return f"{num:.1f}P{suffix}" def init_buffer(source_folder: str, source: str): buffer = "" readme_file_path = f"{source_folder}/{source}/README.md" if os.path.exists(readme_file_path): with open(readme_file_path, "r") as f_readme: for line in f_readme: if line.startswith("#"): buffer += line else: buffer += f"# {line}" buffer += "\n\n" buffer += "from fastcore.basics import NotStr\n\n" return buffer def flush(dry_run, suppress_suffix, source_folder: str, target_folder: str, buffer: str, size: int, part: int, source: str): suffix = '' if suppress_suffix else f"_test" outfile = f"{source}{suffix}.py" if part == 0 else f"{source}_p{part}{suffix}.py" if not dry_run: output_path = f"{target_folder}/{outfile}" if part == 0 else f"{target_folder}/{outfile}" with open(output_path, "w") as f: f.write(buffer) typer.echo(f" Generated {source} as {outfile} ({sizeof_fmt(size)}, max={sizeof_fmt(MAX_SIZE)})") return init_buffer(source_folder, source), 0, part + 1 @app.command("list") def list_icons( source: str = typer.Argument(None, help="The source file to list icons from"), source_folder: str = typer.Option(ROOT_FOLDER, help="The source folder containing icons"), count: bool = typer.Option(False, help="Counts the number of items"), size: bool = typer.Option(False, help="Gets the size of the items"), ): res = [] if source: res.extend(list_icons_from_source(source_folder, source)) else: res.extend(list_sources(source_folder)) if count: typer.echo(len(res)) return if size: path = f"{source_folder}/{source}" if source else f"{source_folder}" size = get_dir_size(path) typer.echo(sizeof_fmt(size)) return for r in res: typer.echo(r) @app.command("generate") def generate_icons( source: str = typer.Argument(None, help="The source file to list icons from"), source_folder: str = typer.Option(ROOT_FOLDER, help="The source folder containing icons"), target_folder: str = typer.Option(".", help="The folder where to create the python files."), top: int = typer.Option(0, help="The number of top items to generate"), dry_run: bool = typer.Option(True, help="Does not generate the icons"), suppress_suffix: bool = typer.Option(False, help="Does not add the suffix to the icon names"), ): sources = [source] if source else list_sources(source_folder) for current_source in sources: typer.echo(f"Generating icons for {current_source}") buffer = init_buffer(source_folder, current_source) size = 0 part = 0 for index, svg_file in enumerate(list_icons_from_source(source_folder, current_source)): if 0 < top <= index: break icon_name = os.path.splitext(os.path.basename(svg_file))[0] svg_content = read_content(source_folder, current_source, svg_file) svg_content = svg_content.replace(" MAX_SIZE: buffer, size, part = flush(dry_run, suppress_suffix, source_folder, target_folder, buffer, size, part, current_source) flush(dry_run, suppress_suffix, source_folder, target_folder, buffer, size, part, current_source) if __name__ == "__main__": app()