Rust/Programming Rust

Rust 문서화

불두화 2024. 2. 24. 23:51

Cargo doc 명령은 여러분의 라이브러리를 위한 HTML 문서를 생성한다.

$ cargo doc --no-deps --open

--no deps 옵션은 카고에서 문서를 생성하되, 의존하고 있는 크레이트에 관한 내용은 전부 제외해달라고 지시한다.

--open 옵션은 카고에게 생성된 문서를 브라우저에서 열어달라고 지시한다.

 

문서는 라이브러리에 있는 pub 기능과 여기에 달린 문서 주석을 바탕으로 생성된다.

문서 주석은 다음과 같이 생겼다.

/// 빠른 프로젝션을 위한 이미지 피라미드를 생성한다.

하지만 러스트는 세개의 슬래시로 시작하는 주석을 #[doc] 어트리뷰트로 취급한다.

즉, 러스트는 앞의 코드를다음 코드오와 똑같다고 본다.

#[doc = "빠른 프로젝션을 위한 이미지 피라미드를 생성한다."]

라이브러리나 바이너리를 컴파일 할 떄는 이들 어트리뷰트가 무시되지만, 문서를 생성할 떄는 공개 기능에 달린 문서 주석이 결과물에 포함된다.

 

마친가지로 //!로 시작하는 주석은 #![doc] 어트리뷰트로 취급된다. 이 주석은 보통 모듈이나 크레이트 같은 바깥쪽 기능에 붙는다.

문서 주석의 내용은 간단한 HTML 서식을 쉽고 빠르게 쓸 수 있는 마크다운으로 작성한다.

이를테면 별표로 *기울인꼴*과 **굵은 꼴**을 표현하고, 빈 줄로 단락을 나누는 식이다.

HTML 태그를 쓸 수도 있는데, 문서 주식에 있는 HTML 태그는 적힌 그대로 형식화된 문서 안에 복사된다.

러스트 문서 주석이 가진 한 가지 특별한 기능은 마크다운 링크의 참조 대상을 지정할 때 상대 URL 대신 leaves::Leaf 같은 러스트 아이템 경로를 쓸 수 있다는 점이다. 이렇게 해 두면 카고가 그 경로의 참조 대상을 찾아서 알맞은 문서 페이지의 위치를 가르키는 링크로 대체해준다.

예를 들어, 다음 코드에서 생성된 무서는 Vascular Path, Leaf, Root에 해당하는 문서 페이지로 연결된다.

/// 주어진 [`Root`][r]에서 [`Leaf`](leaves::Leaf)까지 이어지는 영양소의 이동 경로를
/// 표현하는 [`VascularPath`]를 만들어 반환한다.
///
/// [r]: roots::Root
pub fn trace_path(leaf: &leaves::Leaf, root: &roots::Root) -> VascularPath {
 . . .
 }

또 검색 별칭을 달아서 기본으로 제공되는 검색 기느응ㄹ 통해 내용을 좀 더 쉽게 찾을 수 있도록 만들 수도 있다. 

예르르 들어, 크레이트의 문서에서 "path"나 "route"를 검색할 떄, VascularPath로 연결되도록 만들려면 다음처럼 하면 된다.

#[doc(alias = "route")]
pub struct VascularPath {
 . . .
 }

더 긴 문서 블록이 필요하거나 워크플로를 간소화하고 싶을 떄는 외부 파일을 문서에 표함시킬 수 있다. 

예를 들어 저장소의 README.md 파일이 크레이트의 최상위 문서로 쓸 텍스트를 주고 있을 떄는 다음 코드를 lib.rs나 main.rs 맨 위에 두면 된다.

#![doc = include_str("../README.md")]

텍스트 중간에 코드를 박아 넣을 떄는 역작은 따움표 ` 백틱을 쓴다. 이렇게 하면 결과물에 코드가 고정폭 글꼴로 표시된다. 좀더 큰 코드 샘플을 네 칸 들려쓰기에 추가하면 된다.

/// 문서 주석에 있는 코드 블록:
/// 
/// if samples::everything().work() {
///		println!("ok");
///		}

마크다운의 펜스 코드 블록을 쓸 수도 있는데 이렇게 해도 효과는 동일하다.

/// 앞의 코드 조각을 다음처럼 작성할 수도 있다.
///
/// ```
/// if samples::everything().work() {
///		println!("ok");
/// }
/// ```

