Split project into multiples packages
This commit is contained in:
parent
198caf5603
commit
fb312a8b73
|
|
@ -359,6 +359,60 @@ dependencies = [
|
|||
"curl",
|
||||
"encoding",
|
||||
"float-cmp",
|
||||
"hurl_core",
|
||||
"libflate",
|
||||
"libxml",
|
||||
"proptest",
|
||||
"regex",
|
||||
"roxmltree",
|
||||
"serde",
|
||||
"serde-xml-rs",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
"sxd-document",
|
||||
"url",
|
||||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hurl_core"
|
||||
version = "0.99.14"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"base64",
|
||||
"brotli",
|
||||
"chrono",
|
||||
"clap",
|
||||
"curl",
|
||||
"encoding",
|
||||
"float-cmp",
|
||||
"libflate",
|
||||
"libxml",
|
||||
"proptest",
|
||||
"regex",
|
||||
"roxmltree",
|
||||
"serde",
|
||||
"serde-xml-rs",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
"sxd-document",
|
||||
"url",
|
||||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hurlfmt"
|
||||
version = "0.99.14"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"base64",
|
||||
"brotli",
|
||||
"chrono",
|
||||
"clap",
|
||||
"curl",
|
||||
"encoding",
|
||||
"float-cmp",
|
||||
"hurl_core",
|
||||
"libflate",
|
||||
"libxml",
|
||||
"proptest",
|
||||
|
|
|
|||
49
Cargo.toml
49
Cargo.toml
|
|
@ -1,43 +1,6 @@
|
|||
[package]
|
||||
name = "hurl"
|
||||
version = "0.99.14"
|
||||
authors = ["Fabrice Reix <fabrice.reix@orange.com>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
name = "hurl"
|
||||
|
||||
[features]
|
||||
# Treat warnings as a build error.
|
||||
strict = []
|
||||
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
structopt = "0.2.10"
|
||||
libxml = "0.2.12"
|
||||
regex = "1.1.0"
|
||||
serde_json = "1.0.40"
|
||||
xmlparser = "0.10.0"
|
||||
roxmltree = "0.7.1"
|
||||
serde-xml-rs = "0.3.1"
|
||||
atty = "0.2.13"
|
||||
url = "2.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
serde = "1.0.104"
|
||||
base64 = "0.11.0"
|
||||
float-cmp = "0.6.0"
|
||||
encoding = "0.2"
|
||||
chrono = "0.4.11"
|
||||
curl = "0.4.33"
|
||||
brotli="3.3.0"
|
||||
libflate = "1.0.2"
|
||||
|
||||
|
||||
#[dev-dependencies]
|
||||
proptest = "0.9.4"
|
||||
|
||||
|
||||
|
||||
|
||||
[workspace]
|
||||
members =[
|
||||
"packages/hurl",
|
||||
"packages/hurlfmt",
|
||||
"packages/hurl_core",
|
||||
]
|
||||
|
|
|
|||
44
build.rs
44
build.rs
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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::process;
|
||||
|
||||
// objectives
|
||||
// get version
|
||||
// get git commit
|
||||
// get build date
|
||||
// => hard-code into your binaries
|
||||
fn main() {
|
||||
// Make the current git hash available to the build.
|
||||
if let Some(rev) = git_revision_hash() {
|
||||
println!("cargo:rustc-env=HURL_BUILD_GIT_HASH={}", rev);
|
||||
}
|
||||
}
|
||||
|
||||
fn git_revision_hash() -> Option<String> {
|
||||
let result = process::Command::new("git")
|
||||
.args(&["rev-parse", "--short=10", "HEAD"])
|
||||
.output();
|
||||
result.ok().and_then(|output| {
|
||||
let v = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
if v.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(v)
|
||||
}
|
||||
})
|
||||
}
|
||||
5
build.sh
5
build.sh
|
|
@ -1,11 +1,12 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cargo build --features "strict"
|
||||
#cargo build --features "strict"
|
||||
cargo build
|
||||
cargo test
|
||||
cargo doc --document-private-items
|
||||
|
||||
touch src/lib.rs
|
||||
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
cargo fmt -- --check
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
[package]
|
||||
name = "hurl"
|
||||
version = "0.99.14"
|
||||
authors = ["Fabrice Reix <fabrice.reix@orange.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "hurl"
|
||||
|
||||
[features]
|
||||
# Treat warnings as a build error.
|
||||
strict = []
|
||||
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
structopt = "0.2.10"
|
||||
libxml = "0.2.12"
|
||||
regex = "1.1.0"
|
||||
serde_json = "1.0.40"
|
||||
xmlparser = "0.10.0"
|
||||
roxmltree = "0.7.1"
|
||||
serde-xml-rs = "0.3.1"
|
||||
atty = "0.2.13"
|
||||
url = "2.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
serde = "1.0.104"
|
||||
base64 = "0.11.0"
|
||||
float-cmp = "0.6.0"
|
||||
encoding = "0.2"
|
||||
chrono = "0.4.11"
|
||||
curl = "0.4.33"
|
||||
brotli="3.3.0"
|
||||
libflate = "1.0.2"
|
||||
|
||||
[dependencies.hurl_core]
|
||||
path = "../hurl_core"
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "0.9.4"
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
use crate::ast::SourceInfo;
|
||||
use crate::linter;
|
||||
use crate::linter::LinterError;
|
||||
use crate::parser;
|
||||
use crate::parser::ParseError;
|
||||
use crate::runner;
|
||||
use crate::runner::RunnerError;
|
||||
use hurl_core::ast::SourceInfo;
|
||||
use hurl_core::parser;
|
||||
use hurl_core::parser::ParseError;
|
||||
|
||||
pub trait Error {
|
||||
fn source_info(&self) -> SourceInfo;
|
||||
|
|
@ -217,29 +215,3 @@ impl Error for runner::Error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Textual Output for linter errors
|
||||
///
|
||||
///
|
||||
impl Error for linter::Error {
|
||||
fn source_info(&self) -> SourceInfo {
|
||||
self.clone().source_info
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
match self.inner {
|
||||
LinterError::UnneccessarySpace { .. } => "Unnecessary space".to_string(),
|
||||
LinterError::UnneccessaryJsonEncoding {} => "Unnecessary json encoding".to_string(),
|
||||
LinterError::OneSpace {} => "One space ".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn fixme(&self) -> String {
|
||||
match self.inner {
|
||||
LinterError::UnneccessarySpace { .. } => "Remove space".to_string(),
|
||||
LinterError::UnneccessaryJsonEncoding {} => "Use Simple String".to_string(),
|
||||
LinterError::OneSpace {} => "Use only one space".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,9 +18,8 @@
|
|||
|
||||
use super::error::Error;
|
||||
use crate::format::TerminalColor;
|
||||
use crate::linter;
|
||||
use crate::parser;
|
||||
use crate::runner;
|
||||
use hurl_core::parser;
|
||||
|
||||
pub fn make_logger_verbose(verbose: bool) -> impl Fn(&str) {
|
||||
move |message| log_verbose(verbose, message)
|
||||
|
|
@ -50,16 +49,6 @@ pub fn make_logger_runner_error(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn make_logger_linter_error(
|
||||
lines: Vec<String>,
|
||||
color: bool,
|
||||
filename: Option<String>,
|
||||
) -> impl Fn(&linter::Error, bool) {
|
||||
move |error: &linter::Error, warning: bool| {
|
||||
log_error(lines.clone(), color, filename.clone(), error, warning)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_info(message: &str) {
|
||||
eprintln!("{}", message);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
pub use self::error::Error;
|
||||
pub use self::logger::{
|
||||
log_info, make_logger_error_message, make_logger_parser_error, make_logger_runner_error,
|
||||
make_logger_verbose,
|
||||
};
|
||||
pub use self::options::cookies_output_file;
|
||||
pub use self::options::CLIError;
|
||||
|
||||
mod error;
|
||||
mod logger;
|
||||
mod options;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
use crate::ast::*;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
pub trait Htmlable {
|
||||
fn to_html(&self) -> String;
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
use crate::ast::*;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::color::TerminalColor;
|
||||
use super::token::*;
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use crate::ast::*;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Token {
|
||||
|
|
@ -20,12 +20,9 @@
|
|||
#[macro_use]
|
||||
extern crate float_cmp;
|
||||
|
||||
pub mod ast;
|
||||
pub mod cli;
|
||||
pub mod format;
|
||||
pub mod html;
|
||||
pub mod http;
|
||||
pub mod jsonpath;
|
||||
pub mod linter;
|
||||
pub mod parser;
|
||||
pub mod runner;
|
||||
|
|
@ -22,20 +22,20 @@ use std::fs;
|
|||
use std::io::prelude::*;
|
||||
use std::io::{self, Read};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use atty::Stream;
|
||||
use chrono::{DateTime, Local};
|
||||
use clap::{AppSettings, ArgMatches};
|
||||
|
||||
use hurl::ast::{Pos, SourceInfo};
|
||||
use hurl::cli;
|
||||
use hurl::cli::Error;
|
||||
use hurl::html;
|
||||
use hurl::http;
|
||||
use hurl::parser;
|
||||
use hurl::runner;
|
||||
use hurl::runner::{HurlResult, RunnerOptions};
|
||||
use std::time::Duration;
|
||||
use hurl_core::ast::{Pos, SourceInfo};
|
||||
use hurl_core::parser;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CLIOptions {
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::*;
|
||||
use super::core::{Error, RunnerError};
|
||||
|
|
@ -149,7 +149,7 @@ pub fn eval_assert(
|
|||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::ast::SourceInfo;
|
||||
use hurl_core::ast::SourceInfo;
|
||||
|
||||
use super::super::query;
|
||||
use super::*;
|
||||
|
|
@ -21,7 +21,7 @@ use std::fs::File;
|
|||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::ast::*;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::json::eval_json_value;
|
||||
|
|
@ -85,7 +85,7 @@ pub fn eval_bytes(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ast::SourceInfo;
|
||||
use hurl_core::ast::SourceInfo;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::RunnerError;
|
||||
use super::core::{CaptureResult, Error};
|
||||
|
|
@ -98,7 +98,7 @@ fn eval_subquery(
|
|||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::ast::{Pos, SourceInfo};
|
||||
use hurl_core::ast::{Pos, SourceInfo};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -17,8 +17,8 @@
|
|||
*/
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ast::SourceInfo;
|
||||
use crate::http;
|
||||
use hurl_core::ast::SourceInfo;
|
||||
|
||||
use super::value::Value;
|
||||
|
||||
|
|
@ -18,9 +18,9 @@
|
|||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
use crate::http::HttpError;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::*;
|
||||
use super::core::{Error, RunnerError};
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ast::Expr;
|
||||
use hurl_core::ast::Expr;
|
||||
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::value::Value;
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::*;
|
||||
use super::entry;
|
||||
|
|
@ -30,8 +30,8 @@ use super::value::Value;
|
|||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use hurl_core::parser;
|
||||
/// use hurl::http;
|
||||
/// use hurl::parser;
|
||||
/// use hurl::runner;
|
||||
///
|
||||
/// // Parse Hurl file
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ast::{JsonListElement, JsonObjectElement, JsonValue};
|
||||
use hurl_core::ast::{JsonListElement, JsonObjectElement, JsonValue};
|
||||
|
||||
use super::core::Error;
|
||||
use super::template::eval_template;
|
||||
|
|
@ -77,7 +77,60 @@ pub fn eval_json_object_element(
|
|||
mod tests {
|
||||
use super::super::core::RunnerError;
|
||||
use super::*;
|
||||
use crate::ast::*;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
pub fn json_hello_world_value() -> JsonValue {
|
||||
// "hello\u0020{{name}}!"
|
||||
JsonValue::String(Template {
|
||||
quotes: true,
|
||||
elements: vec![
|
||||
TemplateElement::String {
|
||||
value: "Hello ".to_string(),
|
||||
encoded: "Hello\\u0020".to_string(),
|
||||
},
|
||||
TemplateElement::Expression(Expr {
|
||||
space0: Whitespace {
|
||||
value: "".to_string(),
|
||||
source_info: SourceInfo::init(1, 15, 1, 15),
|
||||
},
|
||||
variable: Variable {
|
||||
name: "name".to_string(),
|
||||
source_info: SourceInfo::init(1, 15, 1, 19),
|
||||
},
|
||||
space1: Whitespace {
|
||||
value: "".to_string(),
|
||||
source_info: SourceInfo::init(1, 19, 1, 19),
|
||||
},
|
||||
}),
|
||||
TemplateElement::String {
|
||||
value: "!".to_string(),
|
||||
encoded: "!".to_string(),
|
||||
},
|
||||
],
|
||||
source_info: SourceInfo::init(1, 2, 1, 22),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn json_person_value() -> JsonValue {
|
||||
JsonValue::Object {
|
||||
space0: "\n ".to_string(),
|
||||
elements: vec![JsonObjectElement {
|
||||
space0: "".to_string(),
|
||||
name: "firstName".to_string(),
|
||||
space1: "".to_string(),
|
||||
space2: " ".to_string(),
|
||||
value: JsonValue::String(Template {
|
||||
quotes: false,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "John".to_string(),
|
||||
encoded: "John".to_string(),
|
||||
}],
|
||||
source_info: SourceInfo::init(1, 1, 1, 1),
|
||||
}),
|
||||
space3: "\n".to_string(),
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scalar_value() {
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
extern crate libxml;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
#[allow(unused)]
|
||||
use std::io::prelude::*;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::http;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
|
||||
pub fn eval_multipart_param(
|
||||
multipart_param: MultipartParam,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<http::MultipartParam, Error> {
|
||||
match multipart_param {
|
||||
MultipartParam::Param(KeyValue { key, value, .. }) => {
|
||||
let name = key.value;
|
||||
let value = eval_template(value, variables)?;
|
||||
Ok(http::MultipartParam::Param(http::Param { name, value }))
|
||||
}
|
||||
MultipartParam::FileParam(param) => {
|
||||
let file_param = eval_file_param(param, context_dir)?;
|
||||
Ok(http::MultipartParam::FileParam(file_param))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_file_param(
|
||||
file_param: FileParam,
|
||||
context_dir: String,
|
||||
) -> Result<http::FileParam, Error> {
|
||||
let name = file_param.key.value;
|
||||
|
||||
let filename = file_param.value.filename.clone();
|
||||
let path = Path::new(filename.value.as_str());
|
||||
let absolute_filename = if path.is_absolute() {
|
||||
filename.value.clone()
|
||||
} else {
|
||||
Path::new(context_dir.as_str())
|
||||
.join(filename.value.clone())
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
};
|
||||
|
||||
let data = match File::open(absolute_filename.clone()) {
|
||||
Ok(mut f) => {
|
||||
let mut bytes = Vec::new();
|
||||
match f.read_to_end(&mut bytes) {
|
||||
Ok(_) => bytes,
|
||||
Err(_) => {
|
||||
return Err(Error {
|
||||
source_info: filename.source_info,
|
||||
inner: RunnerError::FileReadAccess {
|
||||
value: absolute_filename,
|
||||
},
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(Error {
|
||||
source_info: filename.source_info,
|
||||
inner: RunnerError::FileReadAccess {
|
||||
value: absolute_filename,
|
||||
},
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if !Path::new(&absolute_filename).exists() {
|
||||
return Err(Error {
|
||||
source_info: filename.source_info,
|
||||
inner: RunnerError::FileReadAccess {
|
||||
value: filename.value.clone(),
|
||||
},
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
|
||||
let content_type = file_value_content_type(file_param.value);
|
||||
Ok(http::FileParam {
|
||||
name,
|
||||
filename: filename.value,
|
||||
data,
|
||||
content_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn file_value_content_type(file_value: FileValue) -> String {
|
||||
match file_value.content_type.clone() {
|
||||
None => match Path::new(file_value.filename.value.as_str())
|
||||
.extension()
|
||||
.and_then(OsStr::to_str)
|
||||
{
|
||||
Some("gif") => "image/gif".to_string(),
|
||||
Some("jpg") => "image/jpeg".to_string(),
|
||||
Some("jpeg") => "image/jpeg".to_string(),
|
||||
Some("png") => "image/png".to_string(),
|
||||
Some("svg") => "image/svg+xml".to_string(),
|
||||
Some("txt") => "text/plain".to_string(),
|
||||
Some("htm") => "text/html".to_string(),
|
||||
Some("html") => "text/html".to_string(),
|
||||
Some("pdf") => "application/pdf".to_string(),
|
||||
Some("xml") => "application/xml".to_string(),
|
||||
_ => "application/octet-stream".to_string(),
|
||||
},
|
||||
Some(content_type) => content_type,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hurl_core::ast::SourceInfo;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn whitespace() -> Whitespace {
|
||||
Whitespace {
|
||||
value: String::from(" "),
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_eval_file_param() {
|
||||
let line_terminator = LineTerminator {
|
||||
space0: whitespace(),
|
||||
comment: None,
|
||||
newline: whitespace(),
|
||||
};
|
||||
assert_eq!(
|
||||
eval_file_param(
|
||||
FileParam {
|
||||
line_terminators: vec![],
|
||||
space0: whitespace(),
|
||||
key: EncodedString {
|
||||
value: "upload1".to_string(),
|
||||
encoded: "upload1".to_string(),
|
||||
quotes: false,
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
},
|
||||
space1: whitespace(),
|
||||
space2: whitespace(),
|
||||
value: FileValue {
|
||||
space0: whitespace(),
|
||||
filename: Filename {
|
||||
value: "hello.txt".to_string(),
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
},
|
||||
space1: whitespace(),
|
||||
space2: whitespace(),
|
||||
content_type: None,
|
||||
},
|
||||
line_terminator0: line_terminator,
|
||||
},
|
||||
"tests".to_string()
|
||||
)
|
||||
.unwrap(),
|
||||
http::FileParam {
|
||||
name: "upload1".to_string(),
|
||||
filename: "hello.txt".to_string(),
|
||||
data: b"Hello World!".to_vec(),
|
||||
content_type: "text/plain".to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_file_value_content_type() {
|
||||
assert_eq!(
|
||||
file_value_content_type(FileValue {
|
||||
space0: whitespace(),
|
||||
filename: Filename {
|
||||
value: "hello.txt".to_string(),
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
},
|
||||
space1: whitespace(),
|
||||
space2: whitespace(),
|
||||
content_type: None,
|
||||
}),
|
||||
"text/plain".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
file_value_content_type(FileValue {
|
||||
space0: whitespace(),
|
||||
filename: Filename {
|
||||
value: "hello.html".to_string(),
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
},
|
||||
space1: whitespace(),
|
||||
space2: whitespace(),
|
||||
content_type: None,
|
||||
}),
|
||||
"text/html".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
file_value_content_type(FileValue {
|
||||
space0: whitespace(),
|
||||
filename: Filename {
|
||||
value: "hello.txt".to_string(),
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
},
|
||||
space1: whitespace(),
|
||||
space2: whitespace(),
|
||||
content_type: Some("text/html".to_string()),
|
||||
}),
|
||||
"text/html".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
file_value_content_type(FileValue {
|
||||
space0: whitespace(),
|
||||
filename: Filename {
|
||||
value: "hello".to_string(),
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
},
|
||||
space1: whitespace(),
|
||||
space2: whitespace(),
|
||||
content_type: None,
|
||||
}),
|
||||
"application/octet-stream".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ use std::collections::HashMap;
|
|||
|
||||
use regex::Regex;
|
||||
|
||||
use crate::ast::*;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::*;
|
||||
use super::core::{Error, RunnerError};
|
||||
|
|
@ -19,9 +19,9 @@
|
|||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
use crate::jsonpath;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::cookie;
|
||||
use super::core::{Error, RunnerError};
|
||||
|
|
@ -262,7 +262,7 @@ impl Value {
|
|||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{Pos, SourceInfo};
|
||||
use hurl_core::ast::{Pos, SourceInfo};
|
||||
|
||||
pub fn xpath_invalid_query() -> Query {
|
||||
// xpath ???
|
||||
|
|
@ -23,13 +23,14 @@ use std::collections::HashMap;
|
|||
#[allow(unused)]
|
||||
use std::io::prelude::*;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::body::eval_body;
|
||||
use super::core::Error;
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
use crate::runner::multipart::eval_multipart_param;
|
||||
|
||||
pub fn eval_request(
|
||||
request: Request,
|
||||
|
|
@ -84,7 +85,7 @@ pub fn eval_request(
|
|||
|
||||
let mut multipart = vec![];
|
||||
for multipart_param in request.clone().multipart_form_data() {
|
||||
let param = multipart_param.eval(variables, context_dir.clone())?;
|
||||
let param = eval_multipart_param(multipart_param, variables, context_dir.clone())?;
|
||||
multipart.push(param);
|
||||
}
|
||||
|
||||
|
|
@ -184,7 +185,7 @@ fn eval_method(method: Method) -> http::Method {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ast::SourceInfo;
|
||||
use hurl_core::ast::SourceInfo;
|
||||
|
||||
use super::super::core::RunnerError;
|
||||
use super::*;
|
||||
|
|
@ -17,8 +17,8 @@
|
|||
*/
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::assert::eval_assert;
|
||||
use super::body::eval_body;
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ast::*;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::value::Value;
|
||||
|
|
@ -79,7 +79,7 @@ impl Value {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ast::SourceInfo;
|
||||
use hurl_core::ast::SourceInfo;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
Hello World!
|
||||
|
|
@ -1,23 +1,9 @@
|
|||
use curl::easy::Easy;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
use hurl::http::*;
|
||||
|
||||
use server::Server;
|
||||
|
||||
macro_rules! t {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(e) => e,
|
||||
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod server;
|
||||
|
||||
pub fn new_header(name: &str, value: &str) -> Header {
|
||||
Header {
|
||||
name: name.to_string(),
|
||||
|
|
@ -25,35 +11,6 @@ pub fn new_header(name: &str, value: &str) -> Header {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_easy() {
|
||||
let s = Server::new();
|
||||
s.receive(
|
||||
"\
|
||||
GET /hello HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:$PORT\r\n\
|
||||
Accept: */*\r\n\
|
||||
\r\n",
|
||||
);
|
||||
s.send("HTTP/1.1 200 OK\r\n\r\nHello World!");
|
||||
|
||||
let mut data = Vec::new();
|
||||
let mut handle = Easy::new();
|
||||
|
||||
handle.url(&s.url("/hello")).unwrap();
|
||||
{
|
||||
let mut transfer = handle.transfer();
|
||||
transfer
|
||||
.write_function(|new_data| {
|
||||
data.extend_from_slice(new_data);
|
||||
Ok(new_data.len())
|
||||
})
|
||||
.unwrap();
|
||||
transfer.perform().unwrap();
|
||||
}
|
||||
assert_eq!(data, b"Hello World!");
|
||||
}
|
||||
|
||||
fn default_client() -> Client {
|
||||
let options = ClientOptions {
|
||||
follow_location: false,
|
||||
|
|
@ -17,10 +17,11 @@
|
|||
*/
|
||||
extern crate hurl;
|
||||
|
||||
use hurl::ast::*;
|
||||
use hurl::http;
|
||||
use hurl::runner;
|
||||
use hurl::runner::RunnerOptions;
|
||||
use hurl_core::ast::*;
|
||||
use hurl_core::parser;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn log_verbose(message: &str) {
|
||||
|
|
@ -38,10 +39,10 @@ pub fn log_runner_error(error: &runner::Error, _warning: bool) {
|
|||
fn test_hurl_file() {
|
||||
//let filename = "integration/tests/post_json.hurl";
|
||||
//let filename = "integration/tests/error_assert_match_utf8.hurl";
|
||||
let filename = "integration/tests/error_template_variable_not_renderable.hurl";
|
||||
let filename = "../../integration/tests/error_template_variable_not_renderable.hurl";
|
||||
//let filename = "/mnt/secure/repos/work/myshop/integration/src/main/hurl-generated/pcm/pcm-jdd-open-up-150go.hurl";
|
||||
let content = std::fs::read_to_string(filename).expect("Something went wrong reading the file");
|
||||
let hurl_file = hurl::parser::parse_hurl_file(content.as_str()).unwrap();
|
||||
let hurl_file = parser::parse_hurl_file(content.as_str()).unwrap();
|
||||
let variables = HashMap::new();
|
||||
let options = http::ClientOptions {
|
||||
follow_location: false,
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
[package]
|
||||
name = "hurl_core"
|
||||
version = "0.99.14"
|
||||
authors = ["Fabrice Reix <fabrice.reix@orange.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
[features]
|
||||
# Treat warnings as a build error.
|
||||
strict = []
|
||||
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
structopt = "0.2.10"
|
||||
libxml = "0.2.12"
|
||||
regex = "1.1.0"
|
||||
serde_json = "1.0.40"
|
||||
xmlparser = "0.10.0"
|
||||
roxmltree = "0.7.1"
|
||||
serde-xml-rs = "0.3.1"
|
||||
atty = "0.2.13"
|
||||
url = "2.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
serde = "1.0.104"
|
||||
base64 = "0.11.0"
|
||||
float-cmp = "0.6.0"
|
||||
encoding = "0.2"
|
||||
chrono = "0.4.11"
|
||||
curl = "0.4.33"
|
||||
brotli="3.3.0"
|
||||
libflate = "1.0.2"
|
||||
|
||||
|
||||
#[dev-dependencies]
|
||||
proptest = "0.9.4"
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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 super::core::Template;
|
||||
|
||||
///
|
||||
/// This the AST for the JSON used within hurl
|
||||
///
|
||||
/// It is a superset of the standard json spec.
|
||||
/// Strings have been replaced by hurl template.
|
||||
///
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Value {
|
||||
Number(String),
|
||||
String(Template),
|
||||
Boolean(bool),
|
||||
List {
|
||||
space0: String,
|
||||
elements: Vec<ListElement>,
|
||||
},
|
||||
Object {
|
||||
space0: String,
|
||||
elements: Vec<ObjectElement>,
|
||||
},
|
||||
Null {},
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn _type(&self) -> String {
|
||||
match self {
|
||||
Value::Number(_) => "number".to_string(),
|
||||
Value::Null {} => "null".to_string(),
|
||||
Value::Boolean(_) => "boolean".to_string(),
|
||||
Value::List { .. } => "list".to_string(),
|
||||
Value::Object { .. } => "object".to_string(),
|
||||
Value::String(_) => "string".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ListElement {
|
||||
pub space0: String,
|
||||
pub value: Value,
|
||||
pub space1: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ObjectElement {
|
||||
pub space0: String,
|
||||
pub name: String,
|
||||
pub space1: String,
|
||||
pub space2: String,
|
||||
pub value: Value,
|
||||
pub space3: String,
|
||||
}
|
||||
|
|
@ -21,8 +21,5 @@ pub use self::json::ListElement as JsonListElement;
|
|||
pub use self::json::ObjectElement as JsonObjectElement;
|
||||
pub use self::json::Value as JsonValue;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use self::json::tests::*;
|
||||
|
||||
mod core;
|
||||
mod json;
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
#![cfg_attr(feature = "strict", deny(warnings))]
|
||||
|
||||
extern crate float_cmp;
|
||||
|
||||
pub mod ast;
|
||||
pub mod parser;
|
||||
|
|
@ -425,6 +425,38 @@ mod tests {
|
|||
assert_eq!(error.recoverable, true);
|
||||
}
|
||||
|
||||
pub fn json_hello_world_value() -> JsonValue {
|
||||
// "hello\u0020{{name}}!"
|
||||
JsonValue::String(Template {
|
||||
quotes: true,
|
||||
elements: vec![
|
||||
TemplateElement::String {
|
||||
value: "Hello ".to_string(),
|
||||
encoded: "Hello\\u0020".to_string(),
|
||||
},
|
||||
TemplateElement::Expression(Expr {
|
||||
space0: Whitespace {
|
||||
value: "".to_string(),
|
||||
source_info: SourceInfo::init(1, 15, 1, 15),
|
||||
},
|
||||
variable: Variable {
|
||||
name: "name".to_string(),
|
||||
source_info: SourceInfo::init(1, 15, 1, 19),
|
||||
},
|
||||
space1: Whitespace {
|
||||
value: "".to_string(),
|
||||
source_info: SourceInfo::init(1, 19, 1, 19),
|
||||
},
|
||||
}),
|
||||
TemplateElement::String {
|
||||
value: "!".to_string(),
|
||||
encoded: "!".to_string(),
|
||||
},
|
||||
],
|
||||
source_info: SourceInfo::init(1, 2, 1, 22),
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_value() {
|
||||
let mut reader = Reader::init("\"\"");
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
[package]
|
||||
name = "hurlfmt"
|
||||
version = "0.99.14"
|
||||
authors = ["Fabrice Reix <fabrice.reix@orange.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
[features]
|
||||
# Treat warnings as a build error.
|
||||
strict = []
|
||||
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
structopt = "0.2.10"
|
||||
libxml = "0.2.12"
|
||||
regex = "1.1.0"
|
||||
serde_json = "1.0.40"
|
||||
xmlparser = "0.10.0"
|
||||
roxmltree = "0.7.1"
|
||||
serde-xml-rs = "0.3.1"
|
||||
atty = "0.2.13"
|
||||
url = "2.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
serde = "1.0.104"
|
||||
base64 = "0.11.0"
|
||||
float-cmp = "0.6.0"
|
||||
encoding = "0.2"
|
||||
chrono = "0.4.11"
|
||||
curl = "0.4.33"
|
||||
brotli="3.3.0"
|
||||
libflate = "1.0.2"
|
||||
|
||||
|
||||
[dependencies.hurl_core]
|
||||
path = "../hurl_core"
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "0.9.4"
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
use hurl_core::ast::SourceInfo;
|
||||
use hurl_core::parser;
|
||||
use hurl_core::parser::ParseError;
|
||||
|
||||
use crate::linter;
|
||||
use crate::linter::LinterError;
|
||||
|
||||
pub trait Error {
|
||||
fn source_info(&self) -> SourceInfo;
|
||||
fn description(&self) -> String;
|
||||
fn fixme(&self) -> String;
|
||||
}
|
||||
|
||||
///
|
||||
/// Textual Output for parser errors
|
||||
///
|
||||
|
||||
impl Error for parser::Error {
|
||||
fn source_info(&self) -> SourceInfo {
|
||||
SourceInfo {
|
||||
start: self.pos.clone(),
|
||||
end: self.pos.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
match self.clone().inner {
|
||||
ParseError::Method { .. } => "Parsing Method".to_string(),
|
||||
ParseError::Version { .. } => "Parsing Version".to_string(),
|
||||
ParseError::Status { .. } => "Parsing Status".to_string(),
|
||||
ParseError::Filename { .. } => "Parsing Filename".to_string(),
|
||||
ParseError::Expecting { .. } => "Parsing literal".to_string(),
|
||||
ParseError::Space { .. } => "Parsing space".to_string(),
|
||||
ParseError::SectionName { .. } => "Parsing section name".to_string(),
|
||||
ParseError::JsonpathExpr { .. } => "Parsing jsonpath expression".to_string(),
|
||||
ParseError::XPathExpr { .. } => "Parsing xpath expression".to_string(),
|
||||
ParseError::TemplateVariable { .. } => "Parsing template variable".to_string(),
|
||||
ParseError::Json { .. } => "Parsing json".to_string(),
|
||||
ParseError::Predicate { .. } => "Parsing predicate".to_string(),
|
||||
ParseError::PredicateValue { .. } => "Parsing predicate value".to_string(),
|
||||
ParseError::RegexExpr { .. } => "Parsing regex".to_string(),
|
||||
ParseError::DuplicateSection { .. } => "Parsing section".to_string(),
|
||||
ParseError::RequestSection { .. } => "Parsing section".to_string(),
|
||||
ParseError::ResponseSection { .. } => "Parsing section".to_string(),
|
||||
ParseError::EscapeChar { .. } => "Parsing escape character".to_string(),
|
||||
ParseError::InvalidCookieAttribute { .. } => "Parsing cookie attribute".to_string(),
|
||||
_ => format!("{:?}", self),
|
||||
}
|
||||
}
|
||||
|
||||
fn fixme(&self) -> String {
|
||||
match self.inner.clone() {
|
||||
ParseError::Method { .. } => "Available HTTP Method GET, POST, ...".to_string(),
|
||||
ParseError::Version { .. } => "The http version must be 1.0, 1.1, 2 or *".to_string(),
|
||||
ParseError::Status { .. } => "The http status is not valid".to_string(),
|
||||
ParseError::Filename { .. } => "expecting a filename".to_string(),
|
||||
ParseError::Expecting { value } => format!("expecting '{}'", value),
|
||||
ParseError::Space { .. } => "expecting a space".to_string(),
|
||||
ParseError::SectionName { name } => format!("the section {} is not valid", name),
|
||||
ParseError::JsonpathExpr { .. } => "expecting a jsonpath expression".to_string(),
|
||||
ParseError::XPathExpr { .. } => "expecting a xpath expression".to_string(),
|
||||
ParseError::TemplateVariable { .. } => "expecting a variable".to_string(),
|
||||
ParseError::Json { .. } => "json error".to_string(),
|
||||
ParseError::Predicate { .. } => "expecting a predicate".to_string(),
|
||||
ParseError::PredicateValue { .. } => "invalid predicate value".to_string(),
|
||||
ParseError::RegexExpr { .. } => "Invalid Regex expression".to_string(),
|
||||
ParseError::DuplicateSection { .. } => "The section is already defined".to_string(),
|
||||
ParseError::RequestSection { .. } => {
|
||||
"This is not a valid section for a request".to_string()
|
||||
}
|
||||
ParseError::ResponseSection { .. } => {
|
||||
"This is not a valid section for a response".to_string()
|
||||
}
|
||||
ParseError::EscapeChar { .. } => "The escaping sequence is not valid".to_string(),
|
||||
ParseError::InvalidCookieAttribute { .. } => {
|
||||
"The cookie attribute is not valid".to_string()
|
||||
}
|
||||
_ => format!("{:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Textual Output for linter errors
|
||||
///
|
||||
///
|
||||
impl Error for linter::Error {
|
||||
fn source_info(&self) -> SourceInfo {
|
||||
self.clone().source_info
|
||||
}
|
||||
|
||||
fn description(&self) -> String {
|
||||
match self.inner {
|
||||
LinterError::UnneccessarySpace { .. } => "Unnecessary space".to_string(),
|
||||
LinterError::UnneccessaryJsonEncoding {} => "Unnecessary json encoding".to_string(),
|
||||
LinterError::OneSpace {} => "One space ".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn fixme(&self) -> String {
|
||||
match self.inner {
|
||||
LinterError::UnneccessarySpace { .. } => "Remove space".to_string(),
|
||||
LinterError::UnneccessaryJsonEncoding {} => "Use Simple String".to_string(),
|
||||
LinterError::OneSpace {} => "Use only one space".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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 super::error::Error;
|
||||
use crate::format::TerminalColor;
|
||||
use crate::linter;
|
||||
use hurl_core::parser;
|
||||
|
||||
pub fn make_logger_verbose(verbose: bool) -> impl Fn(&str) {
|
||||
move |message| log_verbose(verbose, message)
|
||||
}
|
||||
|
||||
pub fn make_logger_error_message(color: bool) -> impl Fn(bool, &str) {
|
||||
move |warning, message| log_error_message(color, warning, message)
|
||||
}
|
||||
|
||||
pub fn make_logger_parser_error(
|
||||
lines: Vec<String>,
|
||||
color: bool,
|
||||
filename: Option<String>,
|
||||
) -> impl Fn(&parser::Error, bool) {
|
||||
move |error: &parser::Error, warning: bool| {
|
||||
log_error(lines.clone(), color, filename.clone(), error, warning)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_logger_linter_error(
|
||||
lines: Vec<String>,
|
||||
color: bool,
|
||||
filename: Option<String>,
|
||||
) -> impl Fn(&linter::Error, bool) {
|
||||
move |error: &linter::Error, warning: bool| {
|
||||
log_error(lines.clone(), color, filename.clone(), error, warning)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_info(message: &str) {
|
||||
eprintln!("{}", message);
|
||||
}
|
||||
|
||||
fn log_error_message(color: bool, warning: bool, message: &str) {
|
||||
let log_type = match (color, warning) {
|
||||
(false, false) => "warning".to_string(),
|
||||
(false, true) => "error".to_string(),
|
||||
(true, false) => TerminalColor::Red.format("error".to_string()),
|
||||
(true, true) => TerminalColor::Yellow.format("warning".to_string()),
|
||||
};
|
||||
eprintln!("{}: {}", log_type, message);
|
||||
}
|
||||
|
||||
fn log_verbose(verbose: bool, message: &str) {
|
||||
if verbose {
|
||||
eprintln!("* {}", message);
|
||||
}
|
||||
}
|
||||
|
||||
fn log_error(
|
||||
lines: Vec<String>,
|
||||
color: bool,
|
||||
filename: Option<String>,
|
||||
error: &dyn Error,
|
||||
warning: bool,
|
||||
) {
|
||||
let line_number_size = if lines.len() < 100 {
|
||||
2
|
||||
} else if lines.len() < 1000 {
|
||||
3
|
||||
} else {
|
||||
4
|
||||
};
|
||||
|
||||
let error_type = if warning {
|
||||
String::from("warning")
|
||||
} else {
|
||||
String::from("error")
|
||||
};
|
||||
let error_type = if !color {
|
||||
error_type
|
||||
} else if warning {
|
||||
TerminalColor::Yellow.format(error_type)
|
||||
} else {
|
||||
TerminalColor::Red.format(error_type)
|
||||
};
|
||||
eprintln!("{}: {}", error_type, error.description());
|
||||
|
||||
if let Some(filename) = filename {
|
||||
eprintln!(
|
||||
"{}--> {}:{}:{}",
|
||||
" ".repeat(line_number_size).as_str(),
|
||||
filename,
|
||||
error.source_info().start.line,
|
||||
error.source_info().start.column,
|
||||
);
|
||||
}
|
||||
eprintln!("{} |", " ".repeat(line_number_size));
|
||||
|
||||
let line = lines.get(error.source_info().start.line - 1).unwrap();
|
||||
let line = str::replace(line, "\t", " "); // replace all your tabs with 4 characters
|
||||
eprintln!(
|
||||
"{line_number:>width$} |{line}",
|
||||
line_number = error.source_info().start.line,
|
||||
width = line_number_size,
|
||||
line = if line.is_empty() {
|
||||
line
|
||||
} else {
|
||||
format!(" {}", line)
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: to clean/Refacto
|
||||
// specific case for assert errors
|
||||
if error.source_info().start.column == 0 {
|
||||
let fix_me = &error.fixme();
|
||||
let fixme_lines: Vec<&str> = regex::Regex::new(r"\n|\r\n")
|
||||
.unwrap()
|
||||
.split(fix_me)
|
||||
.collect();
|
||||
// edd an empty line at the end?
|
||||
for line in fixme_lines {
|
||||
eprintln!(
|
||||
"{} | {}",
|
||||
" ".repeat(line_number_size).as_str(),
|
||||
fixme = line,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let line = lines.get(error.source_info().start.line - 1).unwrap();
|
||||
let width = (error.source_info().end.column - error.source_info().start.column) as usize;
|
||||
|
||||
let mut tab_shift = 0;
|
||||
for (i, c) in line.chars().enumerate() {
|
||||
if i >= error.source_info().start.column - 1 {
|
||||
break;
|
||||
};
|
||||
if c == '\t' {
|
||||
tab_shift += 1;
|
||||
}
|
||||
}
|
||||
eprintln!(
|
||||
"{} | {}{} {fixme}",
|
||||
" ".repeat(line_number_size).as_str(),
|
||||
" ".repeat(error.source_info().start.column - 1 + tab_shift * 3),
|
||||
"^".repeat(if width > 1 { width } else { 1 }),
|
||||
fixme = error.fixme().as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
eprintln!("{} |\n", " ".repeat(line_number_size));
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
pub use self::error::Error;
|
||||
pub use self::logger::{
|
||||
log_info, make_logger_error_message, make_logger_linter_error, make_logger_parser_error,
|
||||
make_logger_runner_error, make_logger_verbose,
|
||||
make_logger_verbose,
|
||||
};
|
||||
pub use self::options::cookies_output_file;
|
||||
pub use self::options::CLIError;
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
pub struct CLIError {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
pub fn cookies_output_file(filename: String, n: usize) -> Result<std::path::PathBuf, CLIError> {
|
||||
if n > 1 {
|
||||
Err(CLIError {
|
||||
message: "Only save cookies for a unique session".to_string(),
|
||||
})
|
||||
} else {
|
||||
let path = std::path::Path::new(&filename);
|
||||
Ok(path.to_path_buf())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
pub enum TerminalColor {
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
LightGray,
|
||||
LightBlack,
|
||||
LightRed,
|
||||
LightGreen,
|
||||
LightYellow,
|
||||
LightBlue,
|
||||
LightMagenta,
|
||||
LightCyan,
|
||||
LightWhite,
|
||||
}
|
||||
|
||||
impl TerminalColor {
|
||||
pub fn format(self, v: String) -> String {
|
||||
match self {
|
||||
TerminalColor::Black => format!("\x1b[0;30m{}\x1b[0m", v),
|
||||
TerminalColor::Red => format!("\x1b[1;31m{}\x1b[0m", v),
|
||||
TerminalColor::Green => format!("\x1b[0;32m{}\x1b[0m", v),
|
||||
TerminalColor::Yellow => format!("\x1b[0;33m{}\x1b[0m", v),
|
||||
TerminalColor::Blue => format!("\x1b[0;34m{}\x1b[0m", v),
|
||||
TerminalColor::Magenta => format!("\x1b[0;35m{}\x1b[0m", v),
|
||||
TerminalColor::Cyan => format!("\x1b[0;36m{}\x1b[0m", v),
|
||||
TerminalColor::LightGray => format!("\x1b[0;37m{}\x1b[0m", v),
|
||||
TerminalColor::LightBlack => format!("\x1b[0;90m{}\x1b[0m", v),
|
||||
TerminalColor::LightRed => format!("\x1b[0;91m{}\x1b[0m", v),
|
||||
TerminalColor::LightGreen => format!("\x1b[0;92m{}\x1b[0m", v),
|
||||
TerminalColor::LightYellow => format!("\x1b[0;93m{}\x1b[0m", v),
|
||||
TerminalColor::LightBlue => format!("\x1b[0;94m{}\x1b[0m", v),
|
||||
TerminalColor::LightMagenta => format!("\x1b[0;95m{}\x1b[0m", v),
|
||||
TerminalColor::LightCyan => format!("\x1b[0;96m{}\x1b[0m", v),
|
||||
TerminalColor::LightWhite => format!("\x1b[0;97m{}\x1b[0m", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,571 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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 hurl_core::ast::*;
|
||||
|
||||
pub trait Htmlable {
|
||||
fn to_html(&self) -> String;
|
||||
}
|
||||
|
||||
pub fn format_standalone(hurl_file: HurlFile) -> String {
|
||||
let css = include_str!("hurl.css");
|
||||
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str("<!DOCTYPE html>\n");
|
||||
buffer.push_str("<html>");
|
||||
buffer.push_str("<head>");
|
||||
buffer.push_str("<title>Hurl File</title>");
|
||||
buffer.push_str("<style>\n");
|
||||
buffer.push_str(css);
|
||||
buffer.push_str("</style>");
|
||||
buffer.push_str("</head>");
|
||||
buffer.push_str("<body>\n");
|
||||
buffer.push_str(hurl_file.to_html().as_str());
|
||||
buffer.push_str("\n</body>");
|
||||
buffer.push_str("</html>");
|
||||
buffer
|
||||
}
|
||||
|
||||
pub fn format(hurl_file: HurlFile, standalone: bool) -> String {
|
||||
if standalone {
|
||||
format_standalone(hurl_file)
|
||||
} else {
|
||||
hurl_file.to_html()
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for HurlFile {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str("<div class=\"hurl-file\">");
|
||||
for entry in self.clone().entries {
|
||||
buffer.push_str(entry.to_html().as_str());
|
||||
}
|
||||
for line_terminator in self.line_terminators.clone() {
|
||||
buffer.push_str(line_terminator.to_html().as_str());
|
||||
}
|
||||
buffer.push_str("</div>");
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Entry {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str("<div class=\"hurl-entry\">");
|
||||
buffer.push_str(self.request.to_html().as_str());
|
||||
if let Some(response) = self.clone().response {
|
||||
buffer.push_str(response.to_html().as_str());
|
||||
}
|
||||
buffer.push_str("</div>");
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Request {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str("<div class=\"request\">");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str(self.method.to_html().as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str(self.url.to_html().as_str());
|
||||
buffer.push_str(self.line_terminator0.to_html().as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer.push_str("</div>");
|
||||
for header in self.headers.clone() {
|
||||
buffer.push_str(header.to_html().as_str());
|
||||
}
|
||||
for section in self.sections.clone() {
|
||||
buffer.push_str(section.to_html().as_str());
|
||||
}
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Response {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str("<div class=\"response\">");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str(self.version.to_html().as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str(self.status.to_html().as_str());
|
||||
buffer.push_str("</span>");
|
||||
for section in self.sections.clone() {
|
||||
buffer.push_str(section.to_html().as_str());
|
||||
}
|
||||
buffer.push_str("</div>");
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Method {
|
||||
fn to_html(&self) -> String {
|
||||
return format!("<span class=\"method\">{}</span>", self.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Version {
|
||||
fn to_html(&self) -> String {
|
||||
return format!(
|
||||
"<span class=\"version\">HTTP/{}</span>",
|
||||
self.value.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Status {
|
||||
fn to_html(&self) -> String {
|
||||
format!("<span class=\"status\">{}</span>", self.value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Section {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
|
||||
buffer
|
||||
.push_str(format!("<span class=\"section-header\">[{}]</span>", self.name()).as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer.push_str(self.value.to_html().as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for SectionValue {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
match self {
|
||||
SectionValue::Asserts(items) => {
|
||||
for item in items {
|
||||
buffer.push_str(item.to_html().as_str())
|
||||
}
|
||||
}
|
||||
SectionValue::QueryParams(items) => {
|
||||
for item in items {
|
||||
buffer.push_str(item.to_html().as_str())
|
||||
}
|
||||
}
|
||||
SectionValue::FormParams(items) => {
|
||||
for item in items {
|
||||
buffer.push_str(item.to_html().as_str())
|
||||
}
|
||||
}
|
||||
SectionValue::MultipartFormData(items) => {
|
||||
for item in items {
|
||||
buffer.push_str(item.to_html().as_str())
|
||||
}
|
||||
}
|
||||
SectionValue::Cookies(items) => {
|
||||
for item in items {
|
||||
buffer.push_str(item.to_html().as_str())
|
||||
}
|
||||
}
|
||||
SectionValue::Captures(items) => {
|
||||
for item in items {
|
||||
buffer.push_str(item.to_html().as_str())
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for KeyValue {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str(self.key.to_html().as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str("<span>:</span>");
|
||||
buffer.push_str(self.space2.to_html().as_str());
|
||||
buffer.push_str(self.value.to_html().as_str());
|
||||
buffer.push_str(self.line_terminator0.to_html().as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for MultipartParam {
|
||||
fn to_html(&self) -> String {
|
||||
match self {
|
||||
MultipartParam::Param(keyvalue) => keyvalue.to_html(),
|
||||
MultipartParam::FileParam(file_param) => file_param.to_html(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for FileParam {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str(self.key.to_html().as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str("<span>:</span>");
|
||||
buffer.push_str(self.space2.to_html().as_str());
|
||||
buffer.push_str(self.value.to_html().as_str());
|
||||
buffer.push_str(self.line_terminator0.to_html().as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for FileValue {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Cookie {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str(self.name.value.as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str("<span>:</span>");
|
||||
buffer.push_str(self.space2.to_html().as_str());
|
||||
buffer.push_str(self.value.to_html().as_str());
|
||||
buffer.push_str(self.line_terminator0.to_html().as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for CookieValue {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str(self.value.as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Capture {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str(self.name.value.as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str("<span>:</span>");
|
||||
buffer.push_str(self.space2.to_html().as_str());
|
||||
buffer.push_str(self.query.to_html().as_str());
|
||||
buffer.push_str(self.line_terminator0.to_html().as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Query {
|
||||
fn to_html(&self) -> String {
|
||||
self.value.to_html()
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for QueryValue {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
match self {
|
||||
QueryValue::Status {} => {
|
||||
buffer.push_str("<span class=\"query-type\">status</span>");
|
||||
}
|
||||
QueryValue::Header { space0, name } => {
|
||||
buffer.push_str("<span class=\"query-type\">header</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(name.to_html().as_str());
|
||||
}
|
||||
QueryValue::Cookie { space0, expr } => {
|
||||
buffer.push_str("<span class=\"query-type\">cookie</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(expr.to_html().as_str());
|
||||
}
|
||||
QueryValue::Body {} => {
|
||||
buffer.push_str("<span class=\"query-type\">status</span>");
|
||||
}
|
||||
QueryValue::Xpath { space0, expr } => {
|
||||
buffer.push_str("<span class=\"query-type\">xpath</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(expr.to_html().as_str());
|
||||
}
|
||||
QueryValue::Jsonpath { space0, expr } => {
|
||||
buffer.push_str("<span class=\"query-type\">jsonpath</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(expr.to_html().as_str());
|
||||
}
|
||||
QueryValue::Regex { space0, expr } => {
|
||||
buffer.push_str("<span class=\"query-type\">regex</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(expr.to_html().as_str());
|
||||
}
|
||||
QueryValue::Variable { space0, name } => {
|
||||
buffer.push_str("<span class=\"query-type\">variable</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(name.to_html().as_str());
|
||||
}
|
||||
}
|
||||
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for CookiePath {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str(self.name.to_html().as_str());
|
||||
if let Some(attribute) = self.attribute.clone() {
|
||||
buffer.push_str(attribute.to_html().as_str());
|
||||
}
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for CookieAttribute {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str(self.name.value().as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Assert {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str(self.query.to_html().as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str(self.predicate.to_html().as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer.push_str(self.line_terminator0.to_html().as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Predicate {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
if self.not {
|
||||
buffer.push_str("not");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
}
|
||||
buffer.push_str(self.predicate_func.to_html().as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for PredicateFunc {
|
||||
fn to_html(&self) -> String {
|
||||
self.value.to_html()
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for PredicateFuncValue {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
match self {
|
||||
PredicateFuncValue::CountEqual { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">equals</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(format!("<span class=\"number\">{}</span>", value).as_str());
|
||||
}
|
||||
PredicateFuncValue::EqualString { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">equals</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"number\">{}</span>", value.to_html()).as_str(),
|
||||
);
|
||||
}
|
||||
PredicateFuncValue::EqualInt { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">equals</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(format!("<span class=\"number\">{}</span>", value).as_str());
|
||||
}
|
||||
PredicateFuncValue::EqualFloat { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">equals</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"number\">{}</span>", value.to_string()).as_str(),
|
||||
);
|
||||
}
|
||||
PredicateFuncValue::EqualExpression { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">equals</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(value.to_html().as_str());
|
||||
}
|
||||
PredicateFuncValue::StartWith { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">startsWith</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"string\">{}</span>", value.to_html()).as_str(),
|
||||
);
|
||||
}
|
||||
PredicateFuncValue::Contain { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">contains</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"string\">{}</span>", value.to_html()).as_str(),
|
||||
);
|
||||
}
|
||||
PredicateFuncValue::IncludeString { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">includes</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"string\">{}</span>", value.to_html()).as_str(),
|
||||
);
|
||||
}
|
||||
PredicateFuncValue::IncludeInt { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">includes</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(format!("<span class=\"number\">{}</span>", value).as_str());
|
||||
}
|
||||
PredicateFuncValue::IncludeNull { space0 } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">includes</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str("<span class=\"null\">null</span>");
|
||||
}
|
||||
PredicateFuncValue::IncludeBool { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">includes</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(format!("<span class=\"boolean\">{}</span>", value).as_str());
|
||||
}
|
||||
PredicateFuncValue::IncludeFloat { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">includes</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"number\">{}</span>", value.to_string()).as_str(),
|
||||
);
|
||||
}
|
||||
PredicateFuncValue::IncludeExpression { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">includes</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(value.to_html().as_str());
|
||||
}
|
||||
PredicateFuncValue::Match { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">matches</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"string\">{}</span>", value.to_html()).as_str(),
|
||||
);
|
||||
}
|
||||
PredicateFuncValue::EqualNull { space0 } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">equals</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str("<span class=\"null\">null</span>");
|
||||
}
|
||||
PredicateFuncValue::EqualBool { space0, value } => {
|
||||
buffer.push_str("<span class=\"predicate-type\">equals</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(format!("<span class=\"boolean\">{}</span>", value).as_str());
|
||||
}
|
||||
PredicateFuncValue::Exist {} => {
|
||||
buffer.push_str("<span class=\"predicate-type\">exists</span>");
|
||||
}
|
||||
}
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Whitespace {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
let Whitespace { value, .. } = self;
|
||||
if !value.is_empty() {
|
||||
buffer.push_str(self.value.as_str());
|
||||
};
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for LineTerminator {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
if let Some(v) = self.clone().comment {
|
||||
buffer.push_str("<span class=\"comment\">");
|
||||
buffer.push_str(format!("#{}", v.value.as_str()).as_str());
|
||||
buffer.push_str("</span>");
|
||||
}
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for EncodedString {
|
||||
fn to_html(&self) -> String {
|
||||
format!("<span class=\"string\">\"{}\"</span>", self.encoded)
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Template {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
for element in self.elements.clone() {
|
||||
buffer.push_str(element.to_html().as_str());
|
||||
}
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for TemplateElement {
|
||||
fn to_html(&self) -> String {
|
||||
match self {
|
||||
TemplateElement::String { encoded, .. } => {
|
||||
format!("<span class=\"string\">{}</span>", encoded)
|
||||
}
|
||||
TemplateElement::Expression(value) => value.to_html(),
|
||||
/* space0: _, variable: _, space1: _ } => {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str("{{");
|
||||
buffer.push_str("}}");
|
||||
return buffer;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Expr {
|
||||
fn to_html(&self) -> String {
|
||||
format!("<span class=\"variable\">{}</span>", self.variable.name)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_line(v: String) -> String {
|
||||
format!("<span class=\"line\">{}</span>", v)
|
||||
}
|
||||
|
||||
fn add_line_terminators(buffer: &mut String, line_terminators: Vec<LineTerminator>) {
|
||||
for line_terminator in line_terminators.clone() {
|
||||
buffer.push_str(to_line(line_terminator.to_html()).as_str());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
dy {
|
||||
counter-reset: line;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
span.line {
|
||||
display: block;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
|
||||
span.line:before {
|
||||
counter-increment: line;
|
||||
content: counter(line);
|
||||
display: inline-block;
|
||||
border-right: 1px solid #ddd;
|
||||
padding: 0 1em;
|
||||
margin-right: .5em;
|
||||
color: #888;
|
||||
width: 2.5em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.method {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.version {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
color: darkmagenta;
|
||||
}
|
||||
|
||||
.query-type {
|
||||
color: teal;
|
||||
}
|
||||
|
||||
.predicate-type {
|
||||
color: darkblue;
|
||||
}
|
||||
|
||||
.string {
|
||||
color: darkgreen;
|
||||
}
|
||||
|
||||
.comment {
|
||||
color: dimgray;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
pub use self::color::TerminalColor;
|
||||
pub use self::html::format as format_html;
|
||||
pub use self::text::format as format_text;
|
||||
pub use self::token::{Token, Tokenizable};
|
||||
|
||||
mod color;
|
||||
mod html;
|
||||
mod text;
|
||||
mod token;
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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 hurl_core::ast::*;
|
||||
|
||||
use super::color::TerminalColor;
|
||||
use super::token::*;
|
||||
|
||||
pub fn format(hurl_file: HurlFile, color: bool) -> String {
|
||||
let mut buffer = String::from("");
|
||||
for token in hurl_file.tokenize() {
|
||||
buffer.push_str(format_token(token, color).as_str());
|
||||
}
|
||||
buffer
|
||||
}
|
||||
|
||||
fn format_token(token: Token, color: bool) -> String {
|
||||
match token {
|
||||
Token::Whitespace(value) => value,
|
||||
Token::Method(value) => {
|
||||
if color {
|
||||
TerminalColor::LightYellow.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::Version(value) => value,
|
||||
Token::Status(value) => value,
|
||||
Token::SectionHeader(value) => {
|
||||
if color {
|
||||
TerminalColor::Magenta.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::Comment(value) => {
|
||||
if color {
|
||||
TerminalColor::LightBlack.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::Value(value) => value,
|
||||
Token::Colon(value) => value,
|
||||
Token::QueryType(value) => {
|
||||
if color {
|
||||
TerminalColor::LightCyan.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::PredicateType(value) => {
|
||||
if color {
|
||||
TerminalColor::LightYellow.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::Not(value) => {
|
||||
if color {
|
||||
TerminalColor::LightYellow.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::Boolean(value) | Token::Number(value) => {
|
||||
if color {
|
||||
TerminalColor::Cyan.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::String(value) => {
|
||||
if color {
|
||||
TerminalColor::Green.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::Quote(value) => {
|
||||
if color {
|
||||
TerminalColor::Green.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::CodeDelimiter(value) => {
|
||||
if color {
|
||||
TerminalColor::Green.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::CodeVariable(value) => {
|
||||
if color {
|
||||
TerminalColor::Green.format(value)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Token::Keyword(value) => value,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,785 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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 hurl_core::ast::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Token {
|
||||
Method(String),
|
||||
Version(String),
|
||||
Status(String),
|
||||
SectionHeader(String),
|
||||
QueryType(String),
|
||||
PredicateType(String),
|
||||
Not(String),
|
||||
Keyword(String),
|
||||
|
||||
// Primitives
|
||||
Whitespace(String),
|
||||
Comment(String),
|
||||
Value(String),
|
||||
Colon(String),
|
||||
Quote(String),
|
||||
Boolean(String),
|
||||
Number(String),
|
||||
String(String),
|
||||
CodeDelimiter(String),
|
||||
CodeVariable(String),
|
||||
}
|
||||
|
||||
pub trait Tokenizable {
|
||||
fn tokenize(&self) -> Vec<Token>;
|
||||
}
|
||||
|
||||
fn add_tokens(tokens1: &mut Vec<Token>, tokens2: Vec<Token>) {
|
||||
for token in tokens2 {
|
||||
tokens1.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for HurlFile {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.entries.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Entry {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(&mut tokens, self.request.tokenize());
|
||||
if let Some(response) = self.clone().response {
|
||||
add_tokens(&mut tokens, response.tokenize())
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Request {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
tokens.push(Token::Method(self.method.as_str().to_string()));
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
add_tokens(&mut tokens, self.url.tokenize());
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.headers.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.sections.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
if let Some(body) = self.clone().body {
|
||||
add_tokens(&mut tokens, body.tokenize())
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Response {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
add_tokens(&mut tokens, self.version.tokenize());
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
tokens.push(Token::Status(self.status.value.to_string()));
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.headers.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.sections.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
if let Some(body) = self.clone().body {
|
||||
add_tokens(&mut tokens, body.tokenize())
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Version {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::Version(format!(
|
||||
"HTTP/{}",
|
||||
match self.value {
|
||||
VersionValue::Version1 => String::from("1.0"),
|
||||
VersionValue::Version11 => String::from("1.1"),
|
||||
VersionValue::Version2 => String::from("2"),
|
||||
VersionValue::VersionAny => String::from("*"),
|
||||
}
|
||||
)));
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Body {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
add_tokens(&mut tokens, self.value.tokenize());
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Bytes {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
match self {
|
||||
Bytes::Json { value } => tokens.append(&mut value.tokenize()),
|
||||
Bytes::Xml { value } => {
|
||||
tokens.push(Token::String(value.to_string()));
|
||||
}
|
||||
// Bytes::MultilineString { value: _ } => {}
|
||||
Bytes::RawString { newline0, value } => {
|
||||
tokens.push(Token::Keyword(String::from("```")));
|
||||
add_tokens(&mut tokens, newline0.tokenize());
|
||||
tokens.append(&mut value.tokenize());
|
||||
tokens.push(Token::Keyword(String::from("```")));
|
||||
}
|
||||
Bytes::Base64 {
|
||||
space0,
|
||||
encoded,
|
||||
space1,
|
||||
..
|
||||
} => {
|
||||
tokens.push(Token::Keyword(String::from("base64,")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::String(encoded.to_string()));
|
||||
add_tokens(&mut tokens, space1.tokenize());
|
||||
tokens.push(Token::Keyword(String::from(";")));
|
||||
}
|
||||
Bytes::File {
|
||||
space0,
|
||||
filename,
|
||||
space1,
|
||||
} => {
|
||||
tokens.push(Token::Keyword(String::from("file,")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, filename.tokenize());
|
||||
//tokens.push(Token::String(filename.to_string()));
|
||||
add_tokens(&mut tokens, space1.tokenize());
|
||||
tokens.push(Token::Keyword(String::from(";")));
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Section {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
tokens.push(Token::SectionHeader(format!("[{}]", self.name())));
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
add_tokens(&mut tokens, self.value.tokenize());
|
||||
// add_tokens(&mut tokens, self.space0.tokenize());
|
||||
// tokens.push(Token::SectionHeader(format!("[{}]", self.name)));
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for SectionValue {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
match self {
|
||||
SectionValue::Asserts(items) => {
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
items.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
}
|
||||
SectionValue::QueryParams(items) => {
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
items.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
}
|
||||
SectionValue::FormParams(items) => {
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
items.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
}
|
||||
SectionValue::MultipartFormData(items) => {
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
items.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
}
|
||||
SectionValue::Cookies(items) => {
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
items.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
}
|
||||
SectionValue::Captures(items) => {
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
items.iter().flat_map(|e| e.tokenize()).collect(),
|
||||
);
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for KeyValue {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
add_tokens(&mut tokens, self.key.tokenize());
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
tokens.push(Token::Colon(String::from(":")));
|
||||
add_tokens(&mut tokens, self.space2.tokenize());
|
||||
add_tokens(&mut tokens, self.value.tokenize());
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for MultipartParam {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
match self {
|
||||
MultipartParam::Param(key_value) => key_value.tokenize(),
|
||||
MultipartParam::FileParam(file_param) => file_param.tokenize(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for FileParam {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
add_tokens(&mut tokens, self.key.tokenize());
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
tokens.push(Token::Colon(String::from(":")));
|
||||
add_tokens(&mut tokens, self.space2.tokenize());
|
||||
tokens.append(&mut self.value.tokenize());
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for FileValue {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::Keyword("file,".to_string()));
|
||||
tokens.append(&mut self.space0.tokenize());
|
||||
tokens.append(&mut self.filename.tokenize());
|
||||
tokens.append(&mut self.space1.tokenize());
|
||||
tokens.push(Token::Keyword(";".to_string()));
|
||||
tokens.append(&mut self.space2.tokenize());
|
||||
if let Some(content_type) = self.content_type.clone() {
|
||||
tokens.push(Token::String(content_type));
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Cookie {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
add_tokens(&mut tokens, self.name.tokenize());
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
tokens.push(Token::Colon(String::from(":")));
|
||||
add_tokens(&mut tokens, self.space2.tokenize());
|
||||
add_tokens(&mut tokens, self.value.tokenize());
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for CookieValue {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::Value(self.clone().value));
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Capture {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
add_tokens(&mut tokens, self.name.tokenize());
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
tokens.push(Token::Colon(String::from(":")));
|
||||
add_tokens(&mut tokens, self.space2.tokenize());
|
||||
add_tokens(&mut tokens, self.query.tokenize());
|
||||
add_tokens(&mut tokens, self.space3.tokenize());
|
||||
if let Some(subquery) = self.clone().subquery {
|
||||
add_tokens(&mut tokens, subquery.tokenize())
|
||||
}
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Assert {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(
|
||||
&mut tokens,
|
||||
self.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
add_tokens(&mut tokens, self.query.tokenize());
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
// TODO reconvert back your first predicate for jsonpath
|
||||
// so that you can use your firstX predicate for other query
|
||||
add_tokens(&mut tokens, self.predicate.tokenize());
|
||||
add_tokens(&mut tokens, self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Query {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
match self.value.clone() {
|
||||
QueryValue::Status {} => tokens.push(Token::QueryType(String::from("status"))),
|
||||
QueryValue::Header { space0, name } => {
|
||||
tokens.push(Token::QueryType(String::from("header")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, name.tokenize());
|
||||
}
|
||||
QueryValue::Cookie { space0, expr } => {
|
||||
tokens.push(Token::QueryType(String::from("cookie")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::CodeDelimiter("\"".to_string()));
|
||||
add_tokens(&mut tokens, expr.tokenize());
|
||||
tokens.push(Token::CodeDelimiter("\"".to_string()));
|
||||
}
|
||||
QueryValue::Body {} => tokens.push(Token::QueryType(String::from("body"))),
|
||||
QueryValue::Xpath { space0, expr } => {
|
||||
tokens.push(Token::QueryType(String::from("xpath")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, expr.tokenize());
|
||||
}
|
||||
QueryValue::Jsonpath { space0, expr } => {
|
||||
tokens.push(Token::QueryType(String::from("jsonpath")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, expr.tokenize());
|
||||
}
|
||||
QueryValue::Regex { space0, expr } => {
|
||||
tokens.push(Token::QueryType(String::from("regex")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, expr.tokenize());
|
||||
}
|
||||
QueryValue::Variable { space0, name } => {
|
||||
tokens.push(Token::QueryType(String::from("variable")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, name.tokenize());
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for CookiePath {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(&mut tokens, self.name.tokenize());
|
||||
if let Some(attribute) = self.attribute.clone() {
|
||||
add_tokens(&mut tokens, attribute.tokenize());
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for CookieAttribute {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::CodeDelimiter("[".to_string()));
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
tokens.push(Token::String(self.name.value()));
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
tokens.push(Token::CodeDelimiter("]".to_string()));
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Subquery {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
match self.value.clone() {
|
||||
SubqueryValue::Regex { space0, expr } => {
|
||||
tokens.push(Token::QueryType(String::from("regex")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, expr.tokenize());
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Predicate {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
if self.not {
|
||||
tokens.push(Token::Not(String::from("not")));
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
}
|
||||
add_tokens(&mut tokens, self.predicate_func.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for PredicateFunc {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
self.value.tokenize()
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for PredicateFuncValue {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
match self {
|
||||
PredicateFuncValue::EqualNull { space0 } => {
|
||||
tokens.push(Token::PredicateType(String::from("equals")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Keyword("null".to_string()));
|
||||
}
|
||||
PredicateFuncValue::EqualBool { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("equals")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Boolean(value.to_string()));
|
||||
}
|
||||
PredicateFuncValue::EqualString { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("equals")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, value.tokenize());
|
||||
}
|
||||
PredicateFuncValue::EqualInt { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("equals")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Number(value.to_string()));
|
||||
}
|
||||
PredicateFuncValue::EqualFloat { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("equals")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Number(value.to_string()));
|
||||
}
|
||||
PredicateFuncValue::EqualExpression { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("equals")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.append(&mut value.tokenize());
|
||||
}
|
||||
PredicateFuncValue::CountEqual { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("countEquals")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Boolean(value.to_string()));
|
||||
}
|
||||
PredicateFuncValue::StartWith { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("startsWith")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, value.tokenize());
|
||||
}
|
||||
PredicateFuncValue::Contain { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("contains")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, value.tokenize());
|
||||
}
|
||||
PredicateFuncValue::IncludeString { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("includes")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, value.tokenize());
|
||||
}
|
||||
PredicateFuncValue::IncludeInt { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("includes")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Number(value.to_string()));
|
||||
}
|
||||
PredicateFuncValue::IncludeFloat { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("includes")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Number(value.to_string()));
|
||||
}
|
||||
PredicateFuncValue::IncludeNull { space0 } => {
|
||||
tokens.push(Token::PredicateType(String::from("includes")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Keyword("null".to_string()));
|
||||
}
|
||||
PredicateFuncValue::IncludeBool { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("includes")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.push(Token::Boolean(value.to_string()));
|
||||
}
|
||||
PredicateFuncValue::IncludeExpression { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("includes")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
tokens.append(&mut value.tokenize());
|
||||
}
|
||||
PredicateFuncValue::Match { space0, value } => {
|
||||
tokens.push(Token::PredicateType(String::from("matches")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, value.tokenize());
|
||||
}
|
||||
PredicateFuncValue::Exist {} => {
|
||||
tokens.push(Token::PredicateType(String::from("exists")));
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for EncodedString {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
if self.quotes {
|
||||
tokens.push(Token::Quote(
|
||||
if self.clone().quotes { "\"" } else { "" }.to_string(),
|
||||
));
|
||||
}
|
||||
tokens.push(Token::String(self.encoded.clone()));
|
||||
|
||||
if self.quotes {
|
||||
tokens.push(Token::Quote(
|
||||
if self.clone().quotes { "\"" } else { "" }.to_string(),
|
||||
));
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Template {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
if self.quotes {
|
||||
tokens.push(Token::Quote(
|
||||
if self.clone().quotes { "\"" } else { "" }.to_string(),
|
||||
));
|
||||
}
|
||||
for element in self.elements.clone() {
|
||||
add_tokens(&mut tokens, element.tokenize());
|
||||
}
|
||||
|
||||
if self.quotes {
|
||||
tokens.push(Token::Quote(
|
||||
if self.clone().quotes { "\"" } else { "" }.to_string(),
|
||||
));
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for TemplateElement {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
match self {
|
||||
TemplateElement::String { encoded, .. } => {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::String(encoded.to_string()));
|
||||
tokens
|
||||
}
|
||||
TemplateElement::Expression(value) => {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(&mut tokens, value.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Expr {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::CodeDelimiter(String::from("{{")));
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
tokens.push(Token::CodeVariable(self.variable.name.clone()));
|
||||
add_tokens(&mut tokens, self.space1.tokenize());
|
||||
tokens.push(Token::CodeDelimiter(String::from("}}")));
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for LineTerminator {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
add_tokens(&mut tokens, self.space0.tokenize());
|
||||
if let Some(comment) = self.clone().comment {
|
||||
add_tokens(&mut tokens, comment.tokenize());
|
||||
}
|
||||
add_tokens(&mut tokens, self.newline.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Whitespace {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
if self.value != "" {
|
||||
tokens.push(Token::Whitespace(self.clone().value));
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Comment {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::Comment(format!("#{}", self.clone().value)));
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for Filename {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
return vec![Token::String(self.clone().value)];
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for JsonValue {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
match self {
|
||||
JsonValue::String(s) => {
|
||||
//tokens.push(Token::CodeDelimiter("\"".to_string()));
|
||||
tokens.append(&mut s.tokenize());
|
||||
//tokens.push(Token::CodeDelimiter("\"".to_string()));
|
||||
}
|
||||
JsonValue::Number(value) => {
|
||||
tokens.push(Token::Number(value.clone()));
|
||||
}
|
||||
JsonValue::Boolean(value) => {
|
||||
tokens.push(Token::Number(value.to_string()));
|
||||
}
|
||||
JsonValue::List { space0, elements } => {
|
||||
tokens.push(Token::CodeDelimiter("[".to_string()));
|
||||
tokens.push(Token::Whitespace(space0.clone()));
|
||||
for (i, element) in elements.iter().enumerate() {
|
||||
if i > 0 {
|
||||
tokens.push(Token::CodeDelimiter(",".to_string()));
|
||||
}
|
||||
tokens.append(&mut element.tokenize());
|
||||
}
|
||||
tokens.push(Token::CodeDelimiter("]".to_string()));
|
||||
}
|
||||
JsonValue::Object { space0, elements } => {
|
||||
tokens.push(Token::CodeDelimiter("{".to_string()));
|
||||
tokens.push(Token::Whitespace(space0.clone()));
|
||||
for (i, element) in elements.iter().enumerate() {
|
||||
if i > 0 {
|
||||
tokens.push(Token::CodeDelimiter(",".to_string()));
|
||||
}
|
||||
tokens.append(&mut element.tokenize());
|
||||
}
|
||||
tokens.push(Token::CodeDelimiter("}".to_string()));
|
||||
}
|
||||
JsonValue::Null {} => {
|
||||
tokens.push(Token::Keyword("null".to_string()));
|
||||
}
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for JsonListElement {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::Whitespace(self.space0.clone()));
|
||||
tokens.append(&mut self.value.tokenize());
|
||||
tokens.push(Token::Whitespace(self.space1.clone()));
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for JsonObjectElement {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.push(Token::Whitespace(self.space0.clone()));
|
||||
tokens.push(Token::Quote("\"".to_string()));
|
||||
tokens.push(Token::String(self.name.clone()));
|
||||
tokens.push(Token::Quote("\"".to_string()));
|
||||
tokens.push(Token::Whitespace(self.space1.clone()));
|
||||
tokens.push(Token::CodeDelimiter(":".to_string()));
|
||||
tokens.push(Token::Whitespace(self.space2.clone()));
|
||||
tokens.append(&mut self.value.tokenize());
|
||||
tokens.push(Token::Whitespace(self.space3.clone()));
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Html {
|
||||
pub head: Head,
|
||||
pub body: Body,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Head {
|
||||
pub title: String,
|
||||
pub stylesheet: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Body {
|
||||
pub children: Vec<Element>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Element {
|
||||
TextElement(String),
|
||||
NodeElement {
|
||||
name: String,
|
||||
attributes: Vec<Attribute>,
|
||||
children: Vec<Element>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Attribute {
|
||||
Class(String),
|
||||
Id(String),
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
|
||||
pub use self::ast::{Attribute, Body, Element, Head, Html};
|
||||
|
||||
mod ast;
|
||||
mod render;
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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 super::ast::*;
|
||||
|
||||
impl Html {
|
||||
pub fn render(self) -> String {
|
||||
format!(
|
||||
"<!DOCTYPE html>\n<html>{}{}</html>",
|
||||
self.head.render(),
|
||||
self.body.render()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Head {
|
||||
fn render(self) -> String {
|
||||
let mut s = "".to_string();
|
||||
s.push_str(format!("<title>{}</title>", self.title).as_str());
|
||||
if let Some(filename) = self.stylesheet {
|
||||
s.push_str(
|
||||
format!(
|
||||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">",
|
||||
filename
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
format!("<head>{}</head>", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Body {
|
||||
fn render(self) -> String {
|
||||
let children: Vec<String> = self.children.iter().map(|e| e.clone().render()).collect();
|
||||
format!("<body>{}</body>", children.join(""))
|
||||
}
|
||||
}
|
||||
|
||||
impl Element {
|
||||
fn render(self) -> String {
|
||||
match self {
|
||||
Element::NodeElement {
|
||||
name,
|
||||
children,
|
||||
attributes,
|
||||
} => {
|
||||
let attributes = if attributes.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!(
|
||||
" {}",
|
||||
attributes
|
||||
.iter()
|
||||
.map(|a| a.clone().render())
|
||||
.collect::<Vec<String>>()
|
||||
.join("")
|
||||
)
|
||||
};
|
||||
let children: Vec<String> = children.iter().map(|e| e.clone().render()).collect();
|
||||
format!("<{}{}>{}</{}>", name, attributes, children.join(""), name)
|
||||
}
|
||||
Element::TextElement(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
fn render(self) -> String {
|
||||
match self {
|
||||
Attribute::Class(s) => format!("class=\"{}\"", s),
|
||||
Attribute::Id(s) => format!("id=\"{}\"", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
pub fn sample_html() -> Html {
|
||||
Html {
|
||||
head: Head {
|
||||
title: "This is a title".to_string(),
|
||||
stylesheet: None,
|
||||
},
|
||||
body: Body {
|
||||
children: vec![Element::NodeElement {
|
||||
name: "p".to_string(),
|
||||
attributes: vec![],
|
||||
children: vec![Element::TextElement("Hello world!".to_string())],
|
||||
}],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_html() {
|
||||
assert_eq!(sample_html().render(), "<!DOCTYPE html>\n<html><head><title>This is a title</title></head><body><p>Hello world!</p></body></html>");
|
||||
}
|
||||
|
||||
pub fn sample_div() -> Element {
|
||||
Element::NodeElement {
|
||||
name: "div".to_string(),
|
||||
attributes: vec![Attribute::Class("request".to_string())],
|
||||
children: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_div() {
|
||||
assert_eq!(
|
||||
sample_div().render(),
|
||||
"<div class=\"request\"></div>".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* hurl (https://hurl.dev)
|
||||
* Copyright (C) 2020 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.
|
||||
*
|
||||
*/
|
||||
#![cfg_attr(feature = "strict", deny(warnings))]
|
||||
|
||||
pub mod cli;
|
||||
pub mod format;
|
||||
pub mod html;
|
||||
pub mod linter;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue