1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use core::fmt;
19

            
20
use crate::ast::core::*;
21

            
22
impl fmt::Display for Method {
23
2160
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24
2160
        write!(f, "{}", self.0)
25
    }
26
}
27

            
28
impl fmt::Display for Version {
29
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30
        write!(f, "{}", self.value)
31
    }
32
}
33

            
34
impl fmt::Display for VersionValue {
35
11890
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36
11890
        let s = match self {
37
25
            VersionValue::Version1 => "HTTP/1.0",
38
65
            VersionValue::Version11 => "HTTP/1.1",
39
50
            VersionValue::Version2 => "HTTP/2",
40
20
            VersionValue::Version3 => "HTTP/3",
41
11700
            VersionValue::VersionAny => "HTTP",
42
30
            VersionValue::VersionAnyLegacy => "HTTP/*",
43
        };
44
11890
        write!(f, "{s}")
45
    }
46
}
47

            
48
impl fmt::Display for Status {
49
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50
        write!(f, "{}", self.value)
51
    }
52
}
53

            
54
impl fmt::Display for StatusValue {
55
1300
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56
1300
        match self {
57
25
            StatusValue::Any => write!(f, "*"),
58
1275
            StatusValue::Specific(v) => write!(f, "{v}"),
59
        }
60
    }
61
}
62

            
63
impl fmt::Display for Template {
64
3600
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65
3600
        let mut buffer = String::new();
66
3645
        for element in self.elements.iter() {
67
3645
            buffer.push_str(element.to_string().as_str());
68
        }
69
3600
        write!(f, "{buffer}")
70
    }
71
}
72

            
73
impl fmt::Display for TemplateElement {
74
3645
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75
3645
        let s = match self {
76
3480
            TemplateElement::String { value, .. } => value.clone(),
77
165
            TemplateElement::Expression(value) => format!("{{{{{value}}}}}"),
78
        };
79
3645
        write!(f, "{s}")
80
    }
81
}
82

            
83
impl fmt::Display for Number {
84
1270
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85
1270
        match self {
86
400
            Number::Float(value) => write!(f, "{}", value),
87
865
            Number::Integer(value) => write!(f, "{}", value),
88
5
            Number::BigInteger(value) => write!(f, "{}", value),
89
        }
90
    }
91
}
92

            
93
impl fmt::Display for Float {
94
400
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95
400
        write!(f, "{}", self.encoded)
96
    }
97
}
98

            
99
impl fmt::Display for Expr {
100
725
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101
725
        write!(f, "{}", self.variable.name)
102
    }
103
}
104

            
105
impl fmt::Display for CookiePath {
106
10
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107
10
        let mut buf = self.name.to_string();
108
10
        if let Some(attribute) = &self.attribute {
109
5
            let s = format!("[{attribute}]");
110
5
            buf.push_str(s.as_str());
111
        }
112
10
        write!(f, "{buf}")
113
    }
114
}
115

            
116
impl fmt::Display for CookieAttribute {
117
5
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118
5
        let s = match self.name {
119
            CookieAttributeName::MaxAge(_) => "Max-Age",
120
            CookieAttributeName::Value(_) => "Value",
121
5
            CookieAttributeName::Expires(_) => "Expires",
122
            CookieAttributeName::Domain(_) => "Domain",
123
            CookieAttributeName::Path(_) => "Path",
124
            CookieAttributeName::Secure(_) => "Secure",
125
            CookieAttributeName::HttpOnly(_) => "HttpOnly",
126
            CookieAttributeName::SameSite(_) => "SameSite",
127
        };
128
5
        write!(f, "{s}")
129
    }
130
}
131

            
132
impl fmt::Display for Hex {
133
35
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134
35
        write!(
135
35
            f,
136
35
            "hex,{}{}{};",
137
35
            self.space0.value, self.encoded, self.space1.value
138
35
        )
139
    }
140
}
141

            
142
impl fmt::Display for Regex {
143
10
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144
10
        write!(f, "{}", self.inner)
145
    }
