Packages
rbt_weave
0.2.64
0.2.67
0.2.66
0.2.65
0.2.64
0.2.63
0.2.62
0.2.61
0.2.60
0.2.59
0.2.58
0.2.57
0.2.56
0.2.55
0.2.54
0.2.53
0.2.52
0.2.51
0.2.50
0.2.49
0.2.48
0.2.47
0.2.46
0.2.45
0.2.44
0.2.43
0.2.42
0.2.41
0.2.40
0.2.39
0.2.38
0.2.37
0.2.36
0.2.35
0.2.34
0.2.33
0.2.32
0.2.31
0.2.30
0.2.29
0.2.28
0.2.27
0.2.26
0.2.25
0.2.24
0.2.23
0.2.22
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Graph conflict detection and image processing NIFs for OSINT knowledge graphs, powered by Rust.
Current section
Files
Jump to
Current section
Files
crates/weave-content/src/html/jsonld.rs
//! JSON-LD structured data and `OpenGraph` description builders.
use crate::output::{CaseOutput, NodeOutput};
use crate::parser::SourceEntry;
use super::countries::{country_name, format_jurisdiction};
use super::escape::{format_enum, truncate};
pub(crate) fn build_case_og_description(case: &CaseOutput) -> String {
if let Some(tagline) = &case.tagline {
return truncate(tagline, 200);
}
if !case.summary.is_empty() {
return truncate(&case.summary, 200);
}
let people_count = case.nodes.iter().filter(|n| n.label == "person").count();
let org_count = case
.nodes
.iter()
.filter(|n| n.label == "organization")
.count();
truncate(
&format!(
"{} people, {} organizations, {} connections",
people_count,
org_count,
case.relationships.len()
),
200,
)
}
pub(crate) fn build_person_og_description(node: &NodeOutput) -> String {
let mut parts = Vec::new();
if let Some(q) = &node.qualifier {
parts.push(q.clone());
}
if !node.role.is_empty() {
let roles: Vec<_> = node.role.iter().map(|r| format_enum(r)).collect();
parts.push(roles.join(", "));
}
if !node.nationality.is_empty() {
let names: Vec<String> = node.nationality.iter().map(|n| country_name(n)).collect();
parts.push(names.join(", "));
}
if parts.is_empty() {
return truncate(&node.name, 200);
}
truncate(&format!("{} — {}", node.name, parts.join(" · ")), 200)
}
pub(crate) fn build_org_og_description(node: &NodeOutput) -> String {
let mut parts = Vec::new();
if let Some(q) = &node.qualifier {
parts.push(q.clone());
}
if let Some(ot) = &node.org_type {
parts.push(format_enum(ot));
}
if let Some(j) = &node.jurisdiction {
parts.push(format_jurisdiction(j));
}
if parts.is_empty() {
return truncate(&node.name, 200);
}
truncate(&format!("{} — {}", node.name, parts.join(" · ")), 200)
}
pub(crate) fn render_case_json_ld(html: &mut String, case: &CaseOutput) {
let mut ld = serde_json::json!({
"@context": "https://schema.org",
"@type": "Article",
"headline": truncate(&case.title, 120),
"description": truncate(&case.summary, 200),
"url": format!("/{}", case.slug.as_deref().unwrap_or(&case.case_id)),
});
if !case.sources.is_empty() {
let urls: Vec<&str> = case
.sources
.iter()
.map(|s| match s {
SourceEntry::Url(u) => u.as_str(),
SourceEntry::Structured { url, .. } => url.as_str(),
})
.collect();
ld["citation"] = serde_json::json!(urls);
}
html.push_str(&format!(
" <script type=\"application/ld+json\">{}</script>\n",
serde_json::to_string(&ld).unwrap_or_default()
));
}
pub(crate) fn render_person_json_ld(html: &mut String, node: &NodeOutput) {
let mut ld = serde_json::json!({
"@context": "https://schema.org",
"@type": "Person",
"name": &node.name,
"url": format!("/{}", node.slug.as_deref().unwrap_or(&node.id)),
});
if !node.nationality.is_empty() {
let names: Vec<String> = node.nationality.iter().map(|n| country_name(n)).collect();
if names.len() == 1 {
ld["nationality"] = serde_json::json!(names[0]);
} else {
ld["nationality"] = serde_json::json!(names);
}
}
if let Some(desc) = &node.description {
ld["description"] = serde_json::json!(truncate(desc, 200));
}
if let Some(thumb) = &node.thumbnail {
ld["image"] = serde_json::json!(thumb);
}
html.push_str(&format!(
" <script type=\"application/ld+json\">{}</script>\n",
serde_json::to_string(&ld).unwrap_or_default()
));
}
pub(crate) fn render_org_json_ld(html: &mut String, node: &NodeOutput) {
let mut ld = serde_json::json!({
"@context": "https://schema.org",
"@type": "Organization",
"name": &node.name,
"url": format!("/{}", node.slug.as_deref().unwrap_or(&node.id)),
});
if let Some(desc) = &node.description {
ld["description"] = serde_json::json!(truncate(desc, 200));
}
if let Some(thumb) = &node.thumbnail {
ld["logo"] = serde_json::json!(thumb);
}
html.push_str(&format!(
" <script type=\"application/ld+json\">{}</script>\n",
serde_json::to_string(&ld).unwrap_or_default()
));
}