hurl_core-6.1.1/.cargo_vcs_info.json0000644000000001600000000000100130200ustar { "git": { "sha1": "ae921ffbd836db667995c17262d11b58c2ee7e87" }, "path_in_vcs": "packages/hurl_core" }hurl_core-6.1.1/Cargo.lock0000644000000111100000000000100107700ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "colored" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ "windows-sys", ] [[package]] name = "hurl_core" version = "6.1.1" dependencies = [ "colored", "libxml", "regex", ] [[package]] name = "libc" version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libxml" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fe73cdec2bcb36d25a9fe3f607ffcd44bb8907ca0100c4098d1aa342d1e7bec" dependencies = [ "libc", "pkg-config", "vcpkg", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" hurl_core-6.1.1/Cargo.toml0000644000000025600000000000100110240ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "hurl_core" version = "6.1.1" authors = [ "Fabrice Reix ", "Jean-Christophe Amiel ", "Filipe Pinto ", ] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Hurl Core" homepage = "https://hurl.dev" documentation = "https://hurl.dev" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/Orange-OpenSource/hurl" [lib] name = "hurl_core" path = "src/lib.rs" [[test]] name = "json" path = "tests/json.rs" [dependencies.colored] version = "3.0.0" [dependencies.libxml] version = "0.3.3" [dependencies.regex] version = "1.11.1" [lints.clippy] empty_structs_with_brackets = "deny" manual_string_new = "deny" semicolon_if_nothing_returned = "deny" wildcard-imports = "deny" [lints.rust] warnings = "deny" hurl_core-6.1.1/Cargo.toml.orig000064400000000000000000000007431046102023000145060ustar 00000000000000[package] name = "hurl_core" version = "6.1.1" authors = ["Fabrice Reix ", "Jean-Christophe Amiel ", "Filipe Pinto "] edition = "2021" license = "Apache-2.0" description = "Hurl Core" documentation = "https://hurl.dev" homepage = "https://hurl.dev" repository = "https://github.com/Orange-OpenSource/hurl" [dependencies] colored = "3.0.0" libxml = "0.3.3" regex = "1.11.1" [lints] workspace = true hurl_core-6.1.1/README.md000064400000000000000000000001631046102023000130720ustar 00000000000000hurl_core =============== The hurl_core crate provides the common functionalities used by both hurl and hurlfmt. hurl_core-6.1.1/src/ast/core.rs000064400000000000000000000233341046102023000144740ustar 00000000000000/* * Hurl (https://hurl.dev) * Copyright (C) 2025 Orange * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ use std::fmt; use crate::ast::option::EntryOption; use crate::ast::primitive::{ Bytes, KeyValue, LineTerminator, SourceInfo, Template, Whitespace, U64, }; use crate::ast::section::{ Assert, Capture, Cookie, MultipartParam, RegexValue, Section, SectionValue, }; /// Represents Hurl AST root node. #[derive(Clone, Debug, PartialEq, Eq)] pub struct HurlFile { pub entries: Vec, pub line_terminators: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Entry { pub request: Request, pub response: Option, } impl Entry { /// Returns the source information for this entry. pub fn source_info(&self) -> SourceInfo { self.request.space0.source_info } /// Returns true if the request or the response uses multilines string attributes pub fn use_multiline_string_body_with_attributes(&self) -> bool { if let Some(Body { value: Bytes::MultilineString(multiline), .. }) = &self.request.body { if multiline.has_attributes() { return true; } } if let Some(response) = &self.response { if let Some(Body { value: Bytes::MultilineString(multiline), .. }) = &response.body { if multiline.has_attributes() { return true; } } } false } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Request { pub line_terminators: Vec, pub space0: Whitespace, pub method: Method, pub space1: Whitespace, pub url: Template, pub line_terminator0: LineTerminator, pub headers: Vec, pub sections: Vec
, pub body: Option, pub source_info: SourceInfo, } impl Request { /// Returns the query strings params for this request. /// /// See . pub fn querystring_params(&self) -> &[KeyValue] { for section in &self.sections { if let SectionValue::QueryParams(params, _) = §ion.value { return params; } } &[] } /// Returns the form params for this request. /// /// See . pub fn form_params(&self) -> &[KeyValue] { for section in &self.sections { if let SectionValue::FormParams(params, _) = §ion.value { return params; } } &[] } /// Returns the multipart form data for this request. /// /// See . pub fn multipart_form_data(&self) -> &[MultipartParam] { for section in &self.sections { if let SectionValue::MultipartFormData(params, _) = §ion.value { return params; } } &[] } /// Returns the list of cookies on this request. /// /// See . pub fn cookies(&self) -> &[Cookie] { for section in &self.sections { if let SectionValue::Cookies(cookies) = §ion.value { return cookies; } } &[] } /// Returns the basic authentication on this request. /// /// See . pub fn basic_auth(&self) -> Option<&KeyValue> { for section in &self.sections { if let SectionValue::BasicAuth(kv) = §ion.value { return kv.as_ref(); } } None } /// Returns the options specific for this request. pub fn options(&self) -> &[EntryOption] { for section in &self.sections { if let SectionValue::Options(options) = §ion.value { return options; } } &[] } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Response { pub line_terminators: Vec, pub version: Version, pub space0: Whitespace, pub status: Status, pub space1: Whitespace, pub line_terminator0: LineTerminator, pub headers: Vec, pub sections: Vec
, pub body: Option, pub source_info: SourceInfo, } impl Response { /// Returns the captures list of this spec response. pub fn captures(&self) -> &[Capture] { for section in self.sections.iter() { if let SectionValue::Captures(captures) = §ion.value { return captures; } } &[] } /// Returns the asserts list of this spec response. pub fn asserts(&self) -> &[Assert] { for section in self.sections.iter() { if let SectionValue::Asserts(asserts) = §ion.value { return asserts; } } &[] } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Method(String); impl Method { /// Creates a new AST element method/ pub fn new(method: &str) -> Method { Method(method.to_string()) } } impl fmt::Display for Method { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Version { pub value: VersionValue, pub source_info: SourceInfo, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum VersionValue { Version1, Version11, Version2, Version3, VersionAny, } impl fmt::Display for VersionValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { VersionValue::Version1 => "HTTP/1.0", VersionValue::Version11 => "HTTP/1.1", VersionValue::Version2 => "HTTP/2", VersionValue::Version3 => "HTTP/3", VersionValue::VersionAny => "HTTP", }; write!(f, "{s}") } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Status { pub value: StatusValue, pub source_info: SourceInfo, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum StatusValue { Any, Specific(u64), } impl fmt::Display for StatusValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { StatusValue::Any => write!(f, "*"), StatusValue::Specific(v) => write!(f, "{v}"), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Body { pub line_terminators: Vec, pub space0: Whitespace, pub value: Bytes, pub line_terminator0: LineTerminator, } /// Check that variable name is not reserved /// (would conflicts with an existing function) pub fn is_variable_reserved(name: &str) -> bool { ["getEnv", "newDate", "newUuid"].contains(&name) } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Filter { pub source_info: SourceInfo, pub value: FilterValue, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum FilterValue { Base64Decode, Base64Encode, Count, DaysAfterNow, DaysBeforeNow, Decode { space0: Whitespace, encoding: Template, }, Format { space0: Whitespace, fmt: Template, }, HtmlEscape, HtmlUnescape, JsonPath { space0: Whitespace, expr: Template, }, Nth { space0: Whitespace, n: U64, }, Regex { space0: Whitespace, value: RegexValue, }, Replace { space0: Whitespace, old_value: RegexValue, space1: Whitespace, new_value: Template, }, Split { space0: Whitespace, sep: Template, }, ToDate { space0: Whitespace, fmt: Template, }, ToFloat, ToInt, ToString, UrlDecode, UrlEncode, XPath { space0: Whitespace, expr: Template, }, } impl FilterValue { /// Returns the Hurl identifier for this filter type. pub fn identifier(&self) -> &'static str { match self { FilterValue::Base64Decode => "base64Decode", FilterValue::Base64Encode => "base64Encode", FilterValue::Count => "count", FilterValue::DaysAfterNow => "daysAfterNow", FilterValue::DaysBeforeNow => "daysBeforeNow", FilterValue::Decode { .. } => "decode", FilterValue::Format { .. } => "format", FilterValue::HtmlEscape => "htmlEscape", FilterValue::HtmlUnescape => "htmlUnescape", FilterValue::JsonPath { .. } => "jsonpath", FilterValue::Nth { .. } => "nth", FilterValue::Regex { .. } => "regex", FilterValue::Replace { .. } => "replace", FilterValue::Split { .. } => "split", FilterValue::ToDate { .. } => "toDate", FilterValue::ToFloat => "toFloat", FilterValue::ToInt => "toInt", FilterValue::ToString => "toString", FilterValue::UrlDecode => "urlDecode", FilterValue::UrlEncode => "urlEncode", FilterValue::XPath { .. } => "xpath", } } } hurl_core-6.1.1/src/ast/json.rs000064400000000000000000000072441046102023000145170ustar 00000000000000/* * Hurl (https://hurl.dev) * Copyright (C) 2025 Orange * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ use crate::ast::primitive::Placeholder; use crate::ast::Template; use crate::typing::{SourceString, ToSource}; /// This the AST for the JSON used within Hurl (for instance in [implicit JSON body request](https://hurl.dev/docs/request.html#json-body)). /// /// # Example /// /// ```hurl /// POST https://example.org/api/cats /// { /// "id": 42, /// "lives": {{lives_count}}, /// "name": "{{name}}" /// } /// ``` /// /// It is a superset of the standard JSON spec. Strings have been replaced by Hurl [`Placeholder`]. #[derive(Clone, Debug, PartialEq, Eq)] pub enum JsonValue { Placeholder(Placeholder), Number(String), String(Template), Boolean(bool), List { space0: String, elements: Vec, }, Object { space0: String, elements: Vec, }, Null, } impl ToSource for JsonValue { fn to_source(&self) -> SourceString { match self { JsonValue::Placeholder(expr) => format!("{{{{{expr}}}}}").to_source(), JsonValue::Number(s) => s.to_source(), JsonValue::String(template) => template.to_source(), JsonValue::Boolean(value) => { if *value { "true".to_source() } else { "false".to_source() } } JsonValue::List { space0, elements } => { let elements = elements .iter() .map(|e| e.to_source()) .collect::>(); format!("[{}{}]", space0, elements.join(",")).to_source() } JsonValue::Object { space0, elements } => { let elements = elements .iter() .map(|e| e.to_source()) .collect::>(); format!("{{{}{}}}", space0, elements.join(",")).to_source() } JsonValue::Null => "null".to_source(), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct JsonListElement { pub space0: String, pub value: JsonValue, pub space1: String, } impl ToSource for JsonListElement { fn to_source(&self) -> SourceString { let mut s = SourceString::new(); s.push_str(self.space0.as_str()); s.push_str(self.value.to_source().as_str()); s.push_str(self.space1.as_str()); s } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct JsonObjectElement { pub space0: String, pub name: Template, pub space1: String, pub space2: String, pub value: JsonValue, pub space3: String, } impl ToSource for JsonObjectElement { fn to_source(&self) -> SourceString { let mut s = SourceString::new(); s.push_str(self.space0.as_str()); s.push_str(self.name.to_source().as_str()); s.push_str(self.space1.as_str()); s.push(':'); s.push_str(self.space2.as_str()); s.push_str(self.value.to_source().as_str()); s.push_str(self.space3.as_str()); s } } hurl_core-6.1.1/src/ast/mod.rs000064400000000000000000000016621046102023000143230ustar 00000000000000/* * Hurl (https://hurl.dev) * Copyright (C) 2025 Orange * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ //! Exposes Hurl AST nodes (see [Hurl grammar](https://hurl.dev/docs/grammar.html)). pub use self::core::*; pub use self::json::{JsonListElement, JsonObjectElement, JsonValue}; pub use self::option::*; pub use self::primitive::*; pub use self::section::*; mod core; mod json; mod option; mod primitive; mod section; hurl_core-6.1.1/src/ast/option.rs000064400000000000000000000223571046102023000150600ustar 00000000000000/* * Hurl (https://hurl.dev) * Copyright (C) 2025 Orange * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ use std::fmt; use crate::ast::primitive::{ LineTerminator, Number, Placeholder, SourceInfo, Template, Whitespace, U64, }; use crate::typing::{Count, Duration, SourceString, ToSource}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct EntryOption { pub line_terminators: Vec, pub space0: Whitespace, pub space1: Whitespace, pub space2: Whitespace, pub kind: OptionKind, pub line_terminator0: LineTerminator, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum OptionKind { AwsSigV4(Template), CaCertificate(Template), ClientCert(Template), ClientKey(Template), Compressed(BooleanOption), ConnectTo(Template), ConnectTimeout(DurationOption), Delay(DurationOption), Header(Template), Http10(BooleanOption), Http11(BooleanOption), Http2(BooleanOption), Http3(BooleanOption), Insecure(BooleanOption), IpV4(BooleanOption), IpV6(BooleanOption), FollowLocation(BooleanOption), FollowLocationTrusted(BooleanOption), LimitRate(NaturalOption), MaxRedirect(CountOption), NetRc(BooleanOption), NetRcFile(Template), NetRcOptional(BooleanOption), Output(Template), PathAsIs(BooleanOption), Proxy(Template), Repeat(CountOption), Resolve(Template), Retry(CountOption), RetryInterval(DurationOption), Skip(BooleanOption), UnixSocket(Template), User(Template), Variable(VariableDefinition), Verbose(BooleanOption), VeryVerbose(BooleanOption), } impl OptionKind { /// Returns the Hurl string identifier of this option. pub fn identifier(&self) -> &'static str { match self { OptionKind::AwsSigV4(_) => "aws-sigv4", OptionKind::CaCertificate(_) => "cacert", OptionKind::ClientCert(_) => "cert", OptionKind::ClientKey(_) => "key", OptionKind::Compressed(_) => "compressed", OptionKind::ConnectTo(_) => "connect-to", OptionKind::ConnectTimeout(_) => "connect-timeout", OptionKind::Delay(_) => "delay", OptionKind::FollowLocation(_) => "location", OptionKind::FollowLocationTrusted(_) => "location-trusted", OptionKind::Header(_) => "header", OptionKind::Http10(_) => "http1.0", OptionKind::Http11(_) => "http1.1", OptionKind::Http2(_) => "http2", OptionKind::Http3(_) => "http3", OptionKind::Insecure(_) => "insecure", OptionKind::IpV4(_) => "ipv4", OptionKind::IpV6(_) => "ipv6", OptionKind::LimitRate(_) => "limit-rate", OptionKind::MaxRedirect(_) => "max-redirs", OptionKind::NetRc(_) => "netrc", OptionKind::NetRcFile(_) => "netrc-file", OptionKind::NetRcOptional(_) => "netrc-optional", OptionKind::Output(_) => "output", OptionKind::PathAsIs(_) => "path-as-is", OptionKind::Proxy(_) => "proxy", OptionKind::Repeat(_) => "repeat", OptionKind::Resolve(_) => "resolve", OptionKind::Retry(_) => "retry", OptionKind::RetryInterval(_) => "retry-interval", OptionKind::Skip(_) => "skip", OptionKind::UnixSocket(_) => "unix-socket", OptionKind::User(_) => "user", OptionKind::Variable(_) => "variable", OptionKind::Verbose(_) => "verbose", OptionKind::VeryVerbose(_) => "very-verbose", } } } impl fmt::Display for OptionKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let value = match self { OptionKind::AwsSigV4(value) => value.to_string(), OptionKind::CaCertificate(filename) => filename.to_string(), OptionKind::ClientCert(filename) => filename.to_string(), OptionKind::ClientKey(filename) => filename.to_string(), OptionKind::Compressed(value) => value.to_string(), OptionKind::ConnectTo(value) => value.to_string(), OptionKind::ConnectTimeout(value) => value.to_string(), OptionKind::Delay(value) => value.to_string(), OptionKind::FollowLocation(value) => value.to_string(), OptionKind::FollowLocationTrusted(value) => value.to_string(), OptionKind::Header(value) => value.to_string(), OptionKind::Http10(value) => value.to_string(), OptionKind::Http11(value) => value.to_string(), OptionKind::Http2(value) => value.to_string(), OptionKind::Http3(value) => value.to_string(), OptionKind::Insecure(value) => value.to_string(), OptionKind::IpV4(value) => value.to_string(), OptionKind::IpV6(value) => value.to_string(), OptionKind::LimitRate(value) => value.to_string(), OptionKind::MaxRedirect(value) => value.to_string(), OptionKind::NetRc(value) => value.to_string(), OptionKind::NetRcFile(filename) => filename.to_string(), OptionKind::NetRcOptional(value) => value.to_string(), OptionKind::Output(filename) => filename.to_string(), OptionKind::PathAsIs(value) => value.to_string(), OptionKind::Proxy(value) => value.to_string(), OptionKind::Repeat(value) => value.to_string(), OptionKind::Resolve(value) => value.to_string(), OptionKind::Retry(value) => value.to_string(), OptionKind::RetryInterval(value) => value.to_string(), OptionKind::Skip(value) => value.to_string(), OptionKind::UnixSocket(value) => value.to_string(), OptionKind::User(value) => value.to_string(), OptionKind::Variable(value) => value.to_string(), OptionKind::Verbose(value) => value.to_string(), OptionKind::VeryVerbose(value) => value.to_string(), }; write!(f, "{}: {}", self.identifier(), value) } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum BooleanOption { Literal(bool), Placeholder(Placeholder), } impl fmt::Display for BooleanOption { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BooleanOption::Literal(v) => write!(f, "{}", v), BooleanOption::Placeholder(v) => write!(f, "{}", v), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum NaturalOption { Literal(U64), Placeholder(Placeholder), } impl fmt::Display for NaturalOption { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NaturalOption::Literal(v) => write!(f, "{}", v), NaturalOption::Placeholder(v) => write!(f, "{}", v), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum CountOption { Literal(Count), Placeholder(Placeholder), } impl fmt::Display for CountOption { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { CountOption::Literal(v) => write!(f, "{}", v), CountOption::Placeholder(v) => write!(f, "{}", v), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum DurationOption { Literal(Duration), Placeholder(Placeholder), } impl fmt::Display for DurationOption { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { DurationOption::Literal(v) => write!(f, "{}", v), DurationOption::Placeholder(v) => write!(f, "{}", v), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct VariableDefinition { pub source_info: SourceInfo, pub name: String, pub space0: Whitespace, pub space1: Whitespace, pub value: VariableValue, } impl fmt::Display for VariableDefinition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}={}", self.name, self.value) } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum VariableValue { Null, Bool(bool), Number(Number), String(Template), } impl fmt::Display for VariableValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { VariableValue::Null => "null".to_string(), VariableValue::Bool(value) => value.to_string(), VariableValue::Number(n) => n.to_string(), VariableValue::String(s) => s.to_string(), }; write!(f, "{}", s) } } impl ToSource for VariableValue { fn to_source(&self) -> SourceString { match self { VariableValue::Null => "null".to_source(), VariableValue::Bool(value) => value.to_string().to_source(), VariableValue::Number(value) => value.to_source(), VariableValue::String(value) => value.to_string().to_source(), } } } hurl_core-6.1.1/src/ast/primitive.rs000064400000000000000000000540611046102023000155550ustar 00000000000000/* * Hurl (https://hurl.dev) * Copyright (C) 2025 Orange * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ use std::fmt; use std::fmt::Formatter; use crate::ast::JsonValue; use crate::reader::Pos; use crate::typing::{SourceString, ToSource}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct KeyValue { pub line_terminators: Vec, pub space0: Whitespace, pub key: Template, pub space1: Whitespace, pub space2: Whitespace, pub value: Template, pub line_terminator0: LineTerminator, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct MultilineString { pub attributes: Vec, pub space: Whitespace, pub newline: Whitespace, pub kind: MultilineStringKind, } impl fmt::Display for MultilineString { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match &self.kind { MultilineStringKind::Text(value) | MultilineStringKind::Json(value) | MultilineStringKind::Xml(value) => write!(f, "{value}"), MultilineStringKind::GraphQl(value) => write!(f, "{value}"), } } } impl ToSource for MultilineString { fn to_source(&self) -> SourceString { let mut source = SourceString::new(); let att = self .attributes .iter() .map(|att| att.to_source()) .collect::>() .join(","); source.push_str("```"); source.push_str(self.lang()); if !self.lang().is_empty() && !self.attributes.is_empty() { source.push(','); } source.push_str(&att); source.push_str(self.space.as_str()); source.push_str(self.newline.as_str()); source.push_str(self.kind.to_source().as_str()); source.push_str("```"); source } } impl MultilineString { pub fn lang(&self) -> &'static str { match self.kind { MultilineStringKind::Text(_) => "", MultilineStringKind::Json(_) => "json", MultilineStringKind::Xml(_) => "xml", MultilineStringKind::GraphQl(_) => "graphql", } } pub fn value(&self) -> Template { match &self.kind { MultilineStringKind::Text(text) | MultilineStringKind::Json(text) | MultilineStringKind::Xml(text) => text.clone(), MultilineStringKind::GraphQl(text) => text.value.clone(), } } /// Returns true if this multiline string has `escape` or `novariable` attributes. pub fn has_attributes(&self) -> bool { !self.attributes.is_empty() } } #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum MultilineStringKind { Text(Template), Json(Template), Xml(Template), GraphQl(GraphQl), } impl ToSource for MultilineStringKind { fn to_source(&self) -> SourceString { match self { MultilineStringKind::Text(value) | MultilineStringKind::Json(value) | MultilineStringKind::Xml(value) => value.to_source(), MultilineStringKind::GraphQl(value) => value.to_source(), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum MultilineStringAttribute { Escape, NoVariable, } impl ToSource for MultilineStringAttribute { fn to_source(&self) -> SourceString { match self { MultilineStringAttribute::Escape => "escape".to_source(), MultilineStringAttribute::NoVariable => "novariable".to_source(), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct GraphQl { pub value: Template, pub variables: Option, } impl fmt::Display for GraphQl { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.value)?; if let Some(vars) = &self.variables { write!(f, "{}", vars.to_source())?; } Ok(()) } } impl ToSource for GraphQl { fn to_source(&self) -> SourceString { let mut source = SourceString::new(); source.push_str(self.value.to_source().as_str()); if let Some(vars) = &self.variables { source.push_str(vars.to_source().as_str()); } source } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct GraphQlVariables { pub space: Whitespace, pub value: JsonValue, pub whitespace: Whitespace, } impl ToSource for GraphQlVariables { fn to_source(&self) -> SourceString { let mut source = "variable".to_source(); source.push_str(self.space.as_str()); source.push_str(self.value.to_source().as_str()); source.push_str(self.whitespace.as_str()); source } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Base64 { pub space0: Whitespace, pub value: Vec, pub source: SourceString, pub space1: Whitespace, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct File { pub space0: Whitespace, pub filename: Template, pub space1: Whitespace, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Template { pub delimiter: Option, pub elements: Vec, pub source_info: SourceInfo, } impl fmt::Display for Template { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let mut buffer = String::new(); for element in self.elements.iter() { buffer.push_str(element.to_string().as_str()); } write!(f, "{buffer}") } } impl ToSource for Template { fn to_source(&self) -> SourceString { let mut s = SourceString::new(); if let Some(d) = self.delimiter { s.push(d); } let elements: Vec = self.elements.iter().map(|e| e.to_source()).collect(); s.push_str(elements.join("").as_str()); if let Some(d) = self.delimiter { s.push(d); } s } } impl Template { /// Creates a new template. pub fn new( delimiter: Option, elements: Vec, source_info: SourceInfo, ) -> Template { Template { delimiter, elements, source_info, } } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum TemplateElement { String { value: String, source: SourceString }, Placeholder(Placeholder), } impl fmt::Display for TemplateElement { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let s = match self { TemplateElement::String { value, .. } => value.clone(), // TODO: see why we can't need to us `{{` and `}}` in a to_string method TemplateElement::Placeholder(value) => format!("{{{{{value}}}}}"), }; write!(f, "{s}") } } impl ToSource for TemplateElement { fn to_source(&self) -> SourceString { match self { TemplateElement::String { source, .. } => source.clone(), TemplateElement::Placeholder(value) => value.to_source(), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Comment { pub value: String, pub source_info: SourceInfo, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Whitespace { pub value: String, pub source_info: SourceInfo, } impl fmt::Display for Whitespace { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.value) } } impl Whitespace { pub fn as_str(&self) -> &str { &self.value } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum Number { Float(Float), Integer(I64), BigInteger(String), } impl fmt::Display for Number { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Number::Float(value) => write!(f, "{}", value), Number::Integer(value) => write!(f, "{}", value), Number::BigInteger(value) => write!(f, "{}", value), } } } impl ToSource for Number { fn to_source(&self) -> SourceString { match self { Number::Float(value) => value.to_source(), Number::Integer(value) => value.to_source(), Number::BigInteger(value) => value.to_source(), } } } // keep Number terminology for both Integer and Decimal Numbers // different representation for the same float value // 1.01 and 1.010 #[derive(Clone, Debug)] pub struct Float { value: f64, source: SourceString, } impl Float { pub fn new(value: f64, source: SourceString) -> Float { Float { value, source } } pub fn as_f64(&self) -> f64 { self.value } } impl fmt::Display for Float { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.value) } } impl ToSource for Float { fn to_source(&self) -> SourceString { self.source.clone() } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct U64 { value: u64, source: SourceString, } impl U64 { pub fn new(value: u64, source: SourceString) -> U64 { U64 { value, source } } pub fn as_u64(&self) -> u64 { self.value } } impl fmt::Display for U64 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.value) } } impl ToSource for U64 { fn to_source(&self) -> SourceString { self.source.clone() } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct I64 { value: i64, source: SourceString, } impl I64 { pub fn new(value: i64, source: SourceString) -> I64 { I64 { value, source } } pub fn as_i64(&self) -> i64 { self.value } } impl fmt::Display for I64 { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.value) } } impl ToSource for I64 { fn to_source(&self) -> SourceString { self.source.clone() } } impl PartialEq for Float { fn eq(&self, other: &Self) -> bool { self.source == other.source } } impl Eq for Float {} #[derive(Clone, Debug, PartialEq, Eq)] pub struct LineTerminator { pub space0: Whitespace, pub comment: Option, pub newline: Whitespace, } #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum Bytes { Json(JsonValue), Xml(String), MultilineString(MultilineString), OnelineString(Template), Base64(Base64), File(File), Hex(Hex), } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Hex { pub space0: Whitespace, pub value: Vec, pub source: SourceString, pub space1: Whitespace, } impl fmt::Display for Hex { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "hex,{}{}{};", self.space0, self.source, self.space1) } } /// Literal Regex. #[derive(Clone, Debug)] pub struct Regex { pub inner: regex::Regex, } impl fmt::Display for Regex { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.inner) } } impl PartialEq for Regex { fn eq(&self, other: &Self) -> bool { self.inner.to_string() == other.inner.to_string() } } impl Eq for Regex {} #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SourceInfo { pub start: Pos, pub end: Pos, } impl SourceInfo { pub fn new(start: Pos, end: Pos) -> SourceInfo { SourceInfo { start, end } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Placeholder { pub space0: Whitespace, pub expr: Expr, pub space1: Whitespace, } impl fmt::Display for Placeholder { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.expr) } } impl ToSource for Placeholder { fn to_source(&self) -> SourceString { let mut source = SourceString::new(); source.push_str("{{"); source.push_str(self.space0.as_str()); source.push_str(self.expr.to_source().as_str()); source.push_str(self.space1.as_str()); source.push_str("}}"); source } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Expr { pub source_info: SourceInfo, pub kind: ExprKind, } impl fmt::Display for Expr { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.kind) } } impl ToSource for Expr { fn to_source(&self) -> SourceString { self.kind.to_string().to_source() } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum ExprKind { Variable(Variable), Function(Function), } impl fmt::Display for ExprKind { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { ExprKind::Variable(variable) => write!(f, "{}", variable), ExprKind::Function(function) => write!(f, "{}", function), } } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Variable { pub name: String, pub source_info: SourceInfo, } impl fmt::Display for Variable { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.name) } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum Function { NewDate, NewUuid, } impl fmt::Display for Function { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Function::NewDate => write!(f, "newDate"), Function::NewUuid => write!(f, "newUuid"), } } } #[cfg(test)] mod tests { use super::*; use crate::ast::json::{JsonListElement, JsonObjectElement, JsonValue}; use crate::typing::ToSource; #[test] fn test_float() { assert_eq!( Float { value: 1.0, source: "1.0".to_source() } .to_source() .as_str(), "1.0" ); assert_eq!( Float { value: 1.0, source: "1.0".to_source() } .to_string(), "1" ); assert_eq!( Float { value: 1.01, source: "1.01".to_source() } .to_source() .as_str(), "1.01" ); assert_eq!( Float { value: 1.01, source: "1.01".to_source() } .to_string(), "1.01" ); assert_eq!( Float { value: 1.01, source: "1.010".to_source() } .to_source() .as_str(), "1.010" ); assert_eq!( Float { value: 1.01, source: "1.010".to_source() } .to_string(), "1.01" ); assert_eq!( Float { value: -1.333, source: "-1.333".to_source() } .to_source() .as_str(), "-1.333" ); assert_eq!( Float { value: -1.333, source: "-1.333".to_source() } .to_string(), "-1.333" ); } fn whitespace() -> Whitespace { Whitespace { value: String::new(), source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)), } } fn variable_placeholder() -> Placeholder { Placeholder { space0: whitespace(), expr: Expr { kind: ExprKind::Variable(Variable { name: "name".to_string(), source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)), }), source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)), }, space1: whitespace(), } } fn hello_template() -> Template { Template::new( None, vec![ TemplateElement::String { value: "Hello ".to_string(), source: "Hello ".to_source(), }, TemplateElement::Placeholder(variable_placeholder()), TemplateElement::String { value: "!".to_string(), source: "!".to_source(), }, ], SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)), ) } #[test] fn test_template() { assert_eq!(hello_template().to_string(), "Hello {{name}}!"); } #[test] fn test_template_to_source() { assert_eq!( "{{x}}", JsonValue::Placeholder(Placeholder { space0: Whitespace { value: String::new(), source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)), }, expr: Expr { kind: ExprKind::Variable(Variable { name: "x".to_string(), source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)), }), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }, space1: Whitespace { value: String::new(), source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)), }, }) .to_source() .as_str() ); assert_eq!("1", JsonValue::Number("1".to_string()).to_source().as_str()); assert_eq!( "\"hello\"", JsonValue::String(Template::new( Some('"'), vec![TemplateElement::String { value: "hello".to_string(), source: "hello".to_source(), }], SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)) )) .to_source() .as_str() ); assert_eq!("true", JsonValue::Boolean(true).to_source().as_str()); assert_eq!( "[]", JsonValue::List { space0: String::new(), elements: vec![], } .to_source() .as_str() ); assert_eq!( "[1, 2, 3]", JsonValue::List { space0: String::new(), elements: vec![ JsonListElement { space0: String::new(), value: JsonValue::Number("1".to_string()), space1: String::new(), }, JsonListElement { space0: " ".to_string(), value: JsonValue::Number("2".to_string()), space1: String::new(), }, JsonListElement { space0: " ".to_string(), value: JsonValue::Number("3".to_string()), space1: String::new(), } ], } .to_source() .as_str() ); assert_eq!( "{}", JsonValue::Object { space0: String::new(), elements: vec![], } .to_source() .as_str() ); assert_eq!( "{ \"id\": 123 }", JsonValue::Object { space0: String::new(), elements: vec![JsonObjectElement { space0: " ".to_string(), name: Template::new( Some('"'), vec![TemplateElement::String { value: "id".to_string(), source: "id".to_source(), }], SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)) ), space1: String::new(), space2: " ".to_string(), value: JsonValue::Number("123".to_string()), space3: " ".to_string(), }], } .to_source() .as_str() ); assert_eq!("null", JsonValue::Null.to_source().as_str()); assert_eq!( "{{name}}", TemplateElement::Placeholder(Placeholder { space0: Whitespace { value: String::new(), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }, expr: Expr { kind: ExprKind::Variable(Variable { name: "name".to_string(), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }, space1: Whitespace { value: String::new(), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }, }) .to_source() .as_str(), ); assert_eq!( "{{name}}", Template::new( None, vec![TemplateElement::Placeholder(Placeholder { space0: Whitespace { value: String::new(), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }, expr: Expr { kind: ExprKind::Variable(Variable { name: "name".to_string(), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }, space1: Whitespace { value: String::new(), source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)), }, })], SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)) ) .to_source() .as_str(), ); } } hurl_core-6.1.1/src/ast/section.rs000064400000000000000000000315121046102023000152050ustar 00000000000000/* * Hurl (https://hurl.dev) * Copyright (C) 2025 Orange * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ use std::fmt; use crate::ast::option::EntryOption; use crate::ast::primitive::{ Base64, File, Hex, KeyValue, LineTerminator, MultilineString, Number, Placeholder, Regex, SourceInfo, Template, Whitespace, }; use crate::ast::Filter; #[derive(Clone, Debug, PartialEq, Eq)] pub struct Section { pub line_terminators: Vec, pub space0: Whitespace, pub line_terminator0: LineTerminator, pub value: SectionValue, pub source_info: SourceInfo, } impl Section { /// Returns the Hurl string identifier of this section. pub fn identifier(&self) -> &'static str { match self.value { SectionValue::Asserts(_) => "Asserts", SectionValue::QueryParams(_, true) => "Query", SectionValue::QueryParams(_, false) => "QueryStringParams", SectionValue::BasicAuth(_) => "BasicAuth", SectionValue::FormParams(_, true) => "Form", SectionValue::FormParams(_, false) => "FormParams", SectionValue::Cookies(_) => "Cookies", SectionValue::Captures(_) => "Captures", SectionValue::MultipartFormData(_, true) => "Multipart", SectionValue::MultipartFormData(_, false) => "MultipartFormData", SectionValue::Options(_) => "Options", } } } #[derive(Clone, Debug, PartialEq, Eq)] #[allow(clippy::large_enum_variant)] pub enum SectionValue { QueryParams(Vec, bool), // boolean param indicates if we use the short syntax BasicAuth(Option), // boolean param indicates if we use the short syntax FormParams(Vec, bool), MultipartFormData(Vec, bool), // boolean param indicates if we use the short syntax Cookies(Vec), Captures(Vec), Asserts(Vec), Options(Vec), } #[derive(Clone, Debug, PartialEq, Eq)] pub struct Cookie { pub line_terminators: Vec, pub space0: Whitespace, pub name: Template, pub space1: Whitespace, pub space2: Whitespace, pub value: Template, pub line_terminator0: LineTerminator, } #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum MultipartParam { Param(KeyValue), FileParam(FileParam), } #[derive(Clone, Debug, PartialEq, Eq)] pub struct FileParam { pub line_terminators: Vec, pub space0: Whitespace, pub key: Template, pub space1: Whitespace, pub space2: Whitespace, pub value: FileValue, pub line_terminator0: LineTerminator, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct FileValue { pub space0: Whitespace, pub filename: Template, pub space1: Whitespace, pub space2: Whitespace, pub content_type: Option