Rust CLI 생태계에서 조용히 유행어가 퍼지고 있습니다. 증상: 참고 문서는 색맹을 확인하고 싶게 만드는 텍스트들, 오류 메시지는 너무 평평해서 문을 밀어 넣을 수 있는 수준, 그리고 문서화는 문체적 표현력이 현금영수증보다 떨어지는 것들입니다. 그럼에도 불구하고 현대의 모든 터미널 에뮬레이터는 굵은 글꼴, 이탤릭, 색상 및 Unicode 프레임 그리기 문자를 지원합니다. 터미널 이 할 수 있는 것과을 보여주고, 또 그것은 대부분의 CLI 도구들이 을 허락하여 을 보여주기 때문에, 규모는 그랜드 캐년과 비슷하며 깊이는 마리아나 해구의 무관심함과 사용자에 대한 무관심과 같습니다.
Marcli 이번에는 이 틈을 닫습니다. CommonMark Markdown을 가져와 터미널용으로 스타일링된 ANSI 출력을 내놓습니다. 당신은 Markdown을 작성합니다. 당신의 사용자들은 아름답게 포맷된 텍스트를 볼 수 있습니다. 전체 아이디어는 한 문장에 들어 있으며, 이 정도면 정확히 충분합니다.

이것이 무엇을 합니다
Marcli는 comrak을 통해 Markdown을 파싱합니다 — GitHub에서 렌더링을 수행하는 동일한 파서 — AST를 순회하며 지원되는 각 요소에 대해 ANSI 이스케이프 시퀀스를 생성합니다. 코드 블록 내에서의 구문 강조는 syntect에 의해 제공됩니다. — Sublime Text과 동일한 문법을 사용합니다. Pygments 호출 없이, 런타임에서의 로드 없이, 구성 시의 잔치 없이. 단순히 작동한다 — 다른 생태계가 보이는 것처럼 극단적인 개념입니다.
하나의 함수 호출:
let output = marcli::render("# Hello\n\nSome **bold** text.", &Default::default());
println!("{}", output); 일반적인 경우에 대한 공개 API 전부입니다. 함수 render은 마크다운 문자열과 RenderOptions 구조를 받아 반환합니다.String ANSI 시퀀스가 삽입되어 있습니다. 17개의 호출로 이어지는 "건설자" 패턴은 없습니다. 미리 구현해야 하는 트레이트는 없습니다. 의존성 트리에 숨겨진 비동기 런타임은 없으며, 적절한 순간에 당신의 빌드를 삼켜버리기를 기다리지 않습니다.
지원되는 요소
Marcli는 CommonMark의 전체 사양과 몇 가지 확장을 처리합니다:
제목 — h1은 노란색 굵게, h2는 파란색 굵게, h3 이하는 흰색 굵게 표시됩니다. 세 가지 수준의 시각적 계층 구조에 단일 구성 줄 없음.
인라인 형식화 — 굵은 글씨, 이타, 취소선 및 인라인 코드, 각각 자신만의 ANSI 스타일을 가지고 있습니다.
주석付き 목록 — 삼각형 마커(▸) 대신 일반적인 ASCII 하이픈 사용. 사소한 차이지만 읽기 쉬움이 비례 없이 향상됩니다.
번호 목록 — ①, ②, ③, …와 같이 둘러싼 숫자 글리프를 20개까지 사용합니다. 20개 이상은 괄호 안의 숫자를 사용합니다. 아직 누구도 불평하지 않았지만, 건강한 지성을 가진 사람은 21개 항목의 목록을 만들지 않습니다.
둘러싼 코드 블록 — Unicode 그림자 안에 언어 제목을 표시한 틀 안에. 언어를 지정하면 자동으로 구문 강조가 활성화됩니다.
인용구 — 왼쪽에 흐릿한 수직선으로 구분되어 주변 텍스트와 시각적으로 다른 인용구입니다.
표 — 그림자로 만든 완전한 틀로 열 정렬이 올바르게 되어 있습니다 (왼쪽, 오른쪽, 중앙). 제목은 굵게 강조됩니다.
할 일 목록 — 체크박스 마커(☑ / ☐).
주제 구분선 — 그림자로 만든 흐릿한 수평선.
링크 — 텍스트가 파란색으로 강조되어 있고, 그 뒤에는 URL이 어두운 글씨로 표시됩니다.
이미지 — URL과 괄호 안에 대체 텍스트가 있으며, 터미널은 우리의 절망적인 노력에도 불구하고 여전히 이미지를 볼 수 있는 프로그램이 아니기 때문입니다.
이것은 가난한 집단의 부분집합이 아닙니다. 이것은 합리적인 사람이 터미널에 표시하고자 하는 모든 것이며 그 이상은 아닙니다.
구성 설정 없이 작동하는 구문 강조
언어를 지정한 코드 블록은 자동으로 syntect의 내장 문법으로 강조 표시됩니다. Rust, Python, Elixir, JavaScript, Go, C, SQL, TOML, YAML, 자체 Markdown — syntect가 언어를 알면 Marcli가 강조 표시합니다. 알지 못하면 해당 프레임 안에서 일반적인 스타일링된 텍스트로 그리는 것입니다. 어떤 예외도 경고도 경험치 하락도 없습니다 — 단순히 색상 없는 코드만입니다. 산업이 지질학적 과정의 속도로 '처음 보는 입력으로 예외가 발생하지 않는' 혁신적인 개념에 나아가고 있음으로 보입니다.
fn main() {
let md = "```rust\nfn greet(name: &str) {\n println!(\"Hello, {}!\", name);\n}\n```";
let output = marcli::render(md, &Default::default());
println!("{}", output);
}주요 키워드는 하나의 색으로, 줄은 다른 색으로 표시되며, 주석은 어두워지고 이탤릭으로 강조되고, 함수 이름은 전체 배경에서 강조됩니다. TextMate-스코프에서 syntect를 ANSI 시퀀스로 매핑하는 기능이 내장되어 있으며 모든 주요 토큰 카테고리를 다룹니다: 주요 키워드, 문자열, 숫자, 주석, 연산자, 이름, 저장 변수 수정자, 엔티티 이름, 지원 매크로, 상수, 변수 및 차이점 마커. 각 카테고리는 합리적인 기본값을 가지고 있으며 각각을 재정의할 수 있습니다.
키워드 하이라이트를 완전히 비활성화할 수 있습니다:
let mut theme = marcli::Theme::default();
theme.syntax_highlight = false;
let opts = marcli::RenderOptions { theme, ..Default::default() };TOML을 통해 전체적인 색상 테마 설정
Marcli의 출력의 각 시각적 측면은 구조에 의해 제어됩니다Theme : 제목 색상, 목록 기호, 코드 블록 테두리, 표 기호, 구문 강조 토큰 스타일, 테마 구분선 폭, 인용 앞접두사 — 모두. 기본값은 합리적이지만, 기업 색상이나 접근성을 위한 높은 대비 테마가 필요하다면 관심 있는 정확히 해당 필드만 재정의하고 나머지는 건드리지 않는다. Marcli는 한 매개변수를 변경하면 전체 구성을 처음부터 다시 빌드해야 하며, "일관성"에 대해 무언가를 중얼거리는 종류의 라이브러리가 아니다.
테마는 TOML 파일에서 로드됩니다:
let theme = marcli::Theme::load(".marcli.toml").unwrap_or_default();
let opts = marcli::RenderOptions { theme, ..Default::default() };
let output = marcli::render(markdown, &opts);일부 TOML 파일은 chỉ 지정된 필드를 재정의하며, 나머지는 기본값으로 남아있습니다. 알 수 없는 키는 조용히 무시되므로 테마는 향후 버전과 호환됩니다.
테마 시스템은 특징 플래그를 통해 나중에 부착된 수리대가 아닙니다. 이것은 아키텍처입니다. 렌더러는 단 한 개의 하드코딩된 ANSI 시퀀스도 포함하고 있지 않습니다 - 각 스타일은 구조에서 읽어옵니다Theme 렌더링 시간에. 이는 완전히 스타일링되지 않은 출력을 생성할 수 있음을 의미하며, 모든 스타일을 빈 문자열로 설정하거나 특정 터미널의 기능에 맞게 출력을 조정할 수 있습니다. 디자인이 너무 명백해서 그 이유를 설명해야 하는 것에 놀랍니다.
xterm.js와 웹 터미널용 CRLF 지원
CLI 도구가 웹 터미널에서도 작동한다면(xterm.js 같은 경우), 줄 바꿈이 중요합니다. Marcli는 줄 바꿈 시퀀스를 설정할 수 있습니다.
let opts = marcli::RenderOptions {
newline: "\r\n".into(),
..Default::default()
};
let output = marcli::render(markdown, &opts); 목록 항목 사이, 코드 블록 내부, 단락 사이의 모든 내부 줄 바꿈은 설정된 시퀀스를 사용합니다. 후처리 정규식은 없습니다. \n를 "바꾸지 마세요"\r\n 이고 기도할 것이다». 구조적으로 올바른 접근 방식은 일부 개발자들에게 문화적 충격에 가까운 상태를 유발한다.
ANSI 제거를 위한 순수 텍스트
때로는 색상 없이 구조화된 렌더링이 필요하다 — 로깅을 위해, 파일로 리디렉션을 위해, 이스케이프 시퀀스로 압박되는 접근성 도구를 위해. 옵션 escape_sequences는 이를 해결한다:
let opts = marcli::RenderOptions {
escape_sequences: false,
..Default::default()
};
let output = marcli::render(markdown, &opts);
// output содержит структурированный текст
// с маркерами и отступами, но без ANSI-кодовMarcli는 필요할 경우 escape-sequence를 제거하여 임의의 텍스트를 strip_ansi를 공개 함수로 내보낸다:
let plain = marcli::strip_ansi(some_ansi_string);의존성
Marcli는 여섯 개의 crate에 의존한다: comrak (Markdown 파싱), syntect (구문 강조), serde와 toml (테마 직렬화), regex (ANSI 제거) 및once_cell (懶惰한 정적 파일들). 모든 기능이 가능한 한 비활성화된 상태로 시작됩니다. tokio가 없습니다. hyper가 없습니다. tower가 없습니다. serde_json가 없습니다. 조합적 폭발적인 feature-플래그가 없습니다. serde가 요구하는 것을 넘어서는 절차적 매크로가 없습니다.
의존성 트리는 의도적으로 좁게 설계되었습니다. CLI 도구로 Marcli를 추가하여 출력을 렌더링합니다.--help 또는 오류 진단이 갑자기 HTTP 클라이언트와 TLS 스택을 상속하지 않습니다. Rust 세계에서 날짜 형식을 지정하는 크레이트를 추가하는 것은 종종 네트워크 스택의 절반을 끌어들이기 때문에 이는 별도로 언급할 만하며 - 그리고 분명히 샴페인 잔 하나를 마시기에 충분합니다.
왜 Marcli인가, 대안은 아니지?
Rust 생태계는 터미널에서 스타일링된 출력을 위한 여러 접근 방식을 제공하며, 각각은 자신의 작업을 해결합니다. 문제는 대부분이 당신의 문제를 해결하지 못한다는 것입니다.
「칠하기」(예: colored, owo-colors, ansi_term)는 그림자입니다. 이들은 개별 줄을 그림으로 채울 수 있게 합니다. 이들은 구조를 분석하지 않고, 목록을 처리하지 않으며, 표를 렌더링하지 않으며, 코드를 강조하지 않습니다. 만약 내용이 이미 구조화되어 있다면, 모든 포맷팅 로직을 직접 작성해야 합니다. Marcli는 비구조화된 Markdown을 받아서 완전히 구조화된 터미널 출력을 생성합니다. 완전히 다른 추상화 계층입니다. 비교하는 것은 마치 망치와 집 중 어느 것이 더 좋다고 묻는 것과 같습니다.
TUI-프레임워크 (예: ratatui, tui-rs)는 터미널 UI의 완전한 도구입니다. 이들은 레이아웃, 위젯, 이벤트 루프, 대체 화면 버퍼를 관리합니다. 인터랙티브 애플리케이션을 만들고 있다면 이것이 최고입니다. 스타일 있는 도움말을 인쇄하고 나가야 한다면 이것은 사무용 버튼용 망치입니다. Marcli는 사무용 버튼만큼 큰 도구입니다. 더 큰 것을 요구하는 것은 없으며, 그래서 다행입니다.
termimad — 가장 가까운 대안입니다. 마찬가지로 터미널에서 Markdown을 렌더링합니다. 하지만 termimad는 자체 파서를 사용합니다 (CommonMark과 호환되지 않음), 다른 (그리고 더 투명하지 않은) 테마 모델을 가지고 있으며 syntect를 통해 구문 강조를 제공하지 않습니다. Marcli는 comrak을 파싱에 사용합니다 (CommonMark, GitHub과 호환됨), syntect를 통해 구문 강조를 사용합니다 (Sublime Text 문법), 그리고 모든 시각적 파라미터를 평평하고 직렬화 가능한 구조를 통해 설정합니다.Theme는 구성 사양과 구성 가능성을 중시한다면 선택이 명확합니다.
mdcat — 명령줄 도구이지 라이브러리가 아닙니다. 이를 자신의 바이너리에 통합하고 render()을 호출할 수 없습니다. 이는 외부 프로세스로 실행되거나 사용자의 머신에 자신의 바이너리가 필요합니다. Marcli는 라이브러리입니다: 추가하세요.Cargo.toml 함수를 호출하고 문자열을 얻으세요. 공동 거주 의무는 없습니다.
가볍기 때문에 좋은 논거
모든 Rust CLI 라이브러리가 사람들과 소통할 때 동일한 질문에 직면합니다: 구조화된 텍스트를 어떻게 제공할 것인가? 답은 거의 항상 세 가지 중 하나입니다: ⓐ 그대로 텍스트를 내뱉고 사용자가 읽을 것이라는 희망을 걸다(의심할 여지 없이 극단적인 낙관주의), ⓑ 수십 개의 호출로 ANSI 형식 지정을 수동으로 적용하다format!는 하드코딩된 escape 시퀀스(마술과 함께의 마조힘)와 함께, ⓒ 더 무거운 프레임워크를 끌어올리는 것(세계의 node modules의 아키텍처적 해결책).
Marcli는 옵션 ⓓ을 제안합니다: 메시지, 참조 텍스트, 오류 설명, 변경 로그 및 진단을 마크다운 형식으로 작성하세요 — 이 형식은 이미 팀이 알고 있으며, 편집기가 이미 강조 표시하고 있으며, 문서화 파이프라인이 이미 렌더링하고 있으며 — 함수 호출만으로 이를 사람들이 원하는 형태의 터미널 출력으로 변환할 수 있습니다.
가격 — 함수 호출 하나와 여섯 가시적인 종속성. 이점 — CLI가 세상에 발행하는 각 텍스트 조각은 굵게, 기울임꼴로, 구문 강조로, 목록으로, 표로, 인용하며, 테마를 완전히 지원하고 ANSI 시퀀스에 대한 수동 조작을 전혀 하지 않을 수 있습니다.
당신의 터미널은 더 나은 것을值得합니다.println! 마르클리 — 그에게 최상의 것을 주는 방법입니다. 만약 그럴 자격이 없다면 — 그래도 당신의 사용자들은 그럴 자격이 있습니다.
링크
▸ 저장소: github.com/Oeditus/marcli-rust
▸ 문서화: docs.rs/marcli
▸ 크레이트: crates.io/crates/marcli












