Skip to content

Commit

Permalink
Pre-parse template strings and switch to nom for parsing
Browse files Browse the repository at this point in the history
Wow it's a big refactor
  • Loading branch information
LucasPickering committed Nov 14, 2023
1 parent d7ffce5 commit 2445ae5
Show file tree
Hide file tree
Showing 22 changed files with 843 additions and 544 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [Unreleased] - ReleaseDate

### Changed

- Parse templates up front instead of during render
- Switch to nom for template parsing
- Parse errors should be better now

## [0.6.0] - 2023-11-11

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ dirs = "^5.0.1"
futures = "^0.3.28"
indexmap = {version = "^2.0.1", features = ["serde"]}
itertools = "^0.11.0"
nom = "7.1.3"
notify = {version = "^6.1.1", default-features = false, features = ["macos_fsevent"]}
ratatui = "^0.24.0"
regex = {version = "^1.9.5", features = ["pattern"]}
reqwest = {version = "^0.11.20", default-features = false, features = ["rustls-tls"]}
rmp-serde = "^1.1.2"
rusqlite = {version = "^0.29.0", default-features = false, features = ["bundled", "chrono", "uuid"]}
Expand Down
2 changes: 1 addition & 1 deletion docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
- [Request Recipe](./api/request_recipe.md)
- [Chain](./api/chain.md)
- [Chain Source](./api/chain_source.md)
- [Template String](./api/template_string.md)
- [Template](./api/template.md)
2 changes: 1 addition & 1 deletion docs/src/api/chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A chain is a intermediate data type to enable complex template values. Chains enable complex value sources and additional customization, such as much values as sensitive to be masked in the UI.

To use a chain in a template string, reference it as `{{chains.<id>}}`.
To use a chain in a template, reference it as `{{chains.<id>}}`.

## Fields

Expand Down
18 changes: 9 additions & 9 deletions docs/src/api/request_recipe.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ A request recipe defines how to make a particular request. For a REST API, you'l

## Fields

| Field | Type | Description | Default |
| --------- | --------------------------------------------------------- | --------------------------------- | ------------- |
| `id` | `string` | Unique identifier for this recipe | Required |
| `name` | `string` | Descriptive name to use in the UI | Value of `id` |
| `method` | [`TemplateString`](./template_string.md) | HTTP request method | Required |
| `url` | [`TemplateString`](./template_string.md) | HTTP request URL | Required |
| `query` | [`mapping[string, TemplateString]`](./template_string.md) | HTTP request query parameters | `{}` |
| `headers` | [`mapping[string, TemplateString]`](./template_string.md) | HTTP request headers | `{}` |
| `body` | [`TemplateString`](./template_string.md) | HTTP request body | `null` |
| Field | Type | Description | Default |
| --------- | -------------------------------------------- | --------------------------------- | ------------- |
| `id` | `string` | Unique identifier for this recipe | Required |
| `name` | `string` | Descriptive name to use in the UI | Value of `id` |
| `method` | `string` | HTTP request method | Required |
| `url` | [`Template`](./template.md) | HTTP request URL | Required |
| `query` | [`mapping[string, Template]`](./template.md) | HTTP request query parameters | `{}` |
| `headers` | [`mapping[string, Template]`](./template.md) | HTTP request headers | `{}` |
| `body` | [`Template`](./template.md) | HTTP request body | `null` |

## Examples

Expand Down
6 changes: 3 additions & 3 deletions docs/src/api/template_string.md → docs/src/api/template.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Template String
# Template

A template string is represented in YAML as a normal string, and thus supports [all of YAML's string syntaxes](https://www.educative.io/answers/how-to-represent-strings-in-yaml). Template strings receive post-processing that injects dynamic values into the string. A templated value is represented with `{{...}}`.
A template is represented in YAML as a normal string, and thus supports [all of YAML's string syntaxes](https://www.educative.io/answers/how-to-represent-strings-in-yaml). Templates receive post-processing that injects dynamic values into the string. A templated value is represented with `{{...}}`.

Template strings can generally be used in any _value_ in a request recipe (_not_ in keys). They _cannot_ be used in profiles or chains, to avoid the potential for recursive templating. If this feature would be useful to you, [le tme know](https://github.com/LucasPickering/slumber/issues/15).
Templates can generally be used in any _value_ in a request recipe (_not_ in keys). They _cannot_ be used in profiles or chains, to avoid the potential for recursive templating. If this feature would be useful to you, [le tme know](https://github.com/LucasPickering/slumber/issues/15).

## Template Sources

Expand Down
4 changes: 2 additions & 2 deletions docs/src/user_guide/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

Templates enable dynamic string construction. Slumber's template language is relatively simple, compared to complex HTML templating languages like Handlebars or Jinja. The goal is to be intuitive and unsurprising. It doesn't support complex features like for loops, conditionals, etc.

All string _values_ (i.e. _not_ keys) in a request collection are template strings, meaning they support templating. The syntax for templating a value into a string is double curly braces `{{...}}`. The contents inside the braces tell Slumber how to retrieve the dynamic value.
All string _values_ (i.e. _not_ keys) in a request collection are templates, meaning they support templating. The syntax for templating a value into a string is double curly braces `{{...}}`. The contents inside the braces tell Slumber how to retrieve the dynamic value.

TODO add some more content here

## API Reference

For more detailed configuration info, see the [API reference docs](../api/template_string.md) on template strings.
For more detailed configuration info, see the [API reference docs](../api/template.md) on templates.
33 changes: 27 additions & 6 deletions src/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
mod insomnia;

use crate::template::TemplateString;
use crate::template::Template;
use anyhow::{anyhow, Context};
use derive_more::{Deref, Display, From};
use indexmap::IndexMap;
Expand Down Expand Up @@ -86,12 +86,12 @@ pub struct RequestRecipe {
/// *Not* a template string because the usefulness doesn't justify the
/// complexity
pub method: String,
pub url: TemplateString,
pub body: Option<TemplateString>,
pub url: Template,
pub body: Option<Template>,
#[serde(default)]
pub query: IndexMap<String, TemplateString>,
pub query: IndexMap<String, Template>,
#[serde(default)]
pub headers: IndexMap<String, TemplateString>,
pub headers: IndexMap<String, Template>,
}

#[derive(
Expand All @@ -114,7 +114,7 @@ pub struct RequestRecipeId(String);
/// can use it in a template via `{{chains.<chain_id>}}`.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Chain {
pub id: String,
pub id: ChainId,
pub source: ChainSource,
/// Mask chained value in the UI
#[serde(default)]
Expand All @@ -123,6 +123,27 @@ pub struct Chain {
pub selector: Option<JsonPath>,
}

#[derive(
Clone,
Debug,
Deref,
Default,
Display,
Eq,
From,
Hash,
PartialEq,
Serialize,
Deserialize,
)]
pub struct ChainId(String);

impl From<&str> for ChainId {
fn from(value: &str) -> Self {
Self(value.into())
}
}

/// The source of data for a chain
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
Expand Down
19 changes: 11 additions & 8 deletions src/collection/insomnia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{
collection::{Profile, RequestCollection, RequestRecipe},
template::TemplateString,
template::Template,
};
use anyhow::Context;
use indexmap::IndexMap;
Expand Down Expand Up @@ -96,7 +96,7 @@ struct Request {
#[serde(rename = "_id")]
id: String,
name: String,
url: TemplateString,
url: Template,
method: String,
authentication: Authentication,
headers: Vec<Header>,
Expand All @@ -114,13 +114,13 @@ enum Authentication {
#[derive(Debug, Deserialize)]
struct Header {
name: String,
value: TemplateString,
value: Template,
}

#[derive(Debug, Deserialize)]
struct Parameter {
name: String,
value: TemplateString,
value: Template,
}

/// This can't be an `Option` because the empty case is an empty object, not
Expand All @@ -132,7 +132,7 @@ enum Body {
#[serde(rename_all = "camelCase")]
Body {
mime_type: String,
text: TemplateString,
text: Template,
},
// This matches empty object, so it has to be a struct variant
Empty {},
Expand All @@ -150,17 +150,20 @@ impl From<Environment> for Profile {

impl From<Request> for RequestRecipe {
fn from(request: Request) -> Self {
let mut headers: IndexMap<String, TemplateString> = IndexMap::new();
let mut headers: IndexMap<String, Template> = IndexMap::new();

// Preload headers from implicit sources
if let Body::Body { mime_type, .. } = &request.body {
headers.insert("content-type".into(), mime_type.clone().into());
headers.insert(
"content-type".into(),
Template::dangerous_new(mime_type.clone()),
);
}
match request.authentication {
Authentication::Bearer { token } => {
headers.insert(
"authorization".into(),
format!("Bearer {token}").into(),
Template::dangerous_new(format!("Bearer {token}")),
);
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/factory.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
collection::{Chain, ChainSource, RequestRecipeId},
http::{Body, Repository, Request, RequestId, RequestRecord, Response},
template::{Prompt, Prompter, TemplateContext, TemplateString},
template::{Prompt, Prompter, Template, TemplateContext},
};
use chrono::Utc;
use factori::{create, factori};
Expand Down Expand Up @@ -49,7 +49,7 @@ factori!(RequestRecord, {

factori!(Chain, {
default {
id = String::from("chain1"),
id = "chain1".into(),
source = ChainSource::Request(RequestRecipeId::default()),
sensitive = false,
selector = None,
Expand Down Expand Up @@ -96,8 +96,8 @@ impl From<&str> for RequestRecipeId {
}
}

impl From<&str> for TemplateString {
impl From<&str> for Template {
fn from(value: &str) -> Self {
value.to_owned().into()
value.to_owned().try_into().unwrap()
}
}
6 changes: 6 additions & 0 deletions src/http/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ impl RequestId {
}
}

impl Default for RequestId {
fn default() -> Self {
Self::new()
}
}

/// A complete request+response pairing. This is generated by [HttpEngine::send]
/// when a response is received successfully for a sent request.
#[derive(Debug)]
Expand Down
Loading

0 comments on commit 2445ae5

Please sign in to comment.