어떤 형식을 사용하든 문서 주석에 코드블럭을 넣으면 재미있는 일이 벌어지는데, 러스트가 그 코드 블록을 알아서 테스트로 바꾸어 준다.


독 테스트

러스트 라이브러리 크레이트에 있는 테스트를 실행하면 러스트는 문서에 나와 있는 모든 코드가 실제로 실행되고 작동되는지 확인한다. 문서 주석에 나와 있는 각 코드 블록을 가져다가 별도의 실행 파일 크레이트로 컴파일하고, 이를 라이브러리와 링크한 뒤 실행해 봄으로써 검증이 이뤄진다.

그럼 도게스트의 예를 한 번 살펴보자. cargo enw --lib ranges를 실행해 시 프로젝트ㅡㄹ 만들고, 다음 코드를 ranges/src/lib.rs에 붙여 넣자(--lib 플래그는 카고에게 실행 파일 크레이트가 아니라 라이브러리 크레이트를 만들도록 지시한다.

use std::ops::Range

/// 두 범위가 겹치면 true를 반환한다.
///
/// 	assert_eq!(ranges::overlap(0..7, 3..10), true);
/// 	assert_eq!(ranges::overlap(1..5, 101..105), false);
///
///  두 범위 중 하나라도 비어 있으면 겹치지 않는다고 판단한다.
///
/// 	asser_eq!(ranges::overlap(0..0, 0..10), false);
///
pub fn overlap(r1: Range<usize>, r2: Range<usize>) -> bool {
	r1.start < r1.end && r2.start < r2.end &&
    r1.start < r2.end && r2.start < r1.end
}

해당 문서 주석에 있는 두 개의 작은 코드 블록은 cargo doc이 생성하는 문서에 표시된다. 또한 이 둘은 두 개의 독립되 ㄴ테스트가 된다.

카고에 --verbose 플래그를 넘겨보면 이 두 테스트가 rustdoc --test를 통해 실행된다는 걸 알 수 있다. rustdoc은 이 두 코드 샘플을 각각 별도의 파일에 저장한 뒤, 상용구 몇 줄을 더해서 총 두 개의 프로그램을 만들어 낸다. 

실제로 작동하는 예제를 작성하다 보면, 가져오기나 설정 코드처럼 코드를 컴파일하는 데 필요하지만 문서에 보여 줄 만큼 중요하지는 않은 세부사항을 포함하고 있을 때가 많다. 코드 샘플에서 숨기고 싶은 줄이 있을 때는 그 줄 맨 앞에 #과 공백을 붙이면 된다.

/// 해가 드는 조건에서 주어진 시간만큼 시뮬레이션을 돌린다.
///
/// 	# use fern_sim::Terrarium;
///		# use std::time:Duration;
///		# let mut tm = Terrarium::new();
///		tm.apply_sunlight(Duration::from_secs(60));
///
pub fn apply_sunlight(&mut self, time: Duration) {
 . . .
}

특정 코드 블록을 테스트하지 않게 만들 수도 있다. 예제를 컴파일하되 실제 실행되는 것까지는 바라지 않는다면, no_run 애너테이션이 붙은 펜스 코드 블록을 쓰면 된다.

///	로컬 테러리엄을 전부 온라인 갤러리에 업로드한다.
///
///	```no_run
///	let mut session = fern_sim::connect();
///	session.upload_all();
///	```
pub fn upload_all(&mut self) {
	. . .
}

코드가 컴파일도 안 되겠다 싶을 떄는 no_run 대신 ignore을 쓰자. ignore로 표시도니 블록은 cargo run의 출력에 표시되지 않지만, no_run 테스트는 컴파일이 될 경우 통과한 걸로 표시된다. 코드 블록이 러스트 코드가 아닐 때는, c++나 sh처럼 해당하는 언어의 이름을 쓰고 일반 텍스트일 떄는 text를 쓰면 된다. rustdoc은 수백 개나 되는 프로그래밍 언어의 이름을 다 알지 못하기 때문에 인식할 수 없는 애너테이션은 전부 비러스트 코드블록을 나타내는 것으로 간주한다. 이렇게 되면 독 테스트 뿐만 아니라 코드 강조 기능도 꺼진다.