summaryrefslogtreecommitdiff
path: root/library/src/meta/query.rs
blob: c91f0d1aee5aba8a5033de8efa59aba6cccb8f09 (plain) (blame)
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
use crate::prelude::*;

/// Find elements in the document.
///
/// Display: Query
/// Category: meta
/// Returns: content
#[func]
pub fn query(
    /// The thing to search for.
    target: Target,
    /// A function to format the results with.
    format: Func,
) -> Value {
    QueryNode::new(target.0, format).pack().into()
}

/// A query target.
struct Target(Selector);

cast_from_value! {
    Target,
    label: Label => Self(Selector::Label(label)),
    func: Func => {
        let Some(id) = func.id() else {
            return Err("this function is not selectable".into());
        };

        if !Content::new(id).can::<dyn Locatable>() {
            Err(eco_format!("cannot query for {}s", id.name))?;
        }

        Self(Selector::Node(id, None))
    }
}

/// Executes a query.
///
/// Display: Query
/// Category: special
#[node(Locatable, Show)]
pub struct QueryNode {
    /// The thing to search for.
    #[required]
    pub target: Selector,

    /// The function to format the results with.
    #[required]
    pub format: Func,
}

impl Show for QueryNode {
    fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
        if !vt.introspector.init() {
            return Ok(Content::empty());
        }

        let id = self.0.stable_id().unwrap();
        let target = self.target();
        let (before, after) = vt.introspector.query_split(target, id);
        let func = self.format();
        let args = Args::new(func.span(), [encode(before), encode(after)]);
        Ok(func.call_detached(vt.world, args)?.display())
    }
}

fn encode(list: Vec<&Content>) -> Value {
    Value::Array(list.into_iter().cloned().map(Value::Content).collect())
}