Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/check-rust-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ jobs:
LEGACY_COUNT=0
LEGACY_FILES=""

for file in $(find src/coding-guidelines -name "*.rst" 2>/dev/null); do
# Check both .rst and .rst.inc files (per-guideline structure uses .rst.inc)
for file in $(find src/coding-guidelines \( -name "*.rst" -o -name "*.rst.inc" \) 2>/dev/null); do
COUNT=$(grep -c "^\s*\.\. code-block:: rust" "$file" 2>/dev/null || echo 0)
if [ "$COUNT" -gt 0 ]; then
LEGACY_FILES="$LEGACY_FILES\n- $file: $COUNT occurrences"
Expand Down
80 changes: 66 additions & 14 deletions scripts/extract_rust_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
3. Generates a test crate with all examples as doc tests
4. Runs the tests and reports results

Supports both monolithic chapter files (*.rst) and per-guideline files (*.rst.inc).

Usage:
# Extract examples and generate test crate
uv run python scripts/extract_rust_examples.py --extract
Expand Down Expand Up @@ -220,7 +222,7 @@ def extract_rust_examples_from_file(file_path: Path) -> List[RustExample]:
- Legacy code-block:: rust directives (for backwards compatibility during migration)

Args:
file_path: Path to the RST file
file_path: Path to the RST file (supports .rst and .rst.inc)

Returns:
List of RustExample objects
Expand Down Expand Up @@ -326,6 +328,25 @@ def extract_rust_examples_from_file(file_path: Path) -> List[RustExample]:
return examples


def find_rst_files(src_dir: Path) -> List[Path]:
"""
Find all RST files in the source directory.

Searches for both:
- *.rst files (chapter index files, monolithic chapter files)
- *.rst.inc files (per-guideline include files)

Args:
src_dir: Directory to search

Returns:
List of Path objects for all RST files found
"""
rst_files = list(src_dir.glob("**/*.rst"))
rst_inc_files = list(src_dir.glob("**/*.rst.inc"))
return rst_files + rst_inc_files


def extract_all_examples(src_dirs: List[Path], quiet: bool = False) -> List[RustExample]:
"""
Extract all Rust examples from all RST files in the given directories.
Expand All @@ -340,17 +361,47 @@ def extract_all_examples(src_dirs: List[Path], quiet: bool = False) -> List[Rust
examples = []

for src_dir in src_dirs:
rst_files = list(src_dir.glob("**/*.rst"))
all_files = find_rst_files(src_dir)

if not quiet:
print(f"🔍 Scanning {len(rst_files)} RST files in {src_dir}", file=sys.stderr)

for file_path in rst_files:
file_examples = extract_rust_examples_from_file(file_path)
if file_examples:
if not quiet:
print(f" 📄 {file_path.name}: {len(file_examples)} examples", file=sys.stderr)
examples.extend(file_examples)
print(f"🔍 Scanning {len(all_files)} RST files in {src_dir}", file=sys.stderr)

# Group files by parent directory (chapter)
files_by_chapter: Dict[str, List[Path]] = {}
for file_path in all_files:
# Get chapter name (parent directory relative to src_dir)
try:
rel_path = file_path.relative_to(src_dir)
if len(rel_path.parts) > 1:
chapter = rel_path.parts[0]
else:
chapter = "(root)"
except ValueError:
chapter = "(other)"

if chapter not in files_by_chapter:
files_by_chapter[chapter] = []
files_by_chapter[chapter].append(file_path)

# Process files grouped by chapter
for chapter in sorted(files_by_chapter.keys()):
chapter_files = files_by_chapter[chapter]
chapter_has_examples = False
file_results: List[Tuple[Path, List[RustExample]]] = []

# Extract examples from each file
for file_path in sorted(chapter_files):
file_examples = extract_rust_examples_from_file(file_path)
if file_examples:
file_results.append((file_path, file_examples))
examples.extend(file_examples)
chapter_has_examples = True

# Print chapter heading and files with examples
if chapter_has_examples and not quiet:
print(f"\n {chapter}/", file=sys.stderr)
for file_path, file_examples in file_results:
print(f" {file_path.name}: {len(file_examples)} examples", file=sys.stderr)

if not quiet:
print(f"\n📊 Total: {len(examples)} examples found", file=sys.stderr)
Expand Down Expand Up @@ -683,14 +734,15 @@ def main():
filter_min_version=args.filter_min_version,
filter_default=args.filter_default,
)
if args.verbose:
print(f"🔍 Filtered {original_count} -> {len(examples)} examples")
filtered_count = original_count - len(examples)
if filtered_count > 0:
print(f"\n🔍 Filtered: {len(examples)} examples to test ({filtered_count} excluded)")
if args.filter_channel:
print(f" Filter: channel={args.filter_channel}")
if args.filter_min_version:
print(f" Filter: min_version={args.filter_min_version}")
print(f" Filter: min_version>={args.filter_min_version}")
if args.filter_default:
print(" Filter: default (no special requirements)")
print(f" Filter: default toolchain only (excluded {filtered_count} requiring nightly/beta or specific version)")

if args.extract or args.test:
# Generate test crate
Expand Down
65 changes: 55 additions & 10 deletions scripts/generate-rst-comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import json
import os
import re
import sys

# Add the scripts directory to Python path so we can import guideline_utils
Expand All @@ -28,6 +29,20 @@
)


def extract_guideline_id(rst_content: str) -> str:
"""
Extract the guideline ID from RST content.

