hurl_core-7.1.0/.cargo_vcs_info.json0000644000000001600000000000100130200ustar { "git": { "sha1": "77798424906a431e5b1c136f540d6cb5ed79b788" }, "path_in_vcs": "packages/hurl_core" }hurl_core-7.1.0/Cargo.lock0000644000000206740000000000100110070ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] name = "bindgen" version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags", "cexpr", "clang-sys", "itertools", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", ] [[package]] name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clang-sys" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", "libloading", ] [[package]] name = "colored" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ "windows-sys", ] [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "hurl_core" version = "7.1.0" dependencies = [ "colored", "libxml", "regex", ] [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "libc" version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libloading" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", "windows-link", ] [[package]] name = "libxml" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74a5e46b8bcd6b70cb485ca086e43aa020af841e29fb0aba88ce02cd1cb52cc7" dependencies = [ "bindgen", "libc", "pkg-config", "vcpkg", ] [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[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-7.1.0/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 = "7.1.0" 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.8" [dependencies.regex] version = "1.12.2" [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-7.1.0/Cargo.toml.orig000064400000000000000000000007431046102023000145060ustar 00000000000000[package] name = "hurl_core" version = "7.1.0" 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.8" regex = "1.12.2" [lints] workspace = true hurl_core-7.1.0/README.md000064400000000000000000000001631046102023000130720ustar 00000000000000hurl_core =============== The hurl_core crate provides the common functionalities used by both hurl and hurlfmt. hurl_core-7.1.0/src/ast/core.rs000064400000000000000000000270111046102023000144700ustar 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, I64, }; use crate::ast::section::{ Assert, Capture, Cookie, MultipartParam, RegexValue, Section, SectionValue, }; use crate::ast::Placeholder; use crate::types::{SourceString, ToSource}; /// 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) } } impl ToSource for Method { fn to_source(&self) -> SourceString { self.0.to_source() } } #[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}") } } impl ToSource for VersionValue { fn to_source(&self) -> SourceString { self.to_string().to_source() } } #[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}"), } } } impl ToSource for StatusValue { fn to_source(&self) -> SourceString { self.to_string().to_source() } } #[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, Base64UrlSafeDecode, Base64UrlSafeEncode, Count, DaysAfterNow, DaysBeforeNow, Decode { space0: Whitespace, encoding: Template, }, First, Format { space0: Whitespace, fmt: Template, }, DateFormat { space0: Whitespace, fmt: Template, }, HtmlEscape, HtmlUnescape, JsonPath { space0: Whitespace, expr: Template, }, Last, Location, Nth { space0: Whitespace, n: IntegerValue, }, Regex { space0: Whitespace, value: RegexValue, }, Replace { space0: Whitespace, old_value: Template, space1: Whitespace, new_value: Template, }, ReplaceRegex { space0: Whitespace, pattern: RegexValue, space1: Whitespace, new_value: Template, }, Split { space0: Whitespace, sep: Template, }, ToDate { space0: Whitespace, fmt: Template, }, ToFloat, ToHex, ToInt, ToString, UrlDecode, UrlEncode, UrlQueryParam { space0: Whitespace, param: Template, }, Utf8Decode, Utf8Encode, 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::Base64UrlSafeDecode => "base64UrlSafeDecode", FilterValue::Base64UrlSafeEncode => "base64UrlSafeEncode", FilterValue::Count => "count", FilterValue::DaysAfterNow => "daysAfterNow", FilterValue::DaysBeforeNow => "daysBeforeNow", FilterValue::Decode { .. } => "decode", FilterValue::First => "first", FilterValue::Format { .. } => "format", FilterValue::DateFormat { .. } => "dateFormat", FilterValue::HtmlEscape => "htmlEscape", FilterValue::HtmlUnescape => "htmlUnescape", FilterValue::JsonPath { .. } => "jsonpath", FilterValue::Last => "last", FilterValue::Location => "location", FilterValue::Nth { .. } => "nth", FilterValue::Regex { .. } => "regex", FilterValue::Replace { .. } => "replace", FilterValue::ReplaceRegex { .. } => "replaceRegex", FilterValue::Split { .. } => "split", FilterValue::ToDate { .. } => "toDate", FilterValue::ToFloat => "toFloat", FilterValue::ToHex => "toHex", FilterValue::ToInt => "toInt", FilterValue::ToString => "toString", FilterValue::UrlDecode => "urlDecode", FilterValue::UrlEncode => "urlEncode", FilterValue::UrlQueryParam { .. } => "urlQueryParam", FilterValue::Utf8Decode => "utf8Decode", FilterValue::Utf8Encode => "utf8Encode", FilterValue::XPath { .. } => "xpath", } } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum IntegerValue { Literal(I64), Placeholder(Placeholder), } impl fmt::Display for IntegerValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { IntegerValue::Literal(v) => write!(f, "{v}"), IntegerValue::Placeholder(v) => write!(f, "{v}"), } } } hurl_core-7.1.0/src/ast/json.rs000064400000000000000000000072431046102023000145160ustar 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::types::{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-7.1.0/src/ast/mod.rs000064400000000000000000000017011046102023000143150ustar 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; pub mod visit; hurl_core-7.1.0/src/ast/option.rs000064400000000000000000000234161046102023000150550ustar 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::types::{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), MaxTime(DurationOption), Negotiate(BooleanOption), NetRc(BooleanOption), NetRcFile(Template), NetRcOptional(BooleanOption), Ntlm(BooleanOption), Output(Template), PathAsIs(BooleanOption), PinnedPublicKey(Template), 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::MaxTime(_) => "max-time", OptionKind::Negotiate(_) => "negotiate", OptionKind::NetRc(_) => "netrc", OptionKind::NetRcFile(_) => "netrc-file", OptionKind::NetRcOptional(_) => "netrc-optional", OptionKind::Ntlm(_) => "ntlm", OptionKind::Output(_) => "output", OptionKind::PathAsIs(_) => "path-as-is", OptionKind::PinnedPublicKey(_) => "pinnedpubkey", 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::MaxTime(value) => value.to_string(), OptionKind::Negotiate(value) => value.to_string(), OptionKind::NetRc(value) => value.to_string(), OptionKind::NetRcFile(filename) => filename.to_string(), OptionKind::NetRcOptional(value) => value.to_string(), OptionKind::Ntlm(value) => value.to_string(), OptionKind::Output(filename) => filename.to_string(), OptionKind::PathAsIs(value) => value.to_string(), OptionKind::PinnedPublicKey(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_source(), } } } hurl_core-7.1.0/src/ast/primitive.rs000064400000000000000000000546111046102023000155560ustar 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::types::{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.identifier()) .collect::>() .join(","); source.push_str("```"); source.push_str(self.lang()); if !self.lang().is_empty() && self.has_attributes() { 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(Copy, Clone, Debug, PartialEq, Eq)] pub enum MultilineStringAttribute { Escape, NoVariable, } impl MultilineStringAttribute { pub fn identifier(&self) -> &'static str { match self { MultilineStringAttribute::Escape => "escape", MultilineStringAttribute::NoVariable => "novariable", } } } #[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 = "variables".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, } } /// Returns true if this template is empty. pub fn is_empty(&self) -> bool { self.elements.is_empty() } } #[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, } impl ToSource for Comment { fn to_source(&self) -> SourceString { format!("#{}", self.value).to_source() } } #[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, pub source: SourceString, } impl ToSource for Regex { fn to_source(&self) -> SourceString { self.source.clone() } } 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::types::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-7.1.0/src/ast/section.rs000064400000000000000000000337011046102023000152070ustar 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; use crate::types::{SourceString, ToSource}; #[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), FilenameParam(FilenameParam), } #[derive(Clone, Debug, PartialEq, Eq)] pub struct FilenameParam { pub line_terminators: Vec, pub space0: Whitespace, pub key: Template, pub space1: Whitespace, pub space2: Whitespace, pub value: FilenameValue, pub line_terminator0: LineTerminator, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct FilenameValue { pub space0: Whitespace, pub filename: Template, pub space1: Whitespace, pub space2: Whitespace, pub content_type: Option