Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Changelog

All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- **Interpolation Functions**: New string manipulation functions for use in data values and hierarchy paths.
- `substring(string, start[, count])`: Extract a portion of a string using 0-based indexing.
- `%{substring('abcdef', 2)}` returns `'cdef'`
- `%{substring('abcdef', 2, 3)}` returns `'cde'`
- `match(string, pattern)`: Test if a string matches a regex or contains a substring.
- `%{match($role, '/^web/')}` returns `'true'` or `'false'`
- `%{match($hostname, 'db')}` returns `'true'` if hostname contains 'db'
- Regex flags supported: `i` (case-insensitive), `m` (multiline), `x` (extended)
- `grep_captures(string, '/regex/'[, separator][, [groups]])`: Extract capture groups from a regex match.
- `%{grep_captures('abc-123', '/([a-z]+)-(\d+)/')}` returns `'abc123'` (all groups concatenated)
- `%{grep_captures('abc-123', '/([a-z]+)-(\d+)/', '-')}` returns `'abc-123'` (all groups with separator)
- `%{grep_captures('abc-123', '/([a-z]+)-(\d+)/', [1])}` returns `'abc'` (specific groups)
- `%{grep_captures('abc-123', '/([a-z]+)-(\d+)/', '_', [1,2])}` returns `'abc_123'`
- Defaults: empty separator `''`, all capture groups
- Third argument auto-detected: array = groups, quoted string = separator
- `version_gt(a, b)`, `version_gte(a, b)`, `version_lt(a, b)`, `version_lte(a, b)`: Compare semantic versions.
- `%{version_gte($app_version, '2.0.0')}` returns `'true'` or `'false'`
- Uses `Gem::Version` for proper semantic version comparison
- Handles `1.10.0 > 1.9.0` and prerelease versions correctly

### Changed

- `RX_METHOD_AND_ARG` regex updated to support function arguments containing parentheses (needed for regex capture groups).
- Argument parser now handles array literals `[1,2,3]` for specifying capture group indices.

### Fixed

- Regex literals with escaped slashes (e.g., `/a\/b/`) are now parsed correctly.
- Empty quoted strings (`''` or `""`) are now handled correctly as function arguments.

### Notes

- Single-quoted regex patterns (`'/pattern/'`) are recommended:
- Support curly brace quantifiers: `'/\d{3,5}/'`
- No YAML double-escaping needed: `'/\d+/'` instead of `/\\d+/`
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,96 @@ other.key: 'hiera data %{hiera("a.''b.c''.d")}'
Note that two single quotes are used to escape a single quote inside a single quoted string
(that's YAML syntax, not Hiera) and that the quoted key must be quoted in turn.

### Interpolation Functions

Hiera supports interpolation functions that can manipulate strings and perform pattern matching.
These functions can be used in both data values and hierarchy paths in `hiera.yaml`.

#### substring

Extract a portion of a string:

<pre>
# substring(string, start) - from start index to end
# substring(string, start, count) - from start index, count characters

path: "nodes/%{substring($certname, 0, 4)}"
value: "%{substring('abcdef', 2, 3)}" # returns 'cde'
</pre>

Arguments can be:
- Quoted literals: `'string'` or `"string"`
- Scope variables: `$varname` or `varname`
- Integers: `0`, `5`, etc.

Indexing is 0-based. Negative indices are supported (e.g., `-1` for last character).

#### match

Test if a string matches a pattern:

<pre>
# match(string, /regex/) - returns 'true' or 'false'
# match(string, 'needle') - returns 'true' if string contains needle

is_web: "%{match($role, '/^web/')}"
has_db: "%{match($hostname, 'db')}"
</pre>

Regex literals support flags:
- `i` - case insensitive
- `m` - multiline
- `x` - extended (ignore whitespace)

<pre>
# Case-insensitive match
match_result: "%{match('HELLO', '/hello/i')}" # returns 'true'
</pre>

#### grep_captures

Extract capture groups from a regex match and join them with a separator:

<pre>
# grep_captures(string, '/regex/') - returns all capture groups concatenated
# grep_captures(string, '/regex/', separator) - returns all groups joined by separator
# grep_captures(string, '/regex/', [groups]) - returns specified groups concatenated
# grep_captures(string, '/regex/', separator, [groups]) - returns specified groups joined by separator

# Extract all capture groups (default: concatenated)
all: "%{grep_captures('abc-123-def', '/([a-z]+)-(\d+)-([a-z]+)/')}" # returns 'abc123def'

# Extract all groups with separator
parts: "%{grep_captures('abc-123-def', '/([a-z]+)-(\d+)-([a-z]+)/', '-')}" # returns 'abc-123-def'

# Extract specific groups (concatenated)
selected: "%{grep_captures('abc-123-def', '/([a-z]+)-(\d+)-([a-z]+)/', [1,3])}" # returns 'abcdef'

# Extract specific groups with custom separator
custom: "%{grep_captures('abc-123-def', '/([a-z]+)-(\d+)-([a-z]+)/', '_', [1,3])}" # returns 'abc_def'
</pre>

**Tip:** Wrap regex patterns in single quotes (`'/pattern/'`) for cleaner syntax:
- Curly brace quantifiers work: `'/\d{3,5}/'`
- No double-escaping needed: `'/\d+/'` instead of `/\\d+/`

#### Version Comparison Functions

Compare semantic versions using `Gem::Version` comparison:

<pre>
# version_gt(a, b) - returns 'true' if a > b
# version_gte(a, b) - returns 'true' if a >= b
# version_lt(a, b) - returns 'true' if a < b
# version_lte(a, b) - returns 'true' if a <= b

is_newer: "%{version_gt($app_version, '2.0.0')}" # true if app_version > 2.0.0
is_compatible: "%{version_gte($ruby_version, '2.7.0')}" # true if ruby >= 2.7.0
needs_upgrade: "%{version_lt($os_version, '8.0')}" # true if os < 8.0
</pre>

Handles semantic versioning correctly (`1.10.0 > 1.9.0`) and prerelease versions (`1.0.0.alpha < 1.0.0`).

## Future Enhancements

* More backends should be created
Expand Down
Loading