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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! The query module contains the data structures that are used by `typst query
//! <mitex-packages>`

use std::{collections::HashMap, sync::Arc};

use serde::{Deserialize, Serialize};

use crate::{CmdShape, EnvShape};

/// A package specification.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackageSpec {
    /// The name of the package.
    pub name: String,
    /// The command specification of the package.
    pub spec: CommandSpecRepr,
}

/// A ordered list of package specifications.
///
/// The latter package specification will override the former one.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackagesVec(pub Vec<PackageSpec>);

/// An item of command specification.
/// This enum contains more sugar than the canonical representation.
///
/// See [`crate::CommandSpecItem`] for more details.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum CommandSpecItem {
    /// A canonical command item.
    #[serde(rename = "cmd")]
    Cmd(CmdShape),
    /// A canonical environment item.
    #[serde(rename = "env")]
    Env(EnvShape),
    /// A command that takes no argument, and its handler is also a typst
    /// symbol.
    #[serde(rename = "sym")]
    Symbol,
    /// A command that takes zero argument, and its handler is a typst function.
    #[serde(rename = "cmd0")]
    Command0,
    /// A command that takes one argument.
    #[serde(rename = "cmd1")]
    Command1,
    /// A command that takes two arguments.
    #[serde(rename = "cmd2")]
    Command2,
    /// A command that takes one argument and is a left-associative operator.
    #[serde(rename = "left1-cmd")]
    CmdLeft1,
    /// A command that takes no argument and is a matrix environment.
    #[serde(rename = "matrix-env")]
    EnvMatrix,
    /// A command that takes no argument and is a normal environment.
    #[serde(rename = "normal-env")]
    EnvNormal,

    /// A command that is aliased to a Typst symbol.
    #[serde(rename = "alias-sym")]
    SymAlias {
        /// The aliasing typst handle of the symbol.
        alias: String,
    },
    /// A command that is greedy and is aliased to a Typst handler.
    #[serde(rename = "greedy-cmd")]
    CmdGreedy {
        /// The aliasing typst handle of the command.
        alias: String,
    },
    #[serde(rename = "infix-cmd")]
    /// A command that is an infix operator and is aliased to a Typst handler.
    CmdInfix {
        /// The aliasing typst handle of the command.
        alias: String,
    },
    #[serde(rename = "glob-cmd")]
    /// A command that has a glob argument pattern and is aliased to a Typst
    /// handler.
    CmdGlob {
        /// The glob pattern of the command.
        pattern: String,
        /// The aliasing typst handle of the command.
        alias: String,
    },
}

impl From<CommandSpecItem> for crate::CommandSpecItem {
    fn from(item: CommandSpecItem) -> Self {
        use crate::preludes::command::*;
        match item {
            CommandSpecItem::Cmd(shape) => Self::Cmd(shape),
            CommandSpecItem::Env(shape) => Self::Env(shape),
            CommandSpecItem::Symbol => TEX_SYMBOL,
            CommandSpecItem::Command0 => TEX_CMD0,
            CommandSpecItem::Command1 => TEX_CMD1,
            CommandSpecItem::Command2 => TEX_CMD2,
            CommandSpecItem::CmdLeft1 => TEX_LEFT1_OPEARTOR,
            CommandSpecItem::EnvMatrix => TEX_MATRIX_ENV,
            CommandSpecItem::EnvNormal => TEX_NORMAL_ENV,
            CommandSpecItem::SymAlias { alias } => define_symbol(&alias),
            CommandSpecItem::CmdGreedy { alias } => define_greedy_command(&alias),
            CommandSpecItem::CmdInfix { alias } => crate::CommandSpecItem::Cmd(crate::CmdShape {
                args: crate::ArgShape::InfixGreedy,
                alias: Some(alias.to_owned()),
            }),
            CommandSpecItem::CmdGlob { pattern, alias } => define_glob_command(&pattern, &alias),
        }
    }
}

/// Command specification that contains a set of commands and environments. It
/// is used for us to define the meta data of LaTeX packages in typst code and
/// query by `typst query` then. See [`Spec`] for an example.
///
/// Note: There are non-canonical format of items could be used for convenience.
///
/// [`Spec`]: https://github.com/mitex-rs/mitex/tree/main/packages/mitex/specs
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct CommandSpecRepr {
    /// The command specifications.
    pub commands: HashMap<String, CommandSpecItem>,
}

impl From<CommandSpecRepr> for crate::CommandSpec {
    fn from(repr: CommandSpecRepr) -> Self {
        Self(Arc::new(repr.into()))
    }
}

impl From<CommandSpecRepr> for crate::CommandSpecRepr {
    fn from(repr: CommandSpecRepr) -> Self {
        Self {
            commands: repr
                .commands
                .into_iter()
                .map(|(k, v)| (k, v.into()))
                .collect(),
        }
    }
}