sql_composer/parser/
bind.rs1use winnow::combinator::{opt, preceded, trace};
4use winnow::error::ParserError;
5use winnow::stream::{AsBStr, AsChar, Compare, Stream, StreamIsPartial};
6use winnow::token::{literal, take_while};
7use winnow::Parser;
8
9use crate::types::Binding;
10
11pub fn bind_name<'i, Input, Error>(input: &mut Input) -> Result<String, Error>
13where
14 Input: StreamIsPartial + Stream + Compare<&'i str>,
15 <Input as Stream>::Slice: AsBStr,
16 <Input as Stream>::Token: AsChar + Clone,
17 Error: ParserError<Input>,
18{
19 trace("bind_name", move |input: &mut Input| {
20 let name = take_while(1.., |c: <Input as Stream>::Token| {
21 let ch = c.as_char();
22 ch.is_alphanumeric() || ch == '_'
23 })
24 .parse_next(input)?;
25 let name = String::from_utf8_lossy(name.as_bstr()).to_string();
26 Ok(name)
27 })
28 .parse_next(input)
29}
30
31fn ws<'i, Input, Error>(input: &mut Input) -> Result<(), Error>
33where
34 Input: StreamIsPartial + Stream + Compare<&'i str>,
35 <Input as Stream>::Slice: AsBStr,
36 <Input as Stream>::Token: AsChar + Clone,
37 Error: ParserError<Input>,
38{
39 take_while(0.., |c: <Input as Stream>::Token| {
40 let ch = c.as_char();
41 ch == ' ' || ch == '\t'
42 })
43 .void()
44 .parse_next(input)
45}
46
47fn parse_u32<'i, Input, Error>(input: &mut Input) -> Result<u32, Error>
49where
50 Input: StreamIsPartial + Stream + Compare<&'i str>,
51 <Input as Stream>::Slice: AsBStr,
52 <Input as Stream>::Token: AsChar + Clone,
53 Error: ParserError<Input>,
54{
55 let digits = take_while(1.., |c: <Input as Stream>::Token| {
56 c.as_char().is_ascii_digit()
57 })
58 .parse_next(input)?;
59 let s = String::from_utf8_lossy(digits.as_bstr());
60 let n = s
61 .parse::<u32>()
62 .map_err(|_| ParserError::from_input(input))?;
63 Ok(n)
64}
65
66fn expecting<'i, Input, Error>(input: &mut Input) -> Result<(u32, Option<u32>), Error>
68where
69 Input: StreamIsPartial + Stream + Compare<&'i str>,
70 <Input as Stream>::Slice: AsBStr,
71 <Input as Stream>::Token: AsChar + Clone,
72 Error: ParserError<Input>,
73{
74 trace("expecting", move |input: &mut Input| {
75 literal("EXPECTING").parse_next(input)?;
76 ws(input)?;
77 let min = parse_u32(input)?;
78 let max = opt(preceded(literal(".."), parse_u32)).parse_next(input)?;
79 Ok((min, max))
80 })
81 .parse_next(input)
82}
83
84pub fn bind<'i, Input, Error>(input: &mut Input) -> Result<Binding, Error>
89where
90 Input: StreamIsPartial + Stream + Compare<&'i str>,
91 <Input as Stream>::Slice: AsBStr,
92 <Input as Stream>::Token: AsChar + Clone,
93 Error: ParserError<Input>,
94{
95 trace("bind", move |input: &mut Input| {
96 let name = bind_name(input)?;
97 ws(input)?;
98
99 let expecting_result = opt(expecting).parse_next(input)?;
100 ws(input)?;
101
102 let null_kw = opt(literal("NULL")).parse_next(input)?;
103 ws(input)?;
104
105 literal(")").parse_next(input)?;
106
107 let (min_values, max_values) = match expecting_result {
108 Some((min, max)) => (Some(min), max),
109 None => (None, None),
110 };
111
112 Ok(Binding {
113 name,
114 min_values,
115 max_values,
116 nullable: null_kw.is_some(),
117 })
118 })
119 .parse_next(input)
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use winnow::error::ContextError;
126
127 type TestInput<'a> = &'a str;
128
129 #[test]
130 fn test_bind_simple() {
131 let mut input: TestInput = "user_id)";
132 let result = bind::<_, ContextError>.parse_next(&mut input).unwrap();
133 assert_eq!(result.name, "user_id");
134 assert_eq!(result.min_values, None);
135 assert_eq!(result.max_values, None);
136 assert!(!result.nullable);
137 assert_eq!(input, "");
138 }
139
140 #[test]
141 fn test_bind_with_expecting() {
142 let mut input: TestInput = "values EXPECTING 1..10)";
143 let result = bind::<_, ContextError>.parse_next(&mut input).unwrap();
144 assert_eq!(result.name, "values");
145 assert_eq!(result.min_values, Some(1));
146 assert_eq!(result.max_values, Some(10));
147 assert!(!result.nullable);
148 }
149
150 #[test]
151 fn test_bind_with_expecting_min_only() {
152 let mut input: TestInput = "values EXPECTING 3)";
153 let result = bind::<_, ContextError>.parse_next(&mut input).unwrap();
154 assert_eq!(result.name, "values");
155 assert_eq!(result.min_values, Some(3));
156 assert_eq!(result.max_values, None);
157 assert!(!result.nullable);
158 }
159
160 #[test]
161 fn test_bind_nullable() {
162 let mut input: TestInput = "email NULL)";
163 let result = bind::<_, ContextError>.parse_next(&mut input).unwrap();
164 assert_eq!(result.name, "email");
165 assert!(result.nullable);
166 }
167
168 #[test]
169 fn test_bind_full() {
170 let mut input: TestInput = "tags EXPECTING 1..5 NULL)";
171 let result = bind::<_, ContextError>.parse_next(&mut input).unwrap();
172 assert_eq!(result.name, "tags");
173 assert_eq!(result.min_values, Some(1));
174 assert_eq!(result.max_values, Some(5));
175 assert!(result.nullable);
176 }
177
178 #[test]
179 fn test_bind_name_only() {
180 let mut input: TestInput = "active";
181 let result = bind_name::<_, ContextError>.parse_next(&mut input).unwrap();
182 assert_eq!(result, "active");
183 }
184}