specfold_core/assets/
asset.rs1use super::join;
4use crate::config::types::AssetKind;
5use crate::graph::{Graph, Resolution};
6use crate::markdown::frontmatter;
7use serde_json::{Map, Value};
8use std::collections::BTreeSet;
9
10#[derive(Debug, Clone)]
11pub struct Asset {
12 pub kind: AssetKind,
13 pub path: String,
15 pub stem: String,
17 pub fields: Option<Map<String, Value>>,
19 pub invalid: Option<String>,
21 pub body_line: usize,
23 pub text: String,
24}
25
26impl Asset {
27 pub fn field(&self, key: &str) -> Option<&str> {
29 self.fields.as_ref()?.get(key)?.as_str()
30 }
31
32 pub fn name(&self) -> &str {
34 self.field("name").unwrap_or(&self.stem)
35 }
36
37 pub fn body(&self) -> &str {
38 frontmatter::split(&self.text).body
39 }
40
41 pub fn docs(&self, g: &Graph) -> Vec<usize> {
45 let dir = self.path.rsplit_once('/').map(|(d, _)| d).unwrap_or("");
46 let mut out = BTreeSet::new();
47 let ok = |c: char| c.is_ascii_alphanumeric() || ":/._#-".contains(c);
48 for raw in self.text.split(|c: char| !ok(c)) {
49 let t = raw.trim_end_matches(['.', ':', '#']);
50 if !(t.contains(':') || t.contains('/') || t.ends_with(".md")) || t.contains("://") {
51 continue;
52 }
53 for cand in [t.to_string(), join(dir, t)] {
54 if let Resolution::Doc { idx, via_alias: false, .. } = g.resolve(&cand) {
55 out.insert(idx);
56 break;
57 }
58 }
59 }
60 out.into_iter().collect()
61 }
62}