Skip to content

Commit

Permalink
armstrong-numbers: sync (#1959)
Browse files Browse the repository at this point in the history
This also adds a new custom tera filter to format number literals in a
more human-readable way (by inserting `_` every third digit).

Most of the additional tests don't seem to add any particular value
except for the last one, which is [under consideration for
upstreaming](https://forum.exercism.org/t/armstrong-numbers-test-case-against-overflow/12457).

[no important files changed]
  • Loading branch information
senekor authored Aug 12, 2024
1 parent a37101a commit 9534dc5
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 42 deletions.
1 change: 1 addition & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ There are some custom tera filters in [`rust-tooling`](/rust-tooling/generate/sr
Here's the hopefully up-to-date list:
- `to_hex` formats ints in hexadecimal
- `snake_case` massages an arbitrary string into a decent Rust identifier
- `fmt_num` format number literals (insert `_` every third digit)

Feel free to add your own in the crate `rust-tooling`.
Hopefully you'll remember to update the list here as well. 🙂
Expand Down
9 changes: 9 additions & 0 deletions exercises/practice/armstrong-numbers/.meta/test_template.tera
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use armstrong_numbers::*;

{% for test in cases %}
#[test]
#[ignore]
fn {{ test.description | snake_case }}() {
assert!({% if not test.expected %} ! {% endif %}is_armstrong_number({{ test.input.number | fmt_num }}))
}
{% endfor -%}
56 changes: 53 additions & 3 deletions exercises/practice/armstrong-numbers/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,53 @@
# This is an auto-generated file. Regular comments will be removed when this
# file is regenerated. Regenerating will not touch any manually added keys,
# so comments can be added in a "comment" key.
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[c1ed103c-258d-45b2-be73-d8c6d9580c7b]
description = "Zero is an Armstrong number"

[579e8f03-9659-4b85-a1a2-d64350f6b17a]
description = "Single-digit numbers are Armstrong numbers"

[2d6db9dc-5bf8-4976-a90b-b2c2b9feba60]
description = "There are no two-digit Armstrong numbers"

[509c087f-e327-4113-a7d2-26a4e9d18283]
description = "Three-digit number that is an Armstrong number"

[7154547d-c2ce-468d-b214-4cb953b870cf]
description = "Three-digit number that is not an Armstrong number"

[6bac5b7b-42e9-4ecb-a8b0-4832229aa103]
description = "Four-digit number that is an Armstrong number"

[eed4b331-af80-45b5-a80b-19c9ea444b2e]
description = "Four-digit number that is not an Armstrong number"

[f971ced7-8d68-4758-aea1-d4194900b864]
description = "Seven-digit number that is an Armstrong number"

[7ee45d52-5d35-4fbd-b6f1-5c8cd8a67f18]
description = "Seven-digit number that is not an Armstrong number"

[5ee2fdf8-334e-4a46-bb8d-e5c19c02c148]
description = "Armstrong number containing seven zeroes"
include = false
comment = """
The exercise was designed with u32 in the interface,
which does not support numbers that big.
"""

[12ffbf10-307a-434e-b4ad-c925680e1dd4]
description = "The largest and last Armstrong number"
include = false
comment = """
The exercise was designed with u32 in the interface,
which does not support numbers that big.
"""
47 changes: 10 additions & 37 deletions exercises/practice/armstrong-numbers/tests/armstrong-numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,69 +13,42 @@ fn single_digit_numbers_are_armstrong_numbers() {

#[test]
#[ignore]
fn there_are_no_2_digit_armstrong_numbers() {
fn there_are_no_two_digit_armstrong_numbers() {
assert!(!is_armstrong_number(10))
}

#[test]
#[ignore]
fn three_digit_armstrong_number() {
fn three_digit_number_that_is_an_armstrong_number() {
assert!(is_armstrong_number(153))
}

#[test]
#[ignore]
fn three_digit_non_armstrong_number() {
fn three_digit_number_that_is_not_an_armstrong_number() {
assert!(!is_armstrong_number(100))
}

#[test]
#[ignore]
fn four_digit_armstrong_number() {
assert!(is_armstrong_number(9474))
fn four_digit_number_that_is_an_armstrong_number() {
assert!(is_armstrong_number(9_474))
}

#[test]
#[ignore]
fn four_digit_non_armstrong_number() {
assert!(!is_armstrong_number(9475))
fn four_digit_number_that_is_not_an_armstrong_number() {
assert!(!is_armstrong_number(9_475))
}

#[test]
#[ignore]
fn seven_digit_armstrong_number() {
fn seven_digit_number_that_is_an_armstrong_number() {
assert!(is_armstrong_number(9_926_315))
}

#[test]
#[ignore]
fn seven_digit_non_armstrong_number() {
assert!(!is_armstrong_number(9_926_316))
}

#[test]
#[ignore]
fn nine_digit_armstrong_number() {
assert!(is_armstrong_number(912_985_153));
}

#[test]
#[ignore]
fn nine_digit_non_armstrong_number() {
assert!(!is_armstrong_number(999_999_999));
}

#[test]
#[ignore]
fn ten_digit_non_armstrong_number() {
assert!(!is_armstrong_number(3_999_999_999));
}

// The following number has an Armstrong sum equal to 2^32 plus itself,
// and therefore will be detected as an Armstrong number if you are
// incorrectly using wrapping arithmetic.
#[test]
#[ignore]
fn properly_handles_overflow() {
assert!(!is_armstrong_number(4_106_098_957));
fn seven_digit_number_that_is_not_an_armstrong_number() {
assert!(!is_armstrong_number(9_926_314))
}
2 changes: 1 addition & 1 deletion rust-tooling/generate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ convert_case = "0.6.0"
glob = "0.3.1"
inquire = "0.6.2"
models = { version = "0.1.0", path = "../models" }
serde_json = { version = "1.0.105", features = ["preserve_order"] }
serde_json = { version = "1.0.105", features = ["arbitrary_precision", "preserve_order"] }
slug = "0.1.5"
tera = "1.19.1"
utils = { version = "0.1.0", path = "../utils" }
Expand Down
28 changes: 27 additions & 1 deletion rust-tooling/generate/src/custom_filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ use tera::{Result, Value};

type Filter = fn(&Value, &HashMap<String, Value>) -> Result<Value>;

pub static CUSTOM_FILTERS: &[(&str, Filter)] = &[("to_hex", to_hex), ("snake_case", snake_case)];
pub static CUSTOM_FILTERS: &[(&str, Filter)] = &[
("to_hex", to_hex),
("snake_case", snake_case),
("fmt_num", fmt_num),
];

pub fn to_hex(value: &Value, _args: &HashMap<String, Value>) -> Result<Value> {
let Some(value) = value.as_u64() else {
Expand All @@ -28,3 +32,25 @@ pub fn snake_case(value: &Value, _args: &HashMap<String, Value>) -> Result<Value
slug::slugify(value).replace('-', "_"),
))
}

pub fn fmt_num(value: &Value, _args: &HashMap<String, Value>) -> Result<Value> {
let Some(value) = value.as_number() else {
return Err(tera::Error::call_filter(
"fmt_num filter expects a number",
"serde_json::value::Value::as_number",
));
};
let mut num: Vec<_> = value.to_string().into();
num.reverse();

let mut pretty_digits = num
.chunks(3)
.flat_map(|digits| digits.iter().copied().chain([b'_']))
.collect::<Vec<_>>();
if pretty_digits.last() == Some(&b'_') {
pretty_digits.pop();
}
pretty_digits.reverse();
let pretty_num = String::from_utf8(pretty_digits).unwrap_or_default();
Ok(Value::String(pretty_num))
}

0 comments on commit 9534dc5

Please sign in to comment.