.remote-cache/kreuzberg-shared-rules/.ai-rulez/skills/rust-module-organization-public-api-design/SKILL.md
______________________________________________________________________ ## priority: high # Rust Module Organization & Public API Design ## Module Structure Principles **Modules are organizational units, not visibility boundaries**. Use `pub` / `pub(crate)` / `pub(super)` to control visibility explicitly. 1. **Root crate module**: `src/lib.rs` or `src/main.rs` re-exports public items 1. **Feature-specific modules**: `src/parsing/`, `src/conversion/`, `src/utils/` organize by domain 1. **Inte
npx skillsauth add kreuzberg-dev/html-to-markdown .remote-cache/kreuzberg-shared-rules/.ai-rulez/skills/rust-module-organization-public-api-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Modules are organizational units, not visibility boundaries. Use pub / pub(crate) / pub(super) to control visibility explicitly.
src/lib.rs or src/main.rs re-exports public itemssrc/parsing/, src/conversion/, src/utils/ organize by domainpub(crate) if they're internal infrastructurepub use in lib.rs to define the public API surfacesrc/
├── lib.rs # Root: re-exports public API
├── parsing/ # Feature module
│ ├── mod.rs # Module interface, re-exports
│ ├── html_parser.rs # Internal implementation
│ ├── sanitizer.rs # Internal implementation
│ └── error.rs # Domain-specific errors
├── conversion/ # Feature module
│ ├── mod.rs # Module interface
│ ├── node_visitor.rs # Implementation
│ └── formatters/ # Submodule
│ ├── mod.rs
│ ├── markdown.rs
│ └── text.rs
├── error.rs # Crate-wide error types
└── config.rs # Configuration
Golden Rule: Your src/lib.rs should read like a user guide.
//! html-to-markdown: Convert HTML to Markdown with safety & performance
//!
//! # Quick Start
//! ```
//! use html_to_markdown::HtmlConverter;
//! let converter = HtmlConverter::new();
//! let markdown = converter.convert("<h1>Hello</h1>")?;
//! ```
// Public types
pub use crate::conversion::{HtmlConverter, ConversionConfig};
pub use crate::parsing::ParseError;
pub use crate::error::Error;
// Public type aliases for convenience
pub type Result<T> = std::result::Result<T, Error>;
// Don't re-export implementation details
// pub use crate::parsing::html_parser; // BAD
Public (pub): Part of stable API contract
HtmlConverter, ConversionConfigParseError, ConversionErrorPub(crate) (pub(crate)): Internal infrastructure, not for external users
pub(crate) struct InternalParser { ... }
pub(crate) fn internal_helper() { ... }
Private (no visibility keyword): Never exposed
fn internal_detail() { ... }
struct InternalState { ... }
Use Cargo features to conditionally expose API surface:
[features]
default = ["parsing"]
parsing = []
async-runtime = ["tokio"]
ffi = ["libffi"]
[[example]]
name = "async_convert"
required-features = ["async-runtime"]
// Conditional re-export
#[cfg(feature = "async-runtime")]
pub use crate::async_convert::AsyncConverter;
#[cfg(feature = "ffi")]
pub use crate::ffi::ExportedAPI;
Module interface (src/parsing/mod.rs):
//! HTML parsing module with robust error handling
mod html_parser;
mod sanitizer;
pub mod error;
pub use self::html_parser::{Parser, ParseConfig};
pub use self::sanitizer::Sanitizer;
// Internal utilities not re-exported
use self::html_parser::HtmlToken;
Root re-export (src/lib.rs):
pub use crate::parsing::{Parser, ParseConfig};
pub use crate::parsing::error::{ParseError, SyntaxError};
pub use crate::conversion::HtmlConverter;
// Don't expose internal modules
// Bad: pub mod parsing;
Use cargo-public-api to track breaking changes:
# Generate baseline
cargo public-api --baseline > baseline.txt
# Check for breaking changes in PRs
cargo public-api --diff baseline.txt
# Track additions
cargo public-api > current.txt
Add to CI:
- name: Check public API
run: |
cargo install cargo-public-api
cargo public-api --diff baseline.txt
Exposing implementation details:
// BAD: Leaks internal parser state
pub use crate::parsing::internal::ParserState;
// GOOD: Expose stable interface
pub fn parse(input: &str) -> Result<Document> { ... }
Deeply nested modules in public API:
// BAD: Public `mod parsing { mod html_parser { ... } }`
// Users write: html_to_markdown::parsing::html_parser::Parser
// GOOD: Flatten in re-export
// Users write: html_to_markdown::Parser
Mixed public/private in single module:
// BAD: Confusing what's stable
pub fn stable_api() { ... }
pub fn internal_detail() { ... }
// GOOD: Separate concerns
pub mod api { pub fn stable() { ... } }
mod internal { pub(crate) fn detail() { ... } }
Not documenting API stability:
// BAD: Users don't know if this might change
pub struct Config { ... }
// GOOD: Clear stability guarantees
/// Configuration for HTML conversion (part of stable 1.x API)
pub struct Config { ... }
/// Converts HTML to Markdown
///
/// # Examples
/// ```
/// use html_to_markdown::HtmlConverter;
/// let converter = HtmlConverter::new();
/// let md = converter.convert("<h1>Title</h1><p>Content</p>")?;
/// assert!(md.contains("# Title"));
/// ```
///
/// # Errors
/// Returns `ParseError` if HTML is malformed beyond recovery.
pub fn convert(html: &str) -> Result<String> { ... }
Document API stability intentions:
// Stable in 1.x API
/// Configuration builder (stable API)
pub struct ConfigBuilder { ... }
// Experimental, may change
/// Experimental: May change in minor versions
#[doc(alias = "deprecated")]
pub struct ExperimentalFeature { ... }
// Deprecated
/// Deprecated: Use `convert_v2()` instead
#[deprecated(since = "0.5.0", note = "use convert_v2")]
pub fn convert_old(input: &str) -> Result<String> { ... }
tools
Convert HTML to Markdown, Djot, or plain text with structured extraction. Use when writing code that calls html-to-markdown APIs in Rust, Python, TypeScript, Go, Ruby, PHP, Java, C#, Elixir, R, C, or WASM. Covers installation, conversion, configuration, metadata extraction, document structure, and CLI usage.
development
Developer quick start guide with prerequisites, setup, and workflow commands
development
Common task runner commands for build, test, lint, and format workflows
tools
______________________________________________________________________ ## priority: high # Workspace Structure & Project Organization **Rust workspace** (Cargo.toml): crates/{kreuzberg,kreuzberg-py,kreuzberg-node,kreuzberg-ffi,kreuzberg-cli}, packages/ruby/ext/kreuzberg_rb/native, tools/{benchmark-harness,e2e-generator}, e2e/{rust,go}. **Language packages**: packages/{python,typescript,ruby,java,go} - thin wrappers around Rust core. **E2E tests**: Auto-generated from fixtures/ via tools/e2e