1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! **mocra** — a distributed, event-driven crawling and data-collection framework
//! that runs as an embeddable Rust library.
//!
//! Implement a [`Spider`](crate::facade::Spider) and run it with
//! [`Mocra::builder`](crate::facade::Mocra::builder) — **no database and no Redis**
//! required on a single node. Typed output is delivered through
//! [`DataSink`](crate::facade::DataSink) / [`on_item`](crate::facade::on_item).
//!
//! ```no_run
//! use async_trait::async_trait;
//! use mocra::prelude::*;
//! use serde::Serialize;
//!
//! #[derive(Debug, Serialize)]
//! struct Page {
//! url: String,
//! status: u16,
//! }
//!
//! struct MySpider;
//!
//! #[async_trait]
//! impl Spider for MySpider {
//! type Item = Page;
//!
//! fn name(&self) -> &str {
//! "my_spider"
//! }
//!
//! async fn start(&self, seeds: &mut Seeds) {
//! seeds.get("https://httpbin.org/get");
//! }
//!
//! async fn parse(&self, res: Response, cx: &mut Ctx<Self::Item>) -> Result<()> {
//! cx.emit(Page {
//! url: res.module_id(),
//! status: res.status_code,
//! });
//! Ok(())
//! }
//! }
//!
//! #[tokio::main]
//! async fn main() -> Result<()> {
//! Mocra::builder()
//! .spider(
//! MySpider,
//! on_item(|page: Page| async move {
//! println!("{} -> {}", page.url, page.status);
//! }),
//! )
//! .run()
//! .await
//! }
//! ```
//!
//! # Beyond a single node
//!
//! - **Cluster** (`cluster-embedded`): a self-organizing Raft + redb control plane via
//! `Mocra::builder().cluster(..)` — no external ZooKeeper / etcd / Redis.
//! - **Dashboard** (`dashboard`): `.dashboard(port)` serves a built-in web dashboard
//! plus a read-only, CORS-enabled observability API (metrics / logs / tasks / performance).
//!
//! See the [`prelude`] for the curated public surface and [`facade`] for the entry types.
// 结构性 clippy lint —— 现有设计取舍(参数数、类型复杂度、模块同名、error/枚举变体尺寸),
// 非 bug;统一豁免,便于逐步对主 crate 收紧 `-D warnings`。
// 高层 `Spider` 门面(重构 Phase 1)—— 面向 80% 场景的简单入口。
// 模块级文档见 `facade.rs` 顶部的 `//!`(此处用普通注释,避免与其内部
// `//!` 合并后在 crate 根作用域解析、导致 intra-doc 链接失效)。
use MiMalloc;
static GLOBAL: MiMalloc = MiMalloc;