Coverage for src/prosemark/adapters/binder_scaffold.py: 100%
34 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-09-24 18:08 +0000
1# Copyright (c) 2024 Prosemark Contributors
2# This software is licensed under the MIT License
4"""Binder scaffold generator for new prosemark projects.
6This module provides functionality to generate initial _binder.md files
7for new prosemark projects with proper structure, format, and example content.
8"""
10from contextlib import suppress
11from pathlib import Path
13from prosemark.exceptions import FileSystemError, ProsemarkFileExistsError
16def generate_binder_scaffold(target_path: Path, *, create_dirs: bool = False) -> None:
17 """Generate a scaffold _binder.md file for a new prosemark project.
19 Creates a _binder.md file with proper structure including managed block markers,
20 example content, and helpful documentation for users. The file includes HTML
21 comment markers that define the managed section for prosemark to maintain
22 automatically.
24 Args:
25 target_path: Directory where the _binder.md file should be created
26 create_dirs: If True, create parent directories if they don't exist
28 Raises:
29 ProsemarkFileExistsError: If _binder.md already exists in the target directory
30 FileSystemError: If the file cannot be created due to I/O errors,
31 permission issues, or missing parent directories
33 Examples:
34 >>> # Basic usage
35 >>> generate_binder_scaffold(Path('/path/to/project'))
37 >>> # With directory creation
38 >>> generate_binder_scaffold(Path('/path/to/new/project'), create_dirs=True)
40 """
41 # Validate and prepare target directory
42 if create_dirs:
43 try:
44 target_path.mkdir(parents=True, exist_ok=True)
45 except OSError as exc:
46 msg = 'Cannot create target directory'
47 raise FileSystemError(msg, str(target_path)) from exc
48 elif not target_path.exists():
49 msg = 'Target directory does not exist'
50 raise FileSystemError(msg, str(target_path))
51 elif not target_path.is_dir():
52 msg = 'Target path is not a directory'
53 raise FileSystemError(msg, str(target_path))
55 # Check for existing binder file
56 binder_file = target_path / '_binder.md'
57 try:
58 if binder_file.exists():
59 msg = 'Binder file already exists'
60 raise ProsemarkFileExistsError(msg, str(binder_file))
61 except (OSError, PermissionError) as exc:
62 # If we can't even check if the file exists due to permissions, we can't write either
63 msg = 'Cannot access target directory'
64 raise FileSystemError(msg, str(target_path)) from exc
66 # Generate scaffold content
67 content = _generate_scaffold_content()
69 # Write the file atomically
70 try:
71 # Write to temporary file first, then rename for atomicity
72 temp_file = binder_file.with_suffix('.tmp')
73 temp_file.write_text(content, encoding='utf-8')
74 temp_file.rename(binder_file)
75 except OSError as exc:
76 # Clean up temporary file if it exists
77 if temp_file.exists(): # pragma: no cover
78 with suppress(OSError):
79 temp_file.unlink()
81 msg = 'Cannot write binder file'
82 raise FileSystemError(msg, str(binder_file)) from exc
85def _generate_scaffold_content() -> str:
86 """Generate the content for the scaffold _binder.md file.
88 Creates the complete content including documentation, managed block
89 markers, and example content that demonstrates proper binder format.
91 Returns:
92 Complete content string for the _binder.md file
94 """
95 return """# Binder
97Welcome to your new prosemark project! This file serves as your project outline and table of contents.
99You can write notes, introductions, and other content anywhere in this file.
100Only the section between the special markers below is managed by prosemark.
102## Binder (managed by Prosemark)
103<!-- pmk:begin-binder -->
104- [Sample Chapter](01234567.md)
105- [New Placeholder]()
106<!-- pmk:end-binder -->
108The managed section above will be automatically updated as you add, move, and remove nodes.
109"""