jaq_syn/
def.rs

1use crate::filter::Filter;
2use crate::Spanned;
3use alloc::{string::String, vec::Vec};
4use core::ops::Deref;
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8/// Call to a filter identified by a name type `N` with arguments of type `A`.
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct Call<A = Arg, N = String> {
12    /// Name of the filter, e.g. `map`
13    pub name: N,
14    /// Arguments of the filter, e.g. `["f"]`
15    pub args: Vec<A>,
16}
17
18impl<A, N> Call<A, N> {
19    /// Apply a function to the call arguments.
20    pub fn map_args<B>(self, f: impl FnMut(A) -> B) -> Call<B, N> {
21        Call {
22            name: self.name,
23            args: self.args.into_iter().map(f).collect(),
24        }
25    }
26}
27
28/// A definition, such as `def map(f): [.[] | f];`.
29#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30#[derive(Clone, Debug)]
31pub struct Def<Rhs = Main> {
32    /// left-hand side, i.e. what shall be defined, e.g. `map(f)`
33    pub lhs: Call,
34    /// right-hand side, i.e. what the LHS should be defined as, e.g. `[.[] | f]`
35    pub rhs: Rhs,
36}
37
38/// Argument of a definition, such as `$v` or `f` in `def foo($v; f): ...`.
39///
40/// In jq, we can bind filters in three different ways:
41///
42/// 1. `f as $x | ...`
43/// 2. `def g($x): ...; g(f)`
44/// 3. `def g(fx): ...; g(f)`
45///
46/// In the first two cases, we bind the outputs of `f` to a variable `$x`.
47/// In the third case, we bind `f` to a filter `fx`
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum Arg<V = String, F = V> {
51    /// binding to a variable
52    Var(V),
53    /// binding to a filter
54    Fun(F),
55}
56
57impl<T> Arg<T, T> {
58    /// Apply a function to both binding types.
59    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Arg<U, U> {
60        match self {
61            Self::Var(x) => Arg::Var(f(x)),
62            Self::Fun(x) => Arg::Fun(f(x)),
63        }
64    }
65}
66
67impl<V, F> Arg<V, F> {
68    /// Move references inward.
69    pub fn as_ref(&self) -> Arg<&V, &F> {
70        match self {
71            Self::Var(x) => Arg::Var(x),
72            Self::Fun(x) => Arg::Fun(x),
73        }
74    }
75}
76
77impl<V: Deref, F: Deref> Arg<V, F> {
78    /// Move references inward, while dereferencing content.
79    pub fn as_deref(&self) -> Arg<&<V as Deref>::Target, &<F as Deref>::Target> {
80        match self {
81            Self::Var(x) => Arg::Var(x),
82            Self::Fun(x) => Arg::Fun(x),
83        }
84    }
85}
86
87// TODO for v2.0: remove this
88impl<V, F> Arg<V, F> {
89    /// Create a variable argument with given name (without leading "$").
90    pub fn new_var(name: V) -> Self {
91        Self::Var(name)
92    }
93
94    /// Create a filter argument with given name.
95    pub fn new_filter(name: F) -> Self {
96        Self::Fun(name)
97    }
98
99    /// True if the argument is a variable.
100    pub fn is_var(&self) -> bool {
101        matches!(self, Self::Var(_))
102    }
103}
104
105// TODO for v2.0: remove this
106impl<V: Deref, F: Deref> Arg<V, F> {
107    /// If the argument is a variable, return its name without leading "$", otherwise `None`.
108    pub fn get_var(&self) -> Option<&<V as Deref>::Target> {
109        match self {
110            Self::Var(v) => Some(v),
111            Self::Fun(_) => None,
112        }
113    }
114
115    /// If the argument is a filter, return its name, otherwise `None`.
116    pub fn get_filter(&self) -> Option<&<F as Deref>::Target> {
117        match self {
118            Self::Var(_) => None,
119            Self::Fun(f) => Some(f),
120        }
121    }
122}
123
124/// (Potentially empty) sequence of definitions, followed by a filter.
125#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
126#[derive(Clone, Debug)]
127pub struct Main<F = Filter> {
128    /// Definitions at the top of the filter
129    pub defs: Vec<Def<Self>>,
130    /// Body of the filter, e.g. `[.[] | f]`.
131    pub body: Spanned<F>,
132}