slumber-python 5.1.0__tar.gz → 5.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. {slumber_python-5.1.0 → slumber_python-5.1.1}/Cargo.lock +11 -11
  2. {slumber_python-5.1.0 → slumber_python-5.1.1}/Cargo.toml +9 -9
  3. {slumber_python-5.1.0 → slumber_python-5.1.1}/PKG-INFO +1 -1
  4. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/json.rs +89 -91
  5. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/http.rs +21 -26
  6. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/render/functions.rs +9 -12
  7. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/render/tests.rs +31 -74
  8. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/render.rs +109 -88
  9. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/util.rs +21 -0
  10. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/error.rs +0 -10
  11. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/lib.rs +19 -18
  12. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/tests.rs +16 -23
  13. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/value.rs +13 -0
  14. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/lib.rs +0 -15
  15. {slumber_python-5.1.0 → slumber_python-5.1.1}/README.md +0 -0
  16. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/Cargo.toml +0 -0
  17. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/cereal.rs +0 -0
  18. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/default.yml +0 -0
  19. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/default_old.yml +0 -0
  20. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/lib.rs +0 -0
  21. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui/cereal.rs +0 -0
  22. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui/input.rs +0 -0
  23. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui/mime.rs +0 -0
  24. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui/theme.rs +0 -0
  25. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/config/src/tui.rs +0 -0
  26. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/Cargo.toml +0 -0
  27. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/cereal.rs +0 -0
  28. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/models.rs +0 -0
  29. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/recipe_tree.rs +0 -0
  30. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection/schema.rs +0 -0
  31. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/collection.rs +0 -0
  32. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/database/convert.rs +0 -0
  33. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/database/migrations.rs +0 -0
  34. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/database/tests.rs +0 -0
  35. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/database.rs +0 -0
  36. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/http/curl.rs +0 -0
  37. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/http/models.rs +0 -0
  38. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/http/tests.rs +0 -0
  39. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/lib.rs +0 -0
  40. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/render/util.rs +0 -0
  41. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/core/src/test_util.rs +0 -0
  42. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/macros/Cargo.toml +0 -0
  43. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/macros/src/lib.rs +0 -0
  44. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/Cargo.toml +0 -0
  45. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/README.md +0 -0
  46. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/dev.py +0 -0
  47. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/mise.toml +0 -0
  48. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/slumber.pyi +0 -0
  49. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/src/lib.rs +0 -0
  50. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/tests/slumber.yml +0 -0
  51. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/tests/test.py +0 -0
  52. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/python/uv.lock +0 -0
  53. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/Cargo.toml +0 -0
  54. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/proptest-regressions/parse.txt +0 -0
  55. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/cereal.rs +0 -0
  56. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/display.rs +0 -0
  57. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/expression.rs +0 -0
  58. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/parse.rs +0 -0
  59. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/template/src/test_util.rs +0 -0
  60. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/Cargo.toml +0 -0
  61. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/paths.rs +0 -0
  62. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/test_util.rs +0 -0
  63. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/yaml/error.rs +0 -0
  64. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/yaml/resolve.rs +0 -0
  65. {slumber_python-5.1.0 → slumber_python-5.1.1}/crates/util/src/yaml.rs +0 -0
  66. {slumber_python-5.1.0 → slumber_python-5.1.1}/pyproject.toml +0 -0
@@ -809,7 +809,7 @@ dependencies = [
809
809
 
810
810
  [[package]]
811
811
  name = "doc_utils"
812
- version = "5.1.0"
812
+ version = "5.1.1"
813
813
  dependencies = [
814
814
  "anyhow",
815
815
  "clap",
@@ -3280,7 +3280,7 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
3280
3280
 
3281
3281
  [[package]]
3282
3282
  name = "slumber"
3283
- version = "5.1.0"
3283
+ version = "5.1.1"
3284
3284
  dependencies = [
3285
3285
  "anyhow",
3286
3286
  "console-subscriber",
@@ -3294,7 +3294,7 @@ dependencies = [
3294
3294
 
3295
3295
  [[package]]
3296
3296
  name = "slumber_cli"
3297
- version = "5.1.0"
3297
+ version = "5.1.1"
3298
3298
  dependencies = [
3299
3299
  "anyhow",
3300
3300
  "assert_cmd",
@@ -3329,7 +3329,7 @@ dependencies = [
3329
3329
 
3330
3330
  [[package]]
3331
3331
  name = "slumber_config"
3332
- version = "5.1.0"
3332
+ version = "5.1.1"
3333
3333
  dependencies = [
3334
3334
  "derive_more",
3335
3335
  "dirs",
@@ -3354,7 +3354,7 @@ dependencies = [
3354
3354
 
3355
3355
  [[package]]
3356
3356
  name = "slumber_core"
3357
- version = "5.1.0"
3357
+ version = "5.1.1"
3358
3358
  dependencies = [
3359
3359
  "async-trait",
3360
3360
  "base64 0.22.1",
@@ -3402,7 +3402,7 @@ dependencies = [
3402
3402
 
3403
3403
  [[package]]
3404
3404
  name = "slumber_import"
3405
- version = "5.1.0"
3405
+ version = "5.1.1"
3406
3406
  dependencies = [
3407
3407
  "anyhow",
3408
3408
  "base64 0.22.1",
@@ -3434,7 +3434,7 @@ dependencies = [
3434
3434
 
3435
3435
  [[package]]
3436
3436
  name = "slumber_macros"
3437
- version = "5.1.0"
3437
+ version = "5.1.1"
3438
3438
  dependencies = [
3439
3439
  "proc-macro2",
3440
3440
  "quote",
@@ -3443,7 +3443,7 @@ dependencies = [
3443
3443
 
3444
3444
  [[package]]
3445
3445
  name = "slumber_python"
3446
- version = "5.1.0"
3446
+ version = "5.1.1"
3447
3447
  dependencies = [
3448
3448
  "async-trait",
3449
3449
  "dialoguer",
@@ -3457,7 +3457,7 @@ dependencies = [
3457
3457
 
3458
3458
  [[package]]
3459
3459
  name = "slumber_template"
3460
- version = "5.1.0"
3460
+ version = "5.1.1"
3461
3461
  dependencies = [
3462
3462
  "bytes",
3463
3463
  "derive_more",
@@ -3484,7 +3484,7 @@ dependencies = [
3484
3484
 
3485
3485
  [[package]]
3486
3486
  name = "slumber_tui"
3487
- version = "5.1.0"
3487
+ version = "5.1.1"
3488
3488
  dependencies = [
3489
3489
  "anyhow",
3490
3490
  "async-trait",
@@ -3525,7 +3525,7 @@ dependencies = [
3525
3525
 
3526
3526
  [[package]]
3527
3527
  name = "slumber_util"
3528
- version = "5.1.0"
3528
+ version = "5.1.1"
3529
3529
  dependencies = [
3530
3530
  "derive_more",
3531
3531
  "dirs",
@@ -9,7 +9,7 @@ homepage = "https://slumber.lucaspickering.me"
9
9
  keywords = ["rest", "http", "terminal", "tui"]
10
10
  license = "MIT"
11
11
  repository = "https://github.com/LucasPickering/slumber"
12
- version = "5.1.0"
12
+ version = "5.1.1"
13
13
  # Keep in sync w/ rust-toolchain.toml
14
14
  rust-version = "1.90.0"
15
15
 
@@ -43,14 +43,14 @@ serde_json = {version = "1.0.120", default-features = false, features = ["preser
43
43
  serde_json_path = "0.7.1"
44
44
  serde_test = "1.0.176"
45
45
  serde_yaml = {version = "0.9.0", default-features = false}
46
- slumber_cli = {path = "./crates/cli", version = "5.1.0" }
47
- slumber_config = {path = "./crates/config", version = "5.1.0", default-features = false }
48
- slumber_core = {path = "./crates/core", version = "5.1.0" }
49
- slumber_import = {path = "./crates/import", version = "5.1.0" }
50
- slumber_macros = {path = "./crates/macros", version = "5.1.0" }
51
- slumber_template = {path = "./crates/template", version = "5.1.0" }
52
- slumber_tui = {path = "./crates/tui", version = "5.1.0" }
53
- slumber_util = {path = "./crates/util", version = "5.1.0" }
46
+ slumber_cli = {path = "./crates/cli", version = "5.1.1" }
47
+ slumber_config = {path = "./crates/config", version = "5.1.1", default-features = false }
48
+ slumber_core = {path = "./crates/core", version = "5.1.1" }
49
+ slumber_import = {path = "./crates/import", version = "5.1.1" }
50
+ slumber_macros = {path = "./crates/macros", version = "5.1.1" }
51
+ slumber_template = {path = "./crates/template", version = "5.1.1" }
52
+ slumber_tui = {path = "./crates/tui", version = "5.1.1" }
53
+ slumber_util = {path = "./crates/util", version = "5.1.1" }
54
54
  strum = {version = "0.27.0", default-features = false}
55
55
  syn = "2.0.101"
56
56
  terminput = "0.5.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slumber-python
3
- Version: 5.1.0
3
+ Version: 5.1.1
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -1,17 +1,17 @@
1
1
  //! Utilities for working with templated JSON
2
2
 
3
- use crate::render::TemplateContext;
3
+ use crate::{render::TemplateContext, util::value_to_json};
4
4
  use futures::future;
5
5
  use serde::{Serialize, Serializer, ser::SerializeMap};
6
6
  use slumber_template::{
7
- RenderError, Template, TemplateParseError, TryFromValue,
7
+ RenderError, RenderedOutput, Template, TemplateParseError, Value,
8
+ ValueError,
8
9
  };
9
10
  use std::str::FromStr;
10
11
  use thiserror::Error;
11
12
 
12
13
  /// A JSON value like [serde_json::Value], but all strings are templates
13
- #[derive(Clone, Debug, Serialize)]
14
- #[cfg_attr(any(test, feature = "test"), derive(PartialEq))]
14
+ #[derive(Clone, Debug, PartialEq, Serialize)]
15
15
  #[serde(untagged)]
16
16
  #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
17
17
  pub enum JsonTemplate {
@@ -50,82 +50,64 @@ impl JsonTemplate {
50
50
  }
51
51
  }
52
52
 
53
- /// Render all templates to strings and return a static JSON value
54
- pub async fn render(
55
- &self,
56
- context: &TemplateContext,
57
- ) -> Result<serde_json::Value, RenderError> {
58
- let rendered = match self {
59
- Self::Null => serde_json::Value::Null,
60
- Self::Bool(b) => serde_json::Value::Bool(*b),
61
- Self::Number(number) => serde_json::Value::Number(number.clone()),
62
- Self::String(template) => {
63
- // Render to a JSON value instead of just a string. If the
64
- // template is a single chunk that returns a non-string value
65
- // (e.g. a number or array), use that value directly. This
66
- // enables non-string values
67
- serde_json::Value::try_from_value(
68
- template
69
- .render(&context.streaming(false))
70
- .await
71
- .try_collect_value()
72
- .await?,
73
- )
74
- .map_err(|error| RenderError::Value(error.error))?
53
+ /// Render to previewable chunks
54
+ ///
55
+ /// The return value is *usually* a single chunk, but if the JSON value is
56
+ /// a multi-chunk template string, then its multi-chunk output will be the
57
+ /// output for this.
58
+ pub async fn render(&self, context: &TemplateContext) -> RenderedOutput {
59
+ match self {
60
+ JsonTemplate::Null => Value::Null.into(),
61
+ JsonTemplate::Bool(b) => Value::Boolean(*b).into(),
62
+ JsonTemplate::Number(n) => {
63
+ if let Some(i) = n.as_i64() {
64
+ Value::Integer(i).into()
65
+ } else if let Some(f) = n.as_f64() {
66
+ Value::Float(f).into()
67
+ } else {
68
+ // Integer is out of i64 range. Template values really
69
+ // should be a superset of JSON values, but right now that's
70
+ // not the case.
71
+ Err(RenderError::from(ValueError::other(
72
+ "JSON integer out of range",
73
+ )))
74
+ .into()
75
+ }
75
76
  }
76
- Self::Array(array) => {
77
- let array = future::try_join_all(
78
- array.iter().map(|item| item.render(context)),
79
- )
80
- .await?;
81
- serde_json::Value::Array(array)
77
+ JsonTemplate::String(template) => template.render(context).await,
78
+ JsonTemplate::Array(array) => {
79
+ // Render each value and collection into an Array
80
+ future::try_join_all(array.iter().map(|value| async {
81
+ value.render(context).await.try_collect_value().await
82
+ }))
83
+ .await
84
+ .map(Value::from)
85
+ .into() // Wrap into RenderedOutput
82
86
  }
83
- Self::Object(map) => {
84
- let map = future::try_join_all(map.iter().map(
85
- |(key, value)| async {
86
- let key = key
87
- .render_string(&context.streaming(false))
88
- .await?;
89
- let value = value.render(context).await?;
90
- Ok::<_, RenderError>((key, value))
91
- },
92
- ))
93
- .await?;
94
- serde_json::Value::Object(map.into_iter().collect())
87
+ JsonTemplate::Object(map) => {
88
+ // Render each key/value and collect into an Object
89
+ future::try_join_all(map.iter().map(|(key, value)| async {
90
+ let key = key.render_string(context).await?;
91
+ let value =
92
+ value.render(context).await.try_collect_value().await?;
93
+ Ok::<_, RenderError>((key, value))
94
+ }))
95
+ .await
96
+ .map(Value::from)
97
+ .into() // Wrap into RenderedOutput
95
98
  }
96
- };
97
- Ok(rendered)
99
+ }
98
100
  }
99
- }
100
101
 
101
- impl From<&JsonTemplate> for serde_json::Value {
102
- /// Convert a [JsonTemplate] to a [serde_json::Value], stringifying
103
- /// templates
104
- fn from(template: &JsonTemplate) -> Self {
105
- match template {
106
- JsonTemplate::Null => serde_json::Value::Null,
107
- JsonTemplate::Bool(b) => serde_json::Value::Bool(*b),
108
- JsonTemplate::Number(number) => {
109
- serde_json::Value::Number(number.clone())
110
- }
111
- JsonTemplate::String(template) => {
112
- serde_json::Value::String(template.display().to_string())
113
- }
114
- JsonTemplate::Array(array) => serde_json::Value::Array(
115
- array.iter().map(serde_json::Value::from).collect(),
116
- ),
117
- JsonTemplate::Object(object) => serde_json::Value::Object(
118
- object
119
- .iter()
120
- .map(|(key, value)| {
121
- (
122
- key.display().to_string(),
123
- serde_json::Value::from(value),
124
- )
125
- })
126
- .collect(),
127
- ),
128
- }
102
+ /// Render all templates to strings and return a static JSON value
103
+ pub async fn render_json(
104
+ &self,
105
+ context: &TemplateContext,
106
+ ) -> Result<serde_json::Value, RenderError> {
107
+ // Collect render output as a single value. The renderer should always
108
+ // output a single chunk, so it gets unpacked back to one value.
109
+ let value = self.render(context).await.try_collect_value().await?;
110
+ Ok(value_to_json(value))
129
111
  }
130
112
  }
131
113
 
@@ -210,45 +192,61 @@ pub enum JsonTemplateError {
210
192
  mod tests {
211
193
  use super::*;
212
194
  use crate::{
213
- collection::Profile, render::TemplateContext, test_util::by_id,
195
+ collection::Profile,
196
+ render::TemplateContext,
197
+ test_util::{by_id, invalid_utf8},
214
198
  };
215
199
  use indexmap::indexmap;
216
200
  use rstest::rstest;
217
201
  use serde_json::json;
218
202
  use slumber_util::{Factory, assert_result};
219
203
 
220
- /// Test that object keys are rendered as templates
204
+ /// Render JSON templates to JSON values
221
205
  #[rstest]
222
- #[case::template_key(
206
+ #[case::null(json!(null), Ok(json!(null)))]
207
+ #[case::bool(json!(true), Ok(json!(true)))]
208
+ #[case::integer(json!(3), Ok(json!(3)))]
209
+ #[case::float(json!(3.15), Ok(json!(3.15)))]
210
+ #[case::string(json!("{{ username }}"), Ok(json!("testuser")))]
211
+ #[case::array(
212
+ json!([1, 2, "{{ username }}"]),
213
+ Ok(json!([1, 2, "testuser"])),
214
+ )]
215
+ #[case::object(
223
216
  json!({"{{ user_id }}": {"name": "{{ username }}"}}),
224
217
  Ok(json!({"123": {"name": "testuser"}})),
225
218
  )]
226
- #[case::invalid_key(
227
- json!({"{{ user_id": {"name": "{{ username }}"}}),
228
- Err("invalid expression"),
229
- )]
219
+ // serde_json converts the byte string to a number array. Seems reasonable
220
+ // enough.
221
+ #[case::bytes(json!("{{ invalid_utf8 }}"), Ok(json!([0xc3, 0x28])))]
222
+ // Once we have non-string profile values we can test:
223
+ // - Invalid int/float values
224
+ // - Unpacking strings
230
225
  #[tokio::test]
231
- async fn test_render_template_keys(
226
+ async fn test_render_json(
232
227
  #[case] input: serde_json::Value,
233
228
  #[case] expected: Result<serde_json::Value, &str>,
234
229
  ) {
235
230
  let profile = Profile {
236
231
  data: indexmap! {
237
232
  "user_id".into() => "123".into(),
238
- "username".into() => "testuser".into()
233
+ "username".into() => "testuser".into(),
234
+ "invalid_utf8".into() => invalid_utf8(),
239
235
  },
240
236
  ..Profile::factory(())
241
237
  };
242
238
  let context =
243
239
  TemplateContext::factory((by_id([profile]), indexmap! {}));
244
240
 
245
- let result = match JsonTemplate::try_from(input) {
246
- // If we're expecting an error, it should happen during the parse
247
- // so don't check for errors during render. Saves having to
248
- // consolidate the error types
249
- Ok(json) => Ok(json.render(&context).await.unwrap()),
250
- Err(error) => Err(error),
251
- };
241
+ let template = JsonTemplate::try_from(input).expect("Invalid template");
242
+ let result = template.render_json(&context).await;
252
243
  assert_result(result, expected);
253
244
  }
245
+
246
+ /// Parsing a JSON value with a key that isn't a valid template is an error
247
+ #[test]
248
+ fn test_invalid_key_template() {
249
+ let json = json!({"{{ invalid_key": {"name": "{{ username }}"}});
250
+ assert_result(JsonTemplate::try_from(json), Err("invalid expression"));
251
+ }
254
252
  }
@@ -568,7 +568,7 @@ impl Recipe {
568
568
  ) -> Result<Url, RequestBuildErrorKind> {
569
569
  let template = options.url.as_ref().unwrap_or(&self.url);
570
570
  let url = template
571
- .render_string(&context.streaming(false))
571
+ .render_string(context)
572
572
  .await
573
573
  .map_err(RequestBuildErrorKind::UrlRender)?;
574
574
  url.parse::<Url>()
@@ -591,12 +591,12 @@ impl Recipe {
591
591
  );
592
592
 
593
593
  let iter = merged.into_iter().map(async |((param, _), template)| {
594
- let value = template
595
- .render_string(&context.streaming(false))
596
- .await
597
- .map_err(|error| RequestBuildErrorKind::QueryRender {
598
- parameter: param.to_owned(),
599
- error,
594
+ let value =
595
+ template.render_string(context).await.map_err(|error| {
596
+ RequestBuildErrorKind::QueryRender {
597
+ parameter: param.to_owned(),
598
+ error,
599
+ }
600
600
  })?;
601
601
  Ok((param.to_owned(), value))
602
602
  });
@@ -637,7 +637,7 @@ impl Recipe {
637
637
  value_template: &Template,
638
638
  ) -> Result<(HeaderName, HeaderValue), RequestBuildErrorKind> {
639
639
  let mut value: Vec<u8> = value_template
640
- .render_bytes(&context.streaming(false))
640
+ .render_bytes(context)
641
641
  .await
642
642
  .map_err(|error| RequestBuildErrorKind::HeaderRender {
643
643
  header: header.to_owned(),
@@ -679,17 +679,16 @@ impl Recipe {
679
679
  .authentication
680
680
  .as_ref()
681
681
  .or(self.authentication.as_ref());
682
- let context = context.streaming(false); // Auth templates never support streaming
683
682
  match authentication {
684
683
  Some(Authentication::Basic { username, password }) => {
685
684
  let (username, password) =
686
685
  try_join!(
687
686
  username
688
- .render_string(&context)
687
+ .render_string(context)
689
688
  .map_err(RequestBuildErrorKind::AuthUsernameRender),
690
689
  async {
691
690
  OptionFuture::from(password.as_ref().map(
692
- |password| password.render_string(&context),
691
+ |password| password.render_string(context),
693
692
  ))
694
693
  .await
695
694
  .transpose()
@@ -701,7 +700,7 @@ impl Recipe {
701
700
 
702
701
  Some(Authentication::Bearer { token }) => {
703
702
  let token = token
704
- .render_string(&context)
703
+ .render_string(context)
705
704
  .await
706
705
  .map_err(RequestBuildErrorKind::AuthTokenRender)?;
707
706
  Ok(Some(Authentication::Bearer { token }))
@@ -735,7 +734,7 @@ impl Recipe {
735
734
  Some(BodyOverride::Raw(template)),
736
735
  ) => {
737
736
  let rendered = template
738
- .render_bytes(&context.streaming(false))
737
+ .render_bytes(context)
739
738
  .await
740
739
  .map_err(RequestBuildErrorKind::BodyRender)?;
741
740
  Ok(Some(RenderedBody::Raw(rendered)))
@@ -748,7 +747,7 @@ impl Recipe {
748
747
  Some(BodyOverride::Raw(template)),
749
748
  ) => {
750
749
  // Stream body is rendered as a stream (!!)
751
- let output = template.render(&context.streaming(true)).await;
750
+ let output = template.render(&context.stream()).await;
752
751
  let source = output.stream_source().cloned();
753
752
  let stream = output
754
753
  .try_into_stream()
@@ -764,7 +763,7 @@ impl Recipe {
764
763
  | (Some(RecipeBody::Raw(_)), Some(BodyOverride::Json(json)))
765
764
  | (Some(RecipeBody::Stream(_)), Some(BodyOverride::Json(json)))
766
765
  | (Some(RecipeBody::Json(_)), Some(BodyOverride::Json(json))) => {
767
- json.render(context)
766
+ json.render_json(context)
768
767
  .await
769
768
  .map(|value| Some(RenderedBody::Json(value)))
770
769
  .map_err(RequestBuildErrorKind::BodyRender)
@@ -776,15 +775,12 @@ impl Recipe {
776
775
  // handled above
777
776
  let merged = apply_overrides(fields, &options.form_fields);
778
777
  let iter = merged.into_iter().map(async |(field, template)| {
779
- let value = template
780
- .render_string(&context.streaming(false))
781
- .await
782
- .map_err(|error| {
783
- RequestBuildErrorKind::BodyFormFieldRender {
784
- field: field.clone(),
785
- error,
786
- }
787
- })?;
778
+ let value = template.render_string(context).await.map_err(
779
+ |error| RequestBuildErrorKind::BodyFormFieldRender {
780
+ field: field.clone(),
781
+ error,
782
+ },
783
+ )?;
788
784
  Ok::<_, RequestBuildErrorKind>((field.clone(), value))
789
785
  });
790
786
  let rendered = try_join_all(iter).await?;
@@ -793,8 +789,7 @@ impl Recipe {
793
789
  (Some(RecipeBody::FormMultipart(fields)), None) => {
794
790
  let merged = apply_overrides(fields, &options.form_fields);
795
791
  let iter = merged.into_iter().map(async |(field, template)| {
796
- let output =
797
- template.render(&context.streaming(true)).await;
792
+ let output = template.render(&context.stream()).await;
798
793
  // If this is a single-chunk template, we might be able to
799
794
  // load directly from the source, since we support file
800
795
  // streams natively. In that case, the stream will be thrown
@@ -2,7 +2,7 @@
2
2
 
3
3
  use crate::{
4
4
  collection::RecipeId,
5
- render::{FunctionError, Prompt, SelectOption, SingleRenderContext},
5
+ render::{FunctionError, Prompt, SelectOption, TemplateContext},
6
6
  };
7
7
  use base64::{Engine, prelude::BASE64_STANDARD};
8
8
  use bytes::Bytes;
@@ -124,7 +124,7 @@ pub fn boolean(value: Value) -> bool {
124
124
  /// ```
125
125
  #[template]
126
126
  pub fn command(
127
- #[context] context: &SingleRenderContext<'_>,
127
+ #[context] context: &TemplateContext,
128
128
  command: Vec<String>,
129
129
  #[kwarg] cwd: Option<String>,
130
130
  #[kwarg] stdin: Option<Bytes>,
@@ -304,10 +304,7 @@ pub fn env(variable: String, #[kwarg] default: String) -> String {
304
304
  /// output: Contents of config.json file
305
305
  /// ```
306
306
  #[template]
307
- pub fn file(
308
- #[context] context: &SingleRenderContext<'_>,
309
- path: String,
310
- ) -> LazyValue {
307
+ pub fn file(#[context] context: &TemplateContext, path: String) -> LazyValue {
311
308
  let path = context.root_dir.join(expand_home(PathBuf::from(path)));
312
309
  let source = StreamSource::File { path: path.clone() };
313
310
  // Return the file as a stream. If streaming isn't available here, it will
@@ -870,7 +867,7 @@ pub fn lower(value: String) -> String {
870
867
  /// ```
871
868
  #[template]
872
869
  pub async fn prompt(
873
- #[context] context: &SingleRenderContext<'_>,
870
+ #[context] context: &TemplateContext,
874
871
  #[kwarg] message: Option<String>,
875
872
  #[kwarg] default: Option<String>,
876
873
  #[kwarg] sensitive: bool,
@@ -979,7 +976,7 @@ pub fn replace(
979
976
  /// ```
980
977
  #[template]
981
978
  pub async fn response(
982
- #[context] context: &SingleRenderContext<'_>,
979
+ #[context] context: &TemplateContext,
983
980
  recipe_id: RecipeId,
984
981
  #[kwarg] trigger: RequestTrigger,
985
982
  ) -> Result<Bytes, FunctionError> {
@@ -1015,7 +1012,7 @@ pub async fn response(
1015
1012
  /// ```
1016
1013
  #[template]
1017
1014
  pub async fn response_header(
1018
- #[context] context: &SingleRenderContext<'_>,
1015
+ #[context] context: &TemplateContext,
1019
1016
  recipe_id: RecipeId,
1020
1017
  header: String,
1021
1018
  #[kwarg] trigger: RequestTrigger,
@@ -1055,7 +1052,7 @@ pub async fn response_header(
1055
1052
  /// ```
1056
1053
  #[template]
1057
1054
  pub async fn select(
1058
- #[context] context: &SingleRenderContext<'_>,
1055
+ #[context] context: &TemplateContext,
1059
1056
  options: Vec<SelectOption>,
1060
1057
  #[kwarg] message: Option<String>,
1061
1058
  ) -> Result<Value, FunctionError> {
@@ -1107,7 +1104,7 @@ impl TryFromValue for SelectOption {
1107
1104
  /// ```
1108
1105
  #[template]
1109
1106
  pub fn sensitive(
1110
- #[context] context: &SingleRenderContext<'_>,
1107
+ #[context] context: &TemplateContext,
1111
1108
  value: String,
1112
1109
  ) -> String {
1113
1110
  mask_sensitive(context, value)
@@ -1310,7 +1307,7 @@ pub fn upper(value: String) -> String {
1310
1307
  value.to_uppercase()
1311
1308
  }
1312
1309
 
1313
- fn mask_sensitive(context: &SingleRenderContext<'_>, value: String) -> String {
1310
+ fn mask_sensitive(context: &TemplateContext, value: String) -> String {
1314
1311
  if context.show_sensitive {
1315
1312
  value
1316
1313
  } else {