1use super::any::parse_any;
2use super::builtin::parse_builtin;
3use super::r#ref::parse_ref;
4use super::r#struct::parse_struct;
5use super::relation::parse_relation;
6use super::seq::parse_seq;
7use super::set::parse_set;
8use super::tuple::parse_tuple;
9use crate::fio::r#type::parse_subtypeable;
10
11use super::{Type};
12use crate::common::FilePosition;
13use crate::fio::common::Span;
14use nom::branch::alt;
15use nom::character::complete::alphanumeric1;
16use nom::combinator::{peek};
17use nom::sequence::{preceded, separated_pair, terminated};
18use nom::{bytes::complete::tag, combinator::map, IResult};
19use serde::{Serialize, Deserialize};
20use super::common::{ws, take_parenth_content};
21
22
23#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
24pub struct Constraint {
25 pub param: String,
26 pub expr: String,
27 pub position: FilePosition,
28}
29
30#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
31pub struct SubType {
32 pub base: Box<Type>,
33 pub constraints: Vec<Constraint>,
34 pub position: FilePosition,
35}
36
37pub fn parse_anonymous_constraint(input: Span) -> IResult<Span, Constraint> {
38 let constraint = take_parenth_content('(', ')');
39
40 let (rest, parsed) = match constraint(input) {
41 Ok(c) => c,
42 Err(err) => {
43 return Err(err)
44 }
45 };
46
47 let mut param = preceded(ws, terminated(alphanumeric1, preceded(ws, tag("|"))));
48 match param(parsed) {
49 Ok((expr, param)) => {
50 let c = Constraint {
51 param: param.to_string(),
52 expr: expr.to_string(),
53 position: parsed.into(),
54 };
55 Ok((rest, c))
56 }
57 Err(err) => return Err(err)
58 }
59}
60
61pub fn check_looks_like_sub(input: Span) -> IResult<Span, bool> {
62 let types = alt((
64 map(parse_builtin, |_| {}),
65 map(parse_ref, |_| {}),
66 map(parse_seq, |_| {}),
67 map(parse_set, |_| {}),
68 map(parse_struct, |_| {}),
69 map(parse_tuple, |_| {}),
70 map(parse_relation, |_| {}),
71 map(parse_any, |_| {}),
72 ));
73 let check = separated_pair(types, ws, tag("("));
74 match peek(check)(input) {
75 Ok(_s) => Ok((input, true)),
76 Err(err) => Err(err),
77 }
78}
79
80pub fn parse_sub(input: Span) -> IResult<Span, SubType> {
81 match peek(check_looks_like_sub)(input) {
83 Err(err) => Err(err),
84 Ok(_) => {
85 map(
86 separated_pair(parse_subtypeable, ws, parse_anonymous_constraint),
87 |(ftype, constraint)| SubType {
88 base: Box::new(ftype),
89 constraints: vec![constraint],
90 position: input.into(),
91 },
92 )(input)
93 }
94 }
95}
96
97#[cfg(test)]
98use super::{BuiltinType, SeqType};
99#[cfg(test)]
100use crate::fio::common::assert_parse;
101
102#[test]
103fn test_parse_anonymous_constraint() {
104 assert_parse(
105 parse_anonymous_constraint(Span::new("(s | some anonymous constraint)")),
106 Constraint {
107 param: "s".to_string(),
108 expr: " some anonymous constraint".to_string(),
109 position: FilePosition { line: 1, column: 2 },
110 },
111 );
112}
113
114#[test]
115fn test_check_looks_like_sub() {
116 let output = check_looks_like_sub(Span::new("Number(s | foo bar baz)"));
117 let output = output.unwrap();
118 assert_eq!(output.0.fragment(), &"Number(s | foo bar baz)");
119 assert_eq!(output.1, true);
120}
121
122#[test]
123fn test_parse_sub_type_builtin() {
124 assert_parse(
125 parse_sub(Span::new(".Number(s | some anonymous constraint)")),
126 SubType {
127 position: FilePosition { line: 1, column: 1 },
128 base: Box::new(Type::BuiltinType(BuiltinType {
129 name: "Number".to_string(),
130 position: FilePosition { line: 1, column: 1 },
131 })),
132 constraints: vec![Constraint {
133 param: "s".to_string(),
134 expr: " some anonymous constraint".to_string(),
135 position: FilePosition { line: 1, column: 9 },
136 }],
137 },
138 );
139}
140
141#[test]
142fn test_parse_sub_type_seq() {
143 assert_parse(
144 parse_sub(Span::new("[.Number](s | some anonymous constraint)")),
145 SubType {
146 position: FilePosition { line: 1, column: 1 },
147 base: Box::new(Type::SeqType(SeqType {
148 elm_type: Box::new(Type::BuiltinType(BuiltinType {
149 name: "Number".to_string(),
150 position: FilePosition { line: 1, column: 2 },
151 })),
152 position: FilePosition { line: 1, column: 1 },
153 })),
154 constraints: vec![Constraint {
155 param: "s".to_string(),
156 expr: " some anonymous constraint".to_string(),
157 position: FilePosition {
158 line: 1,
159 column: 11,
160 },
161 }],
162 },
163 );
164}
165
166#[test]
167fn test_parse_sub_type_spacing() {
168 assert_parse(
169 parse_sub(Span::new(
170 "[ .Number ] ( s | \nsome anonymous constraint)",
171 )),
172 SubType {
173 position: FilePosition { line: 1, column: 1 },
174 base: Box::new(Type::SeqType(SeqType {
175 elm_type: Box::new(Type::BuiltinType(BuiltinType {
176 name: "Number".to_string(),
177 position: FilePosition { line: 1, column: 3 },
178 })),
179 position: FilePosition { line: 1, column: 1 },
180 })),
181 constraints: vec![Constraint {
182 param: "s".to_string(),
183 expr: " \nsome anonymous constraint".to_string(),
184 position: FilePosition {
185 line: 1,
186 column: 14,
187 },
188 }],
189 },
190 );
191}
192
193
194#[test]
195fn test_parse_sub_type_functions_in_expressions() {
196 assert_parse(
197 parse_sub(Span::new(
198 ".String(s | len(s) > 8)",
199 )),
200 SubType {
201 position: FilePosition { line: 1, column: 1 },
202 base: Box::new(Type::BuiltinType(BuiltinType {
203 name: "String".to_string(),
204 position: FilePosition { line: 1, column: 1 },
205 })),
206 constraints: vec![Constraint {
207 param: "s".to_string(),
208 expr: " len(s) > 8".to_string(),
209 position: FilePosition { line: 1, column: 9 },
210 }],
211 },
212 );
213}