146
}
147

            
148
impl fmt::Display for MultilineString {
149
95
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150
95
        let body = match &self.kind {
151
35
            MultilineStringKind::Text(text)
152
20
            | MultilineStringKind::Json(text)
153
75
            | MultilineStringKind::Xml(text) => text.value.to_string(),
154
20
            MultilineStringKind::GraphQl(graphql) => {
155
20
                let var = match &graphql.variables {
156
20
                    None => String::new(),
157
                    Some(var) => {
158
                        format!(
159
                            "variables{}{}{}",
160
                            var.space.value, var.value, var.whitespace.value
161
                        )
162
                    }
163
                };
164
20
                format!("{}{}", graphql.value, var)
165
            }
166
        };
167
95
        write!(f, "{body}")
168
    }
169
}
170

            
171
impl fmt::Display for MultilineStringAttribute {
172
10
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173
10
        match self {
174
10
            MultilineStringAttribute::Escape => write!(f, "escape"),
175
            MultilineStringAttribute::NoVariable => write!(f, "novariable"),
176
        }
177
    }
178
}
179

            
180
impl fmt::Display for BooleanOption {
181
750
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182
750
        match self {
183
745
            BooleanOption::Literal(v) => write!(f, "{}", v),
184
5
            BooleanOption::Expression(v) => write!(f, "{}", v),
185
        }
186
    }
187
}
188

            
189
impl fmt::Display for NaturalOption {
190
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191
        match self {
192
            NaturalOption::Literal(v) => write!(f, "{}", v),
193
            NaturalOption::Expression(v) => write!(f, "{}", v),
194
        }
195
    }
196
}
197

            
198
impl fmt::Display for CountOption {
199
160
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200
160
        match self {
201
150
            CountOption::Literal(v) => write!(f, "{}", v),
202
10
            CountOption::Expression(v) => write!(f, "{}", v),
203
        }
204
    }
205
}
206

            
207
impl fmt::Display for DurationOption {
208
70
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209
70
        match self {
210
65
            DurationOption::Literal(v) => write!(f, "{}", v),
211
5
            DurationOption::Expression(v) => write!(f, "{}", v),
212
        }
213
    }
214
}
215

            
216
impl fmt::Display for VariableDefinition {
217
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218
        write!(f, "{}={}", self.name, self.value)
219
    }
220
}
221

            
222
impl fmt::Display for VariableValue {
223
380
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224
380
        let s = match self {
225
5
            VariableValue::Null => "null".to_string(),
226
10
            VariableValue::Bool(value) => value.to_string(),
227
135
            VariableValue::Number(n) => n.to_string(),
228
230
            VariableValue::String(s) => s.to_string(),
229
        };
230
380
        write!(f, "{}", s)
231
    }
