스킬: add-rust-svgbob
이 스킬의 파일 참조(@path)는 이 디렉토리를 기준으로 합니다.
내가 하는 일
svgbobdoc 크레이트의 transform!() 매크로를 사용하여 Rust /// 문서 주석에 ASCII 아트 다이어그램을 추가합니다. ```svgbob 코드 펜스 안의 ASCII 아트는 컴파일 시간에 인라인 SVG로 변환되어 data:image/svg+xml 데이터 URI로 임베드됩니다. 외부 파일이 필요 없으며 로컬 cargo doc과 docs.rs 모두에서 작동합니다.
다이어그램이 소스 코드 자체에 존재합니다. Rust 파일을 읽는 누구나 ASCII 아트를 직접 볼 수 있습니다.
사용 시기
다음과 같은 경우에 사용하세요:
- 다이어그램으로 설명하는 것이 가장 좋은 구조적/아키텍처 개념이 있는 Rust 라이브러리가 있는 경우
- 외부 이미지 파일 없이 빠른 순서도, 프로세스 다이어그램, 데이터 흐름 시각화가 필요한 경우
- 다이어그램이 ASCII 상자 그리기 문자로 표현할 수 있을 정도로 간단한 경우
- 소스 코드에서 볼 수 있는 다이어그램(외부 PNG에 숨겨지지 않음)을 원하는 경우
svgbobdoc의 의존성 무게를 감수할 수 있는 크레이트를 유지 관리하는 경우
적합하지 않은 경우:
- 실제 데이터 시각화(산점도, 선 그래프) — 대신
embed-doc-image사용 - 곡선 경로나 세부 묘사가 복잡한 다이어그램 — ASCII 아트에는 한계가 있음
- 매우 오래된 Rust 컴파일러(< 1.54)를 사용하는 프로젝트
전제 조건
| 요구 사항 | 목적 |
|---|---|
| Rust ≥ 1.54 또는 nightly | 내부 속성 + 프로시저 매크로 지원 |
의존성에 svgbobdoc | 프로시저 매크로 크레이트 |
svgbobdoc의 enable 기능 | svgbob SVG 렌더러 활성화 |
doc-images 기능 플래그 (또는 사용자 정의) | 호환성을 위한 기능 게이팅 |
| docs.rs 메타데이터 구성 | docs.rs에서 SVG가 렌더링되도록 보장 |
작동 방식
1단계: 현재 상태 확인
Cargo.toml을 읽어 svgbobdoc가 이미 구성되어 있는지 확인:
svgbobdoc가[dependencies]에features = ["enable"]와 함께 있는가?- 선택 사항(권장)인가?
- 이를 활성화하는
doc-images또는 유사한 기능이 있는가? [package.metadata.docs.rs]에features = ["doc-images"]가 포함되어 있는가?
소스 파일에서 기존 ```svgbob 사용 또는 svgbobdoc::transform!() 호출을 확인.
2단계: 인프라 설정 (누락된 경우)
svgbobdoc가 구성되지 않은 경우 추가:
Cargo.toml 추가 내용:
[dependencies]
svgbobdoc = { version = "0.3", optional = true, features = ["enable"] }
[features]
doc-images = ["dep:svgbobdoc"]
[package.metadata.docs.rs]
features = ["doc-images"]
embed-doc-image도 사용하는 경우 동일한 기능 아래 결합:
[features]
doc-images = ["dep:embed-doc-image", "dep:svgbobdoc"]
3단계: 프로젝트 스캔
<project>/src/에서 재귀적으로 *.rs 파일을 찾습니다. ASCII 다이어그램이 개념을 명확히 할 다이어그램 후보 (유형 및 함수)를 식별합니다.
4단계: 다이어그램 후보 식별
다음과 같은 경우 함수/유형/모듈은 "다이어그램 후보"입니다:
| 신호 | 예시 |
|---|---|
| 데이터 흐름 | 입력 → 처리 → 출력 |
| 프로세스 단계 | 명확한 단계가 있는 알고리즘 |
| 좌표 매핑 | (x, y) → 변환된 좌표 |
| 기하학적 변환 | 회전, 투영, 크기 조정 |
| 트리/그래프 구조 | 이진 트리, 네트리스트, 의존성 그래프 |
| 아키텍처 | 모듈 계층 구조, 파이프라인 단계 |
| 상태 기계 | 상태 및 전이 |
| 비교 | 전과 후, 두 대안 |
| 레이아웃 | 평면도, 메모리 레이아웃, 패킹 |
5단계: ASCII 다이어그램 디자인
svgbob 렌더링 기능:
| ASCII 요소 | svgbob 렌더링 |
|---|---|
.─. / '─' | 둥근 직사각형 테두리 |
│ / ─ | 선 |
/ / \ | 대각선 |
► / ▼ / ▲ / ◄ | 화살촉 (유니코드) |
-> / <- | 화살표 연결자 |
=== / ~~~ | 이중/물결 선 |
( ) / [ ] | 곡선/각진 괄호 |
○ / ● | 원 마커 |
... | 점선 연결자 |
명확한 다이어그램을 위한 디자인 팁:
- 간결하게 유지 — 다이어그램당 최대 15-25행
- 상자 테두리 사용 — 상단에
.───., 하단에'───' - 화살표에 레이블 추가 —
──►연결자 옆에 텍스트 추가 - 유니코드 상자 그리기 정렬 —
─ │ ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼ - 다이어그램 테스트 — svgbob 편집기에서 미리보기
예시 — 간단한 데이터 흐름:
.───────────. .───────────. .───────────.
│ 입력 │────►│ 처리 │────►│ 출력 │
'───────────' '───────────' '───────────'
6단계: 다이어그램 추가
구조체 수준 문서:
/// 기존 설명.
///
/// 변환 흐름:
#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
/// ```svgbob
/// .───────────. .───────────.
/// │ 1단계 │────►│ 2단계 │
/// '───────────' '───────────'
/// ```
))]
pub struct MyType { ... }
함수 수준 문서:
/// 설명.
#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
/// ```svgbob
/// .───────────. .───────────.
/// │ 입력 x │────►│ 출력 f(x) │
/// '───────────' '───────────'
/// ```
))]
pub fn my_function(x: f64) -> f64 { ... }
중요: transform!() 내부의 /// 줄을 정확히 유지해야 합니다. 매크로는 ///를 문서 주석 줄로 구문 분석하고 그 안에서 ```svgbob 블록을 처리합니다.
7단계: 빌드 및 확인
# 다이어그램과 함께 빌드
cargo doc --features doc-images --no-deps --open
확인 체크리스트:
-
cargo build(기능 없이) 통과 — svgbobdoc 의존성 누출 없음 -
cargo build --features doc-images통과 —transform!()매크로 실행 -
cargo doc --features doc-images --no-deps경고 없이 완료 - 생성된 HTML에
data:image/svg+xml;base64,...URI 포함 - 브라우저에서 SVG가 올바르게 렌더링됨 (적절한 화살표, 상자, 텍스트)
# 각 구조체 페이지에서 SVG 데이터 URI 확인
grep -c "data:image/svg" target/doc/my_crate/struct.MyType.html
8단계: 두 기능 상태 모두 확인
cargo build # 반드시 통과 (svgbobdoc 링크되지 않음)
cargo build --features doc-images # 반드시 통과 (transform! 실행)
cargo test --all-features # 모든 테스트 통과 필요
구성 참조
기본 Cargo.toml (svgbobdoc만)
svgbobdoc만 사용하는 프로젝트의 최소 설정:
[package]
name = "my-crate"
version = "0.1.0"
edition = "2024"
[dependencies]
svgbobdoc = { version = "0.3", optional = true, features = ["enable"] }
[features]
doc-images = ["dep:svgbobdoc"]
[package.metadata.docs.rs]
features = ["doc-images"]
embed-doc-image와 결합
svgbobdoc (ASCII 다이어그램)과 embed-doc-image (PNG/SVG 그림)을 모두 사용할 때:
[dependencies]
embed-doc-image = { version = "0.1", optional = true }
svgbobdoc = { version = "0.3", optional = true, features = ["enable"] }
[features]
doc-images = ["dep:embed-doc-image", "dep:svgbobdoc"]
[package.metadata.docs.rs]
features = ["doc-images"]
둘 다 동일한 doc-images 기능을 공유합니다. 한 번의 cargo doc --features doc-images로 둘 다 활성화됩니다.
사용자 정의 기능 이름
프로젝트에 이미 다른 목적의 doc-images 기능이 있는 경우 다른 이름을 사용하세요:
[features]
default = []
svg-docs = ["dep:svgbobdoc"] # 사용자 정의 이름
doc-images = ["dep:embed-doc-image"] # 기존 이름
그런 다음 소스 코드에서:
#[cfg_attr(feature = "svg-docs", doc = svgbobdoc::transform!(
/// ```svgbob
/// diagram
/// ```
))]
pub struct MyType;
dep: 구문을 사용한 기능 확인
기능 정의의 dep: 접두사는 Rust 1.60에서 안정화되었습니다. 선택적 의존성을 명시적으로 참조합니다:
# 최신 구문 (Rust ≥ 1.60) — 권장:
doc-images = ["dep:svgbobdoc"]
# 레거시 구문 — 작동하지만 덜 명시적:
doc-images = ["svgbobdoc"]
dep: 형식이 선호됩니다. 의존성 이름과 기능 이름 간의 모호성을 방지합니다.
docs.rs 메타데이터 옵션
| 설정 | 목적 |
|---|---|
features = ["doc-images"] | 문서 빌드 중 svgbobdoc 활성화 |
rustdoc-args = ["--html-in-header", "src/doc-header.html"] | KaTeX/JS 헤더 주입 |
all-features = true | 모든 기능으로 문서 빌드 (주의해서 사용) |
[package.metadata.docs.rs]
features = ["doc-images"]
rustdoc-args = ["--html-in-header", "src/doc-header.html"]
cfg_attr을 사용한 조건부 컴파일
핵심 패턴:
#[cfg_attr(
feature = "doc-images", // 조건
doc = svgbobdoc::transform!( // 내보낼 속성
/// ```svgbob
/// 다이어그램
/// ```
)
)]
각 빌드에서 작동 방식:
| 빌드 명령 | cfg_attr 평가 결과 | 결과 |
|---|---|---|
cargo build | feature = "doc-images" 거짓 | 속성 억제 — 매크로 없음, svgbob 의존성 없음 |
cargo build --features doc-images | feature = "doc-images" 참 | #[doc = svgbobdoc::transform!(...)] 내보냄 — SVG 생성 |
cargo doc --features doc-images | 위와 동일 | SVG 데이터 URI가 HTML에 임베드됨 |
| docs.rs (메타데이터가 기능 설정) | 위와 동일 | SVG가 docs.rs에 렌더링됨 |
기능 확인 확인
기능이 올바르게 연결되었는지 확인:
# 기본 빌드에서 svgbobdoc이 연결되지 않았는지 확인
cargo build --verbose 2>&1 | grep svgbobdoc
# (출력 없어야 함)
# 기능으로 svgbobdoc이 연결되었는지 확인
cargo build --features doc-images --verbose 2>&1 | grep svgbobdoc
# 출력: "Compiling svgbobdoc v0.3.0"
svgbob ASCII 아트 구문 가이드
상자 그리기 문자
svgbob 렌더러는 다음 유니코드 상자 그리기 문자를 인식하여 SVG 경로로 변환합니다:
| 문자 | 코드 포인트 | 렌더링 |
|---|---|---|
─ | U+2500 | 가로선 |
│ | U+2502 | 세로선 |
┌ | U+250C | 왼쪽 위 모서리 |
┐ | U+2510 | 오른쪽 위 모서리 |
└ | U+2514 | 왼쪽 아래 모서리 |
┘ | U+2518 | 오른쪽 아래 모서리 |
├ | U+251C | 왼쪽 T-접합 |
┤ | U+2524 | 오른쪽 T-접합 |
┬ | U+252C | 위쪽 T-접합 |
┴ | U+2534 | 아래쪽 T-접합 |
┼ | U+253C | 십자 접합 |
╱ | U+2571 | 오른쪽 위 대각선 |
╲ | U+2572 | 왼쪽 위 대각선 |
◄ | U+25C4 | 왼쪽 화살촉 |
► | U+25BA | 오른쪽 화살촉 |
▲ | U+25B2 | 위쪽 화살촉 |
▼ | U+25BC | 아래쪽 화살촉 |
● | U+25CF | 채워진 원 |
○ | U+25CB | 빈 원 |
═ | U+2550 | 이중 가로선 |
║ | U+2551 | 이중 세로선 |
≈ | U+2248 | 물결선 |
→ | U+2192 | 오른쪽 화살표 |
상자 만들기
둥근 직사각형 (다이어그램 노드에 가장 일반적):
.───────────.
│ 내용 │
'───────────'
- 왼쪽 위 모서리에
., 오른쪽 위 모서리에'사용 - 왼쪽 아래 모서리에
', 오른쪽 아래 모서리에.사용 - 위/아래 테두리를
─로 채움 - 측면 테두리를
│로 채움
날카로운 직사각형:
┌───────────┐
│ 내용 │
└───────────┘
화살표로 상자 연결
가로 화살표:
.───. .───.
│ A │────────►│ B │
'───' '───'
세로 화살표:
.───.
│ A │
'───'
│
▼
.───.
│ B │
'───'
특정 측면에서 화살표:
.───.
│ B │
'───'
▲
│
.───. │
│ A │──────┘
'───'
화살표에 레이블 추가
화살표 선 옆에 텍스트를 배치합니다. svgbob이 자동으로 위치를 지정합니다:
.───. 조건 .───.
│ A │══════════════►│ B │
'───' '───'
.───. 거짓 .───.
│ A │──────────────►│ B │
'───' '───'
상자 내 여러 줄 텍스트
svgbob은 상자 너비에 따라 텍스트를 자연스럽게 줄 바꿈합니다:
.──────────────────.
│ 이 텍스트는 │
│ 상자 경계 안에서 │
│ 줄 바꿈됩니다 │
'──────────────────'
대각선
.───. .───.
│ A │╲ │ C │
'───' ╲ '───'
╲
╲ .───.
╲│ B │
╲'───'
트리 및 계층 구조
이진 트리:
.───.
│ R │
'───'
╱ ╲
.───. .───.
│ L │ │ R │
'───' '───'
╱ ╲ ╲
.───. .───. .───.
│LL │ │LR │ │RR │
'───' '───' '───'
들여쓰기된 계층 구조 (선 없음):
.───────────────.
│ 루트 │
│ ├── 자식 A │
│ ├── 자식 B │
│ └── 자식 C │
'───────────────'
그리드 및 행렬
.──────┬──────┬──────.
│ a │ b │ c │
├──────┼──────┼──────┤
│ d │ e │ f │
├──────┼──────┼──────┤
│ g │ h │ i │
'──────┴──────┴──────'
비교 레이아웃
나란히:
.─────────────. .─────────────.
│ 이전 │ │ 이후 │
│ .───────. │ │ .───────. │
│ │ 이전 │ │ │ │ 새로 │ │
│ '───────' │ │ '───────' │
'─────────────' '─────────────'
이전 → 이후 (순차):
.───────────────. .───────────────.
│ 느린 경로 │────►│ 빠른 경로 │
│ O(n²) │ │ O(n log n) │
'───────────────' '───────────────'
데이터베이스 / 테이블 스키마
.──────────────────────.
│ 테이블: users │
├──────────────────────┤
│ id INTEGER │──► PK
│ name TEXT │
│ email TEXT │──► UNIQUE
│ created_at DATETIME │
'──────────────────────'
결정이 있는 순서도
.───────────.
│ 시작 │
'─────┬─────'
│
▼
.───────────.
│ 처리 │
'─────┬─────'
│
▼
.───────────. 예 .───────────.
│ 확인? │─────────►│ 옵션 A │
'─────┬─────' '───────────'
│
│ 아니오
▼
.───────────.
│ 옵션 B │
'───────────'
계층형 아키텍처
.───────────────────────────.
│ 애플리케이션 계층 │
├───────────────────────────┤
│ 비즈니스 로직 계층 │
├───────────────────────────┤
│ 데이터 접근 계층 │
├───────────────────────────┤
│ 데이터베이스 │
'───────────────────────────'
타이밍 / 시퀀스 다이어그램 (간단)
클라이언트───┬──────────────────┐
│ 요청 │
│─────────────────►│
│ │ 서버
│◄─────────────────│
│ 응답 │
│──────────────────┘
전체 예제
벡터 회전 함수
/// 2D에서 벡터를 주어진 각도(라디안)만큼 회전합니다.
///
/// # 회전 변환
///
/// 다음 다이어그램은 입력 벡터가 어떻게 회전하는지 보여줍니다:
#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
/// ```svgbob
/// y
/// ^
/// | . (x', y') -- 회전된
/// | ╱
/// | ╱ ▲
/// | ╱ | 각도
/// |╱ |
/// ╰─────►──────► x
/// (x, y) 원본
/// ```
))]
pub fn rotate(theta: f64, v: (f64, f64)) -> (f64, f64) {
let (x, y) = v;
let cos = theta.cos();
let sin = theta.sin();
(x * cos - y * sin, x * sin + y * cos)
}
데이터 변환 파이프라인
/// 원시 문자열을 정리된 토큰으로 처리하는 파이프라인.
///
/// # 처리 흐름
#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
/// ```svgbob
/// .──────────────. .───────────────. .───────────────. .──────────────.
/// │ 원시 입력 │────►│ 전처리 │────►│ 토큰화 │────►│ 출력 │
/// │ &str │ │ 트림, 소문자 │ │ 공백 분할 │ │ Vec<&str> │
/// '──────────────' '───────────────' '───────────────' '──────────────'
/// ```
))]
pub fn process(input: &str) -> Vec<&str> {
input.trim().to_lowercase().split_whitespace().collect()
}
메모리 레이아웃 (바이트 정렬)
/// 패딩을 보여주는 struct 메모리 레이아웃.
///
/// # 레이아웃
#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
/// ```svgbob
/// .──────┬──────┬──────┬──────.
/// │ 바이트 0-3 │ 바이트 4-7 │ 바이트 8-11│ 바이트 12-15│
/// ├──────┼──────┼──────┼──────┤
/// │ x │ 패딩 │ y │ 패딩 │
/// │ (f32) │ 4B │ (f32) │ 4B │
/// '──────┴──────┴──────┴──────'
/// ```
))]
pub struct AlignedPair {
x: f32,
y: f32,
}
문제 해결
일반적인 문제
| 문제 | 원인 | 해결책 |
|---|---|---|
transform!() 내부에서 구문 분석 오류 | 잘못된 /// 정렬 | 각 줄이 정확히 /// 로 시작하는지 확인 |
cargo build에서 svgbobdoc 오류 | 기능이 기본 빌드에서 활성화됨 | dep: 구문 확인, optional = true인지 확인 |
| docs.rs에 SVG 표시 안 됨 | docs.rs 메타데이터 누락 | [package.metadata.docs.rs]에 features = ["doc-images"] 추가 |
cargo doc에서 잘못된 크기의 다이어그램 | SVG 뷰박스 문제 | svgbob 편집기에서 다이어그램 테스트, 상자 정리 |
feature = "doc-images"에 대한 경고 | 기능이 제대로 선언되지 않음 | 기능 정의에서 dep: 접두사 사용 |
빌드 진단
# 기능 확인 디버깅
cargo tree --features doc-images -i svgbobdoc
# 문서 오류 확인
cargo doc --features doc-images --no-deps 2>&1 | grep -i error
# SVG URI가 생성되었는지 확인
grep -l "image/svg+xml" target/doc/*/*.html | head -5