Args:
rst_content: The generated RST content

Returns:
The guideline ID (e.g., "gui_abc123XYZ") or empty string if not found
"""
match = re.search(r':id:\s*(gui_[a-zA-Z0-9]+)', rst_content)
return match.group(1) if match else ""


def generate_comment(rst_content: str, chapter: str) -> str:
"""
Generate a formatted GitHub comment with instructions and RST content.
Expand All @@ -40,17 +55,39 @@ def generate_comment(rst_content: str, chapter: str) -> str:
Formatted Markdown comment string
"""
chapter_slug = chapter_to_filename(chapter)
target_file = f"src/coding-guidelines/{chapter_slug}.rst"
guideline_id = extract_guideline_id(rst_content)

# Determine target path based on whether we have a guideline ID
if guideline_id:
target_dir = f"src/coding-guidelines/{chapter_slug}/"
target_file = f"{target_dir}{guideline_id}.rst.inc"
file_instructions = f"""
### 📁 Target Location

Create a new file: `{target_file}`

> **Note:** The `.rst.inc` extension prevents Sphinx from auto-discovering the file.
> It will be included via the chapter's `index.rst`."""
else:
# Fallback for legacy structure (shouldn't happen with new template)
target_file = f"src/coding-guidelines/{chapter_slug}.rst"
file_instructions = f"""
### 📁 Target File

Add this guideline to: `{target_file}`"""

comment = f"""## 📋 RST Preview for Coding Guideline

This is an automatically generated preview of your coding guideline in reStructuredText format.
{file_instructions}

### 📁 Target File
### 📝 How to Use This

Add this guideline to: `{target_file}`
**Option A: Automatic (Recommended)**

### 📝 How to Use This
Once this issue is approved, a maintainer will add the `sign-off: create pr from issue` label, which automatically creates a PR with the guideline file.

**Option B: Manual**

1. **Fork the repository** (if you haven't already) and clone it locally
2. **Create a new branch** from `main`:
Expand All @@ -59,19 +96,27 @@ def generate_comment(rst_content: str, chapter: str) -> str:
git pull origin main
git checkout -b guideline/your-descriptive-branch-name
```
3. **Open the target file** `{target_file}` in your editor
4. **Copy the RST content** below and paste it at the end of the file (before any final directives if present)
5. **Build locally** to verify the guideline renders correctly:
3. **Create the guideline file**:
```bash
mkdir -p src/coding-guidelines/{chapter_slug}
```
4. **Copy the RST content** below into a new file named `{guideline_id}.rst.inc`
5. **Update the chapter index** - Add an include directive to `src/coding-guidelines/{chapter_slug}/index.rst`:
```rst
.. include:: {guideline_id}.rst.inc
```
Keep the includes in alphabetical order by guideline ID.
6. **Build locally** to verify the guideline renders correctly:
```bash
./make.py
```
6. **Commit and push** your changes:
7. **Commit and push** your changes:
```bash
git add {target_file}
git add src/coding-guidelines/{chapter_slug}/
git commit -m "Add guideline: <your guideline title>"
git push origin guideline/your-descriptive-branch-name
```
7. **Open a Pull Request** against `main`
8. **Open a Pull Request** against `main`

<details>
<summary><strong>📄 Click to expand RST content</strong></summary>
Expand Down
Loading