232
}
233

            
234
impl PredicateFuncValue {
235
3250
    pub fn name(&self) -> String {
236
3250
        match self {
237
2035
            PredicateFuncValue::Equal { operator, .. } => {
238
2035
                if *operator {
239
2035
                    "==".to_string()
240
                } else {
241
                    "equals".to_string()
242
                }
243
            }
244
70
            PredicateFuncValue::NotEqual { operator, .. } => {
245
70
                if *operator {
246
70
                    "!=".to_string()
247
                } else {
248
                    "notEquals".to_string()
249
                }
250
            }
251
85
            PredicateFuncValue::GreaterThan { operator, .. } => {
252
85
                if *operator {
253
85
                    ">".to_string()
254
                } else {
255
                    "greaterThan".to_string()
256
                }
257
            }
258
20
            PredicateFuncValue::GreaterThanOrEqual { operator, .. } => {
259
20
                if *operator {
260
20
                    ">=".to_string()
261
                } else {
262
                    "greaterThanOrEquals".to_string()
263
                }
264
            }
265
70
            PredicateFuncValue::LessThan { operator, .. } => {
266
70
                if *operator {
267
70
                    "<".to_string()
268
                } else {
269
                    "lessThan".to_string()
270
                }
271
            }
272
30
            PredicateFuncValue::LessThanOrEqual { operator, .. } => {
273
30
                if *operator {
274
30
                    "<=".to_string()
275
                } else {
276
                    "lessThanOrEquals".to_string()
277
                }
278
            }
279
135
            PredicateFuncValue::StartWith { .. } => "startsWith".to_string(),
280
45
            PredicateFuncValue::EndWith { .. } => "endsWith".to_string(),
281
75
            PredicateFuncValue::Contain { .. } => "contains".to_string(),
282
85
            PredicateFuncValue::Include { .. } => "includes".to_string(),
283
95
            PredicateFuncValue::Match { .. } => "matches".to_string(),
284
20
            PredicateFuncValue::IsInteger => "isInteger".to_string(),
285
25
            PredicateFuncValue::IsFloat => "isFloat".to_string(),
286
20
            PredicateFuncValue::IsBoolean => "isBoolean".to_string(),
287
20
            PredicateFuncValue::IsString => "isString".to_string(),
288
45
            PredicateFuncValue::IsCollection => "isCollection".to_string(),
289
35
            PredicateFuncValue::IsDate => "isDate".to_string(),
290
45
            PredicateFuncValue::IsIsoDate => "isIsoDate".to_string(),
291
215
            PredicateFuncValue::Exist => "exists".to_string(),
292
55
            PredicateFuncValue::IsEmpty => "isEmpty".to_string(),
293
25
            PredicateFuncValue::IsNumber => "isNumber".to_string(),
294
        }
295
    }
296
}
297

            
298
#[cfg(test)]
299
mod tests {
300
    use super::*;
301
    use crate::reader::Pos;
302

            
303
    fn whitespace() -> Whitespace {
304
        Whitespace {
305
            value: String::new(),
306
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
307
        }
308
    }
309

            
310
    fn variable_expr() -> Expr {
311
        Expr {
312
            space0: whitespace(),
313
            variable: Variable {
314
                name: "name".to_string(),
315
                source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
316
            },
317
            space1: whitespace(),
318
        }
319
    }
320

            
321
    fn hello_template() -> Template {
322
        Template {
323
            delimiter: None,
324
            elements: vec![
325
                TemplateElement::String {
326
                    value: "Hello ".to_string(),
327
                    encoded: "Hello ".to_string(),
328
                },
329
                TemplateElement::Expression(variable_expr()),
330
                TemplateElement::String {
331
                    value: "!".to_string(),
332
                    encoded: "!".to_string(),
333
                },
334
            ],
335
            source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
336
        }
337
    }
338

            
339
    #[test]
340
    fn test_float() {
341
        assert_eq!(
342
            Float {
343
                value: 1.0,
344
                encoded: "1.0".to_string()
345
            }
346
            .to_string(),
347
            "1.0"
348
        );
349
        assert_eq!(
350
            Float {
351
                value: 1.01,
352
                encoded: "1.01".to_string()
353
            }
354
            .to_string(),
355
            "1.01"
356
        );
357
        assert_eq!(
358
            Float {
359
                value: 1.01,
360
                encoded: "1.010".to_string()
361
            }
362
            .to_string(),
363
            "1.010"
364
        );
365
        assert_eq!(
366
            Float {
367
                value: -1.333,
368
                encoded: "-1.333".to_string()
369
            }
370
            .to_string(),
371
            "-1.333"
372
        );
373
    }
374

            
375
    #[test]
376
    fn test_template() {
377
        assert_eq!(hello_template().to_string(), "Hello {{name}}!");
378
    }
379

            
380
    #[test]
381
    fn test_cookie_path() {
382
        assert_eq!(
383
            CookiePath {
384
                name: Template {
385
                    delimiter: None,
386
                    elements: vec![TemplateElement::String {
387
                        value: "LSID".to_string(),
388
                        encoded: "unused".to_string(),
389
                    }],
390
                    source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
391
                },
392
                attribute: Some(CookieAttribute {
393
                    space0: whitespace(),
394
                    name: CookieAttributeName::MaxAge("Max-Age".to_string()),
395
                    space1: whitespace(),
396
                }),
397
            }
398
            .to_string(),
399
            "LSID[Max-Age]".to_string()
400
        );
401
    }
402
}