schemaorg_rs/wasm.rs
1//! WASM bindings for `schemaorg-rs`.
2//!
3//! Exposes three functions to JavaScript:
4//!
5//! - [`extract`] -- parse HTML and return structured data as JSON
6//! - [`validate_html`] -- full pipeline: extract -> validate -> profile evaluate
7//! - [`schema_version`] -- returns the Schema.org vocabulary version
8//!
9//! All functions return JSON strings. The JS wrapper in `wasm/index.js`
10//! calls `JSON.parse()` on the results.
11
12use wasm_bindgen::prelude::*;
13
14use crate::graph;
15use crate::profiles::ProfileRegistry;
16use crate::validation;
17
18/// Initializes the WASM module. Call once before other functions.
19/// Sets up a panic hook that logs to `console.error`.
20#[wasm_bindgen(start)]
21pub fn wasm_start() {
22 std::panic::set_hook(Box::new(|info| {
23 let msg = info.to_string();
24 log_error(&msg);
25 }));
26}
27
28#[wasm_bindgen]
29extern "C" {
30 #[wasm_bindgen(js_namespace = console, js_name = error)]
31 fn log_error(s: &str);
32}
33
34/// Extracts structured data from HTML and returns it as a JSON string.
35///
36/// The returned JSON contains an array of `SchemaNode` objects with their
37/// types, properties, source format, and source location.
38///
39/// # Returns
40///
41/// JSON string: `{ "nodes": [...], "warnings": [...] }` on success,
42/// or `{ "error": "..." }` on failure.
43#[wasm_bindgen]
44pub fn extract(html: &str) -> String {
45 match graph::extract_all(html) {
46 Ok(graph) => {
47 let result = serde_json::json!({
48 "nodes": graph.nodes,
49 "warnings": graph.warnings,
50 });
51 serde_json::to_string(&result)
52 .unwrap_or_else(|e| serde_json::json!({ "error": e.to_string() }).to_string())
53 }
54 Err(e) => serde_json::json!({ "error": e.to_string() }).to_string(),
55 }
56}
57
58/// Full validation pipeline: extract -> vocab validate -> profile evaluate.
59///
60/// Runs extraction, Schema.org vocabulary validation, and profile evaluation
61/// in a single call. Returns a combined JSON result.
62///
63/// # Arguments
64///
65/// - `html` -- the HTML document to analyze
66/// - `profile` -- the profile name to evaluate against (`"google"` or `"baseline"`)
67///
68/// # Returns
69///
70/// JSON string containing:
71/// ```json
72/// {
73/// "extraction": { "nodes": [...], "warnings": [...] },
74/// "validation": { "diagnostics": [...], "has_errors": bool },
75/// "profile": { "eligibility": "...", "type_results": [...], "diagnostics": [...] }
76/// }
77/// ```
78///
79/// Or `{ "error": "..." }` on extraction failure.
80#[wasm_bindgen]
81pub fn validate_html(html: &str, profile: &str) -> String {
82 // Step 1: Extract
83 let graph = match graph::extract_all(html) {
84 Ok(g) => g,
85 Err(e) => return serde_json::json!({ "error": e.to_string() }).to_string(),
86 };
87
88 // Step 2: Vocabulary validation
89 let vocab_result = validation::validate(&graph);
90
91 // Step 3: Profile evaluation
92 let registry = match profile {
93 "baseline" => ProfileRegistry::with_baseline(),
94 _ => ProfileRegistry::with_google(),
95 };
96
97 let profile_result = registry.evaluate(profile, &graph, &vocab_result.diagnostics);
98
99 let profile_json = match profile_result {
100 Ok(r) => serde_json::json!({
101 "eligibility": r.eligibility.to_string(),
102 "type_results": r.type_results,
103 "diagnostics": r.diagnostics,
104 }),
105 Err(e) => serde_json::json!({
106 "eligibility": "NotEligible",
107 "type_results": [],
108 "diagnostics": [],
109 "note": e.to_string(),
110 }),
111 };
112
113 let result = serde_json::json!({
114 "extraction": {
115 "nodes": graph.nodes,
116 "warnings": graph.warnings,
117 },
118 "validation": {
119 "diagnostics": vocab_result.diagnostics,
120 "has_errors": vocab_result.has_errors(),
121 },
122 "profile": profile_json,
123 });
124
125 serde_json::to_string(&result)
126 .unwrap_or_else(|e| serde_json::json!({ "error": e.to_string() }).to_string())
127}
128
129/// Returns the Schema.org vocabulary version used by this build.
130#[wasm_bindgen]
131pub fn schema_version() -> String {
132 crate::vocabulary::schema_version().to_string()
133}