Rust Crate API

Public exports from lectito:

The crate exposes the extraction API, output structs, trope diagnostics, errors, and Markdown helpers.

#![allow(unused)]
fn main() {
pub use config::{Article, MarkdownOptions, MediaRetention, ReadabilityOptions, ReadableOptions};
pub use diagnostics::{
    AttemptDiagnostic, CandidateDiagnostic, CandidateSelection,
    CleanupDiagnostic, ContentSelectorDiagnostic, ExtractionDiagnostics,
    ExtractionOutcome, ExtractionReport, FlagDiagnostic, NodeDiagnostic,
    RecoveryDiagnostic,
};
pub use error::Error;
pub use extract::{clean_article_html, extract, extract_with_diagnostics};
pub use markdown::{html_to_markdown, markdown_to_html, markdown_with_toml_frontmatter};
pub use readable::is_probably_readable;
pub use tropes::analyze_tropes;
pub use tropes::{
    RuleSource, Severity, TextRange, TropeCategory, TropeFinding, TropeOptions,
    TropePreset, TropeReport, TropeSignal, TropeSummary,
};
}

Extraction

Use extract for normal application code.

#![allow(unused)]
fn main() {
pub fn extract(
    html: &str,
    base_url: Option<&str>,
    options: &ReadabilityOptions,
) -> Result<Option<Article>, Error>
}

Returns Ok(Some(article)) when content is found, Ok(None) when the document has no useful article content, and Err for invalid input or processing failures.

Extraction tries JSON-LD article text and common article-body containers before generic readability scoring.

Set content_selector when you already know the article root.

Set disable_json_ld when structured data is wrong for the page.

Use extract_with_diagnostics when you need extraction details in addition to the article.

#![allow(unused)]
fn main() {
pub fn extract_with_diagnostics(
    html: &str,
    base_url: Option<&str>,
    options: &ReadabilityOptions,
) -> Result<ExtractionReport, Error>
}

Returns the same article result with extraction diagnostics.

Use clean_article_html when you only need the cleaned article HTML.

#![allow(unused)]
fn main() {
pub fn clean_article_html(
    html: &str,
    base_url: Option<&str>,
    options: &ReadabilityOptions,
) -> Result<Option<String>, Error>
}

Readability Check

Use is_probably_readable before full extraction when you are filtering many documents.

#![allow(unused)]
fn main() {
pub fn is_probably_readable(
    html: &str,
    options: &ReadableOptions,
) -> Result<bool, Error>
}

Returns a quick readability estimate without full extraction.

Markdown

The Markdown helpers are available separately for callers that already have a clean HTML fragment, want to render Markdown as HTML, or want CLI-style frontmatter.

#![allow(unused)]
fn main() {
pub fn html_to_markdown(html: &str) -> String
}

Converts HTML fragments to Markdown.

#![allow(unused)]
fn main() {
pub fn markdown_to_html(markdown: &str, options: &MarkdownOptions) -> String
}

Converts Markdown to HTML using CommonMark/GFM options.

#![allow(unused)]
fn main() {
pub fn markdown_with_toml_frontmatter(
    article: &Article,
    source: Option<&str>,
) -> Result<String, Error>
}

Formats an article as Markdown with TOML frontmatter.

Trope Diagnostics

Use analyze_tropes to inspect text for AI-like writing tropes and other formulaic patterns.

The function is local and deterministic so it does not fetch content, call a model, or make an authorship judgment.

#![allow(unused)]
fn main() {
use lectito::{analyze_tropes, TropeOptions};

let text = "It is worth noting that this serves as an example.";
let report = analyze_tropes(text, &TropeOptions::default());

println!("{:?}: {}", report.signal, report.summary.finding_count);
for finding in report.findings {
    println!("{}: {}", finding.rule_id, finding.matched_text);
}
}

A trope report is a heuristic writing diagnostic. It reports matched phrases and structural patterns; it does not determine authorship or prove that a person or model wrote the text.

TropeOptions accepts a preset, a min_severity filter, and include_suggestions. TropePreset::Balanced is the default. Lenient omits low-severity findings and uses higher evidence thresholds for repeated patterns. Strict keeps low-severity findings and lowers some structural and document thresholds, so it can be noisier. Findings include the matched text, byte and UTF-16 ranges, line and column positions, category, severity, and an optional editing suggestion.

TropeReport::score is a 0–100 density heuristic, not a probability. The signal field groups that score into low, medium, high, or very_high. summary includes word and finding counts, findings per 1,000 words, category and severity counts, top categories, and repeated rule IDs.

The built-in catalog is vendored at crates/core/src/tropes/tropes.md and normalized into crates/core/src/tropes/rules.json. It is based on the AI Writing Tropes to Avoid catalog from tropes.fyi. Each built-in finding carries the source file and heading that produced it.

Custom rules are not supported by the current public API. Callers can filter the bundled rules by severity, but cannot add patterns to the analyzer.

Errors

Extraction and conversion functions return Result<_, lectito::Error>. An empty or non-article document is represented by Ok(None) from extract; it is not an error.

The most relevant extraction errors are:

  • Error::InvalidBaseUrl when the optional base URL cannot be parsed.
  • Error::MaxElemsExceeded when the document exceeds ReadabilityOptions::max_elems_to_parse.
  • Error::InvalidSiteProfile when a TOML site profile cannot be parsed or converted into selectors.
  • Error::HtmlParse when HTML parsing fails before extraction starts.
  • Error::Serialization when the cleaned article cannot be serialized.

The public constructors Error::invalid_site_profile and Error::max_elems_exceeded create the corresponding structured variants when an integration needs to preserve Lectito's error shape.