Lines
95.24 %
Functions
87.5 %
Branches
100 %
/*
* 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<Entry>,
pub line_terminators: Vec<LineTerminator>,
}
pub struct Entry {
pub request: Request,
pub response: Option<Response>,
impl Entry {
/// Returns the source information for this entry.
pub fn source_info(&self) -> SourceInfo {
self.request.space0.source_info
pub struct Request {
pub space0: Whitespace,
pub method: Method,
pub space1: Whitespace,
pub url: Template,
pub line_terminator0: LineTerminator,
pub headers: Vec<KeyValue>,
pub sections: Vec<Section>,
pub body: Option<Body>,
pub source_info: SourceInfo,
impl Request {
/// Returns the query strings params for this request.
///
/// See <https://developer.mozilla.org/en-US/docs/Web/API/URL/searchParams>.
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 <https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST#url-encoded_form_submission>.
pub fn form_params(&self) -> &[KeyValue] {
if let SectionValue::FormParams(params, _) = §ion.value {
/// Returns the multipart form data for this request.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST#multipart_form_submission>.
pub fn multipart_form_data(&self) -> &[MultipartParam] {
if let SectionValue::MultipartFormData(params, _) = §ion.value {
/// Returns the list of cookies on this request.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie>.
pub fn cookies(&self) -> &[Cookie] {
if let SectionValue::Cookies(cookies) = §ion.value {
return cookies;
/// Returns the basic authentication on this request.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication>.
pub fn basic_auth(&self) -> Option<&KeyValue> {
if let SectionValue::BasicAuth(kv) = §ion.value {
return kv.as_ref();
None
/// Returns the options specific for this request.
pub fn options(&self) -> &[EntryOption] {
if let SectionValue::Options(options) = §ion.value {
return options;
pub struct Response {
pub version: Version,
pub status: Status,
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] {
if let SectionValue::Asserts(asserts) = §ion.value {
return asserts;
pub struct Method(pub String);
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
pub struct Version {
pub value: VersionValue,
impl fmt::Display for Version {
write!(f, "{}", self.value)
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum VersionValue {
Version1,
Version11,
Version2,
Version3,
VersionAny,
impl fmt::Display for VersionValue {
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}")
pub struct Status {
pub value: StatusValue,
impl fmt::Display for Status {
pub enum StatusValue {
Any,
Specific(u64),
impl fmt::Display for StatusValue {
match self {
StatusValue::Any => write!(f, "*"),
StatusValue::Specific(v) => write!(f, "{v}"),
pub struct Body {
pub value: Bytes,
/// 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)
pub struct Filter {
pub value: FilterValue,
pub enum FilterValue {
Base64Decode,
Base64Encode,
Count,
DaysAfterNow,
DaysBeforeNow,
Decode {
space0: Whitespace,
encoding: Template,
},
Format {
fmt: Template,
HtmlEscape,
HtmlUnescape,
JsonPath {
expr: Template,
Nth {
n: U64,
Regex {
value: RegexValue,
Replace {
old_value: RegexValue,
space1: Whitespace,
new_value: Template,
Split {
sep: Template,
ToDate {
ToFloat,
ToInt,
UrlDecode,
UrlEncode,
XPath {
impl FilterValue {
/// Returns the Hurl identifier for this filter type.
pub fn identifier(&self) -> &'static str {
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::UrlDecode => "urlDecode",
FilterValue::UrlEncode => "urlEncode",
FilterValue::XPath { .. } => "xpath",