Skip to content

Commit

Permalink
Support booleans and numbers for query values (#170)
Browse files Browse the repository at this point in the history
## Description

Booleans, numeric and nulls are now supported for query values.

#### Changes:
- Implemented ```Deserialize``` manually for ```Template``` using
~~```serde_yaml::value::Value```~~ ```TemplateVisitor```
- Adjusted documentation, although ```"true"``` is still supported I
replaced it with ```true```
- Updated ```CHANGELOG.md``` (twice 😅)

## QA
- Added Template deserialization tests inside ```cereal.rs```

Closes #141
  • Loading branch information
maksimowiczm committed Apr 24, 2024
1 parent de1cc86 commit eb7f0cc
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased] - ReleaseDate

### Added

- Support booleans and numbers for query values ([#141](https://github.com/LucasPickering/slumber/issues/141))

### Changed

- Folders can now be collapsed in the recipe list ([#155](https://github.com/LucasPickering/slumber/issues/155))
Expand Down
2 changes: 1 addition & 1 deletion docs/src/api/request_collection/request_recipe.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ fish: !folder
method: GET
url: "{{host}}/fishes"
query:
big: "true"
big: true
```
2 changes: 1 addition & 1 deletion docs/src/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ requests:
method: GET
url: "{{host}}/fishes"
query:
big: "true"
big: true
```

This request collection uses [templates](./user_guide//templates.md) and [profiles](./api/request_collection/profile.md) allow you to dynamically change the target host.
2 changes: 1 addition & 1 deletion docs/src/user_guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ requests:
method: GET
url: "{{host}}/fishes"
query:
big: "true"
big: true
```

You can use the `request` subcommand:
Expand Down
6 changes: 3 additions & 3 deletions docs/src/user_guide/inheritance.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ requests:
method: GET
url: "{{host}}/fishes"
query:
big: "true"
big: true
headers:
Accept: application/json
authentication: !bearer "{{chains.token}}"
Expand Down Expand Up @@ -63,7 +63,7 @@ requests:
method: GET
url: "{{host}}/fishes"
query:
big: "true"
big: true

get_fish: !request
<<: *request_base
Expand Down Expand Up @@ -100,7 +100,7 @@ requests:
method: GET
url: "{{host}}/fishes"
query:
big: "true"
big: true

get_fish: !request
<<: *request_base
Expand Down
2 changes: 1 addition & 1 deletion docs/src/user_guide/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ requests:
method: GET
url: "{{host}}/fishes"
query:
big: "true"
big: true
```

Now you can easily select which host to hit. In the TUI, this is done via the Profile list. In the CLI, use the `--profile` option:
Expand Down
79 changes: 76 additions & 3 deletions src/collection/cereal.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
//! Serialization/deserialization helpers for various types

use crate::collection::{
recipe_tree::RecipeNode, Chain, ChainId, Profile, ProfileId, RecipeId,
use crate::{
collection::{
recipe_tree::RecipeNode, Chain, ChainId, Profile, ProfileId, RecipeId,
},
template::Template,
};
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer,
};
use serde::{Deserialize, Deserializer};
use std::hash::Hash;

/// A type that has an `id` field. This is ripe for a derive macro, maybe a fun
Expand Down Expand Up @@ -62,6 +68,48 @@ where
Ok(map)
}

// Custom deserializer for `Template`. This is useful for deserializing values
// that are not strings, but should be treated as strings such as numbers,
// booleans, and nulls.
impl<'de> Deserialize<'de> for Template {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct TemplateVisitor;

macro_rules! visit_primitive {
($func:ident, $type:ty) => {
fn $func<E>(self, v: $type) -> Result<Self::Value, E>
where
E: Error,
{
Template::try_from(v.to_string()).map_err(E::custom)
}
};
}

impl<'de> Visitor<'de> for TemplateVisitor {
type Value = Template;

fn expecting(
&self,
formatter: &mut std::fmt::Formatter,
) -> std::fmt::Result {
formatter.write_str("string, number, or boolean")
}

visit_primitive!(visit_bool, bool);
visit_primitive!(visit_u64, u64);
visit_primitive!(visit_i64, i64);
visit_primitive!(visit_f64, f64);
visit_primitive!(visit_str, &str);
}

deserializer.deserialize_any(TemplateVisitor)
}
}

/// Serialize/deserialize a duration with unit shorthand. This does *not* handle
/// subsecond precision. Supported units are:
/// - s
Expand Down Expand Up @@ -198,3 +246,28 @@ pub mod serde_duration {
}
}
}

#[cfg(test)]
mod tests {
use crate::template::Template;
use rstest::rstest;
use serde_test::{assert_de_tokens, Token};

#[rstest]
// boolean
#[case(Token::Bool(true), "true")]
#[case(Token::Bool(false), "false")]
// numeric
#[case(Token::U64(1000), "1000")]
#[case(Token::I64(-1000), "-1000")]
#[case(Token::F64(10.1), "10.1")]
#[case(Token::F64(-10.1), "-10.1")]
// string
#[case(Token::Str("hello"), "hello")]
#[case(Token::Str("null"), "null")]
#[case(Token::Str("true"), "true")]
#[case(Token::Str("false"), "false")]
fn test_deserialize_template(#[case] token: Token, #[case] expected: &str) {
assert_de_tokens(&Template::from(expected), &[token]);
}
}
6 changes: 3 additions & 3 deletions src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
};
use derive_more::{Deref, Display};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use std::{fmt::Debug, sync::atomic::AtomicU8};

/// Maximum number of layers of nested templates
Expand Down Expand Up @@ -58,9 +58,9 @@ pub struct TemplateContext {
pub recursion_count: AtomicU8,
}

/// A immutable string that can contain templated content. The string is parsed
/// An immutable string that can contain templated content. The string is parsed
/// during creation to identify template keys, hence the immutability.
#[derive(Clone, Debug, Deref, Display, Serialize, Deserialize)]
#[derive(Clone, Debug, Deref, Display, Serialize)]
#[cfg_attr(test, derive(PartialEq))]
#[display("{template}")]
#[serde(into = "String", try_from = "String")]
Expand Down

0 comments on commit eb7f0cc

Please sign in to comment.