usage/spec/
helpers.rs

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
use indexmap::IndexMap;
use kdl::{KdlEntry, KdlNode, KdlValue};
use miette::SourceSpan;
use std::fmt::Debug;
use std::ops::RangeBounds;

use crate::error::UsageErr;
use crate::spec::context::ParsingContext;

#[derive(Debug)]
pub struct NodeHelper<'a> {
    pub(crate) node: &'a KdlNode,
    pub(crate) ctx: &'a ParsingContext,
}

impl<'a> NodeHelper<'a> {
    pub(crate) fn new(ctx: &'a ParsingContext, node: &'a KdlNode) -> Self {
        Self { node, ctx }
    }

    pub(crate) fn name(&self) -> &str {
        self.node.name().value()
    }
    pub(crate) fn span(&self) -> SourceSpan {
        *self.node.span()
    }
    pub(crate) fn ensure_arg_len<R>(&self, range: R) -> Result<&Self, UsageErr>
    where
        R: RangeBounds<usize> + Debug,
    {
        let count = self.args().count();
        if !range.contains(&count) {
            let ctx = self.ctx;
            let span = self.span();
            bail_parse!(ctx, span, "expected {range:?} arguments, got {count}",)
        }
        Ok(self)
    }
    pub(crate) fn get(&self, key: &str) -> Option<ParseEntry> {
        self.node.get(key).map(|e| ParseEntry::new(self.ctx, e))
    }
    pub(crate) fn arg(&self, i: usize) -> Result<ParseEntry, UsageErr> {
        if let Some(entry) = self.args().nth(i) {
            return Ok(entry);
        }
        bail_parse!(self.ctx, self.span(), "missing argument")
    }
    pub(crate) fn args(&self) -> impl Iterator<Item = ParseEntry> + '_ {
        self.node
            .entries()
            .iter()
            .filter(|e| e.name().is_none())
            .map(|e| ParseEntry::new(self.ctx, e))
    }
    pub(crate) fn props(&self) -> IndexMap<&str, ParseEntry> {
        self.node
            .entries()
            .iter()
            .filter_map(|e| {
                e.name()
                    .map(|key| (key.value(), ParseEntry::new(self.ctx, e)))
            })
            .collect()
    }
    pub(crate) fn children(&self) -> Vec<Self> {
        self.node
            .children()
            .map(|c| {
                c.nodes()
                    .iter()
                    .map(|n| NodeHelper::new(self.ctx, n))
                    .collect()
            })
            .unwrap_or_default()
    }
}

#[derive(Debug)]
pub(crate) struct ParseEntry<'a> {
    pub(crate) ctx: &'a ParsingContext,
    pub(crate) entry: &'a KdlEntry,
    pub(crate) value: &'a KdlValue,
}

impl<'a> ParseEntry<'a> {
    fn new(ctx: &'a ParsingContext, entry: &'a KdlEntry) -> Self {
        Self {
            ctx,
            entry,
            value: entry.value(),
        }
    }

    fn span(&self) -> SourceSpan {
        *self.entry.span()
    }
}

impl ParseEntry<'_> {
    pub fn ensure_usize(&self) -> Result<usize, UsageErr> {
        match self.value.as_i64() {
            Some(i) => Ok(i as usize),
            None => bail_parse!(self.ctx, self.span(), "expected usize"),
        }
    }
    #[allow(dead_code)]
    pub fn ensure_f64(&self) -> Result<f64, UsageErr> {
        match self.value.as_f64() {
            Some(f) => Ok(f),
            None => bail_parse!(self.ctx, self.span(), "expected float"),
        }
    }
    pub fn ensure_bool(&self) -> Result<bool, UsageErr> {
        match self.value.as_bool() {
            Some(b) => Ok(b),
            None => bail_parse!(self.ctx, self.span(), "expected bool"),
        }
    }
    pub fn ensure_string(&self) -> Result<String, UsageErr> {
        match self.value.as_string() {
            Some(s) => Ok(s.to_string()),
            None => bail_parse!(self.ctx, self.span(), "expected string"),
        }
    }
}