Skip to main content

texform/
argspec.rs

1use texform_argspec::{ArgForm, ArgSpec, DelimiterToken, ValueKind};
2use texform_interface::syntax_node::ContentMode;
3
4#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
5pub struct ValidateArgspecResult {
6    pub valid: bool,
7    pub error: Option<String>,
8    pub arg_count: Option<usize>,
9    pub parsed: Option<Vec<ParsedArgSpecSlot>>,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
13pub struct ParsedArgSpecSlot {
14    pub required: bool,
15    pub no_leading_space: bool,
16    pub nullable: bool,
17    pub kind: ArgSpecKindInfo,
18    pub form: ArgSpecFormInfo,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
22#[serde(tag = "type", rename_all = "lowercase")]
23pub enum ArgSpecKindInfo {
24    Content {
25        mode: RuntimeContentModeInfo,
26    },
27    Delimiter,
28    #[serde(rename = "csname")]
29    CsName,
30    Dimension,
31    Integer,
32    #[serde(rename = "keyval")]
33    KeyVal,
34    Column,
35    Star,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
39#[serde(rename_all = "lowercase")]
40pub enum RuntimeContentModeInfo {
41    Math,
42    Text,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
46#[serde(tag = "type", rename_all = "lowercase")]
47pub enum ArgSpecFormInfo {
48    Standard,
49    Star,
50    Group,
51    Delimited {
52        open: DelimiterTokenInfo,
53        close: DelimiterTokenInfo,
54    },
55    Paired {
56        pairs: Vec<DelimiterTokenPairInfo>,
57    },
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
61#[serde(tag = "type", rename_all = "kebab-case")]
62pub enum DelimiterTokenInfo {
63    Char { value: char },
64    ControlSeq { value: String },
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
68pub struct DelimiterTokenPairInfo {
69    pub open: DelimiterTokenInfo,
70    pub close: DelimiterTokenInfo,
71}
72
73pub fn validate_argspec(spec: &str) -> ValidateArgspecResult {
74    match texform_argspec::parse_arg_specs(spec, "validate_argspec") {
75        Ok(parsed) => ValidateArgspecResult {
76            valid: true,
77            error: None,
78            arg_count: Some(parsed.len()),
79            parsed: Some(parsed.iter().map(parsed_arg_spec_slot).collect()),
80        },
81        Err(error) => ValidateArgspecResult {
82            valid: false,
83            error: Some(error.to_string()),
84            arg_count: None,
85            parsed: None,
86        },
87    }
88}
89
90pub fn parsed_arg_spec_slot(spec: &ArgSpec) -> ParsedArgSpecSlot {
91    ParsedArgSpecSlot {
92        required: spec.required,
93        no_leading_space: spec.no_leading_space,
94        nullable: spec.nullable,
95        kind: arg_spec_kind_info(spec.kind),
96        form: arg_spec_form_info(&spec.form),
97    }
98}
99
100fn arg_spec_kind_info(kind: ValueKind) -> ArgSpecKindInfo {
101    match kind {
102        ValueKind::Content { mode } => ArgSpecKindInfo::Content {
103            mode: runtime_content_mode_info(mode),
104        },
105        ValueKind::Delimiter => ArgSpecKindInfo::Delimiter,
106        ValueKind::CSName => ArgSpecKindInfo::CsName,
107        ValueKind::Dimension => ArgSpecKindInfo::Dimension,
108        ValueKind::Integer => ArgSpecKindInfo::Integer,
109        ValueKind::KeyVal => ArgSpecKindInfo::KeyVal,
110        ValueKind::Column => ArgSpecKindInfo::Column,
111        ValueKind::Star => ArgSpecKindInfo::Star,
112    }
113}
114
115fn runtime_content_mode_info(mode: ContentMode) -> RuntimeContentModeInfo {
116    match mode {
117        ContentMode::Math => RuntimeContentModeInfo::Math,
118        ContentMode::Text => RuntimeContentModeInfo::Text,
119    }
120}
121
122fn arg_spec_form_info(form: &ArgForm) -> ArgSpecFormInfo {
123    match form {
124        ArgForm::Standard => ArgSpecFormInfo::Standard,
125        ArgForm::Star => ArgSpecFormInfo::Star,
126        ArgForm::Group => ArgSpecFormInfo::Group,
127        ArgForm::Delimited { open, close } => ArgSpecFormInfo::Delimited {
128            open: delimiter_token_info(open),
129            close: delimiter_token_info(close),
130        },
131        ArgForm::Paired { pairs } => ArgSpecFormInfo::Paired {
132            pairs: pairs
133                .iter()
134                .map(|(open, close)| DelimiterTokenPairInfo {
135                    open: delimiter_token_info(open),
136                    close: delimiter_token_info(close),
137                })
138                .collect(),
139        },
140    }
141}
142
143fn delimiter_token_info(token: &DelimiterToken) -> DelimiterTokenInfo {
144    match token {
145        DelimiterToken::Char(value) => DelimiterTokenInfo::Char { value: *value },
146        DelimiterToken::ControlSeq(value) => DelimiterTokenInfo::ControlSeq {
147            value: value.to_string(),
148        },
149    }
150}