Skip to content

Commit 06fafb9

Browse files
authored
Merge pull request #1145 from Gijsreyn/gh-57/main/add-substring-function
Add `substring()` function
2 parents 61da4b7 + ff5006e commit 06fafb9

File tree

5 files changed

+562
-0
lines changed

5 files changed

+562
-0
lines changed
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
---
2+
description: Reference for the 'substring' DSC configuration document function
3+
ms.date: 09/27/2025
4+
ms.topic: reference
5+
title: substring
6+
---
7+
8+
# substring
9+
10+
## Synopsis
11+
12+
Returns a substring that starts at the specified character position and contains
13+
the specified number of characters.
14+
15+
## Syntax
16+
17+
```Syntax
18+
substring(<stringToParse>, <startIndex>)
19+
substring(<stringToParse>, <startIndex>, <length>)
20+
```
21+
22+
## Description
23+
24+
The `substring()` function extracts a portion of a string based on the specified
25+
starting position and optional length. The function uses zero-based indexing,
26+
meaning the first character is at position 0. This is useful for parsing
27+
identifiers, extracting prefixes or suffixes, manipulating configuration values,
28+
or formatting display strings.
29+
30+
Key behaviors:
31+
32+
- **Zero-based indexing**: The first character is at index 0
33+
- **Optional length**: If length is omitted, returns the remainder of the string
34+
from the start position
35+
- **Boundary validation**: Prevents access beyond string boundaries with clear
36+
error messages
37+
38+
## Examples
39+
40+
### Example 1 - Extract environment from resource name
41+
42+
This example demonstrates extracting environment information from standardized
43+
resource names for conditional configuration. It uses the [`parameters()`][08]
44+
function to retrieve the resource name.
45+
46+
```yaml
47+
# substring.example.1.dsc.config.yaml
48+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
49+
parameters:
50+
resourceName:
51+
type: string
52+
defaultValue: svc-api-prod-east
53+
resources:
54+
- name: Extract environment
55+
type: Microsoft.DSC.Debug/Echo
56+
properties:
57+
output:
58+
environment: "[substring(parameters('resourceName'), 8, 4)]"
59+
```
60+
61+
```bash
62+
dsc config get --file substring.example.1.dsc.config.yaml
63+
```
64+
65+
```yaml
66+
results:
67+
- name: Extract environment
68+
type: Microsoft.DSC.Debug/Echo
69+
result:
70+
actualState:
71+
output:
72+
environment: prod
73+
messages: []
74+
hadErrors: false
75+
```
76+
77+
### Example 2 - Extract region from resource identifier
78+
79+
This example shows extracting a region code from a standardized resource
80+
identifier without specifying length, using [`parameters()`][08] to retrieve
81+
the identifier.
82+
83+
```yaml
84+
# substring.example.2.dsc.config.yaml
85+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
86+
parameters:
87+
resourceId:
88+
type: string
89+
defaultValue: app-web-eastus2-001
90+
resources:
91+
- name: Extract region
92+
type: Microsoft.DSC.Debug/Echo
93+
properties:
94+
output:
95+
region: "[substring(parameters('resourceId'), 8)]"
96+
```
97+
98+
```bash
99+
dsc config get --file substring.example.2.dsc.config.yaml
100+
```
101+
102+
```yaml
103+
results:
104+
- name: Extract region
105+
type: Microsoft.DSC.Debug/Echo
106+
result:
107+
actualState:
108+
output:
109+
region: eastus2-001
110+
messages: []
111+
hadErrors: false
112+
```
113+
114+
### Example 3 - Parse version components
115+
116+
This example demonstrates parsing semantic version strings to extract major,
117+
minor, and patch components. It uses [`parameters()`][08] to retrieve the
118+
version string.
119+
120+
```yaml
121+
# substring.example.3.dsc.config.yaml
122+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
123+
parameters:
124+
version:
125+
type: string
126+
defaultValue: "3.2.1"
127+
resources:
128+
- name: Parse version
129+
type: Microsoft.DSC.Debug/Echo
130+
properties:
131+
output:
132+
major: "[substring(parameters('version'), 0, 1)]"
133+
minor: "[substring(parameters('version'), 2, 1)]"
134+
patch: "[substring(parameters('version'), 4, 1)]"
135+
```
136+
137+
```bash
138+
dsc config get --file substring.example.3.dsc.config.yaml
139+
```
140+
141+
```yaml
142+
results:
143+
- name: Parse version
144+
type: Microsoft.DSC.Debug/Echo
145+
result:
146+
actualState:
147+
output:
148+
major: "3"
149+
minor: "2"
150+
patch: "1"
151+
messages: []
152+
hadErrors: false
153+
```
154+
155+
### Example 4 - Unicode and emoji support
156+
157+
This example shows that `substring()` correctly handles Unicode characters and
158+
emojis. It uses [`parameters()`][08] to retrieve the message string.
159+
160+
```yaml
161+
# substring.example.4.dsc.config.yaml
162+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
163+
parameters:
164+
message:
165+
type: string
166+
defaultValue: "Hello 🌍 World!"
167+
resources:
168+
- name: Unicode substring
169+
type: Microsoft.DSC.Debug/Echo
170+
properties:
171+
output:
172+
greeting: "[substring(parameters('message'), 0, 5)]"
173+
emoji: "[substring(parameters('message'), 6, 1)]"
174+
remainder: "[substring(parameters('message'), 8)]"
175+
```
176+
177+
```bash
178+
dsc config get --file substring.example.4.dsc.config.yaml
179+
```
180+
181+
```yaml
182+
results:
183+
- name: Unicode substring
184+
type: Microsoft.DSC.Debug/Echo
185+
result:
186+
actualState:
187+
output:
188+
greeting: Hello
189+
emoji: 🌍
190+
remainder: " World!"
191+
messages: []
192+
hadErrors: false
193+
```
194+
195+
## Parameters
196+
197+
### stringToParse
198+
199+
The original string from which the substring is extracted.
200+
201+
```yaml
202+
Type: string
203+
Required: true
204+
Position: 1
205+
```
206+
207+
### startIndex
208+
209+
The zero-based starting character position for the substring. Must be a
210+
non-negative integer and cannot exceed the length of the string.
211+
212+
```yaml
213+
Type: int
214+
Required: true
215+
Position: 2
216+
```
217+
218+
### length
219+
220+
The number of characters for the substring. Must be a non-negative integer. The
221+
start index plus length cannot exceed the length of the string. If omitted, the
222+
remainder of the string from the start position is returned.
223+
224+
```yaml
225+
Type: int
226+
Required: false
227+
Position: 3
228+
```
229+
230+
## Output
231+
232+
The `substring()` function returns a string containing the extracted portion of
233+
the original string.
234+
235+
```yaml
236+
Type: string
237+
```
238+
239+
## Exceptions
240+
241+
The `substring()` function raises errors for the following conditions:
242+
243+
- **Invalid start index**: When `startIndex` is negative
244+
- **Start index out of bounds**: When `startIndex` exceeds the string length
245+
- **Invalid length**: When `length` is negative
246+
- **Length out of bounds**: When `startIndex + length` exceeds the string
247+
length
248+
249+
## Related functions
250+
251+
- [`string()`][00] - Converts values to strings
252+
- [`concat()`][01] - Concatenates strings together
253+
- [`indexOf()`][02] - Finds the index of a substring in a string
254+
- [`lastIndexOf()`][03] - Finds the last index of a substring in a string
255+
- [`length()`][04] - Returns the length of a string or array
256+
- [`startsWith()`][05] - Checks if a string starts with a prefix
257+
- [`endsWith()`][06] - Checks if a string ends with a suffix
258+
259+
<!-- Link reference definitions -->
260+
[00]: ./string.md
261+
[01]: ./concat.md
262+
[02]: ./indexOf.md
263+
[03]: ./lastIndexOf.md
264+
[04]: ./length.md
265+
[05]: ./startsWith.md
266+
[06]: ./endsWith.md
267+
[08]: ./parameters.md

dsc/tests/dsc_functions.tests.ps1

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,56 @@ Describe 'tests for function expressions' {
660660
type: Microsoft.DSC.Debug/Echo
661661
properties:
662662
output: "$expression"
663+
"@
664+
$out = dsc -l trace config get -i $config_yaml 2>$TestDrive/error.log
665+
$LASTEXITCODE | Should -Not -Be 0
666+
$errorContent = Get-Content $TestDrive/error.log -Raw
667+
$errorContent | Should -Match ([regex]::Escape($expectedError))
668+
}
669+
670+
It 'substring function works for: <expression>' -TestCases @(
671+
@{ expression = "[substring('hello world', 6, 5)]"; expected = 'world' }
672+
@{ expression = "[substring('hello', 0, 2)]"; expected = 'he' }
673+
@{ expression = "[substring('hello', 1, 3)]"; expected = 'ell' }
674+
@{ expression = "[substring('hello', 2)]"; expected = 'llo' }
675+
@{ expression = "[substring('hello', 0)]"; expected = 'hello' }
676+
@{ expression = "[substring('hello', 5)]"; expected = '' }
677+
@{ expression = "[substring('hello', 1, 1)]"; expected = 'e' }
678+
@{ expression = "[substring('hello', 5, 0)]"; expected = '' }
679+
@{ expression = "[substring('', 0)]"; expected = '' }
680+
@{ expression = "[substring('', 0, 0)]"; expected = '' }
681+
@{ expression = "[substring('héllo', 1, 2)]"; expected = 'él' }
682+
) {
683+
param($expression, $expected)
684+
685+
$escapedExpression = $expression -replace "'", "''"
686+
$config_yaml = @"
687+
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
688+
resources:
689+
- name: Echo
690+
type: Microsoft.DSC.Debug/Echo
691+
properties:
692+
output: '$escapedExpression'
693+
"@
694+
$out = $config_yaml | dsc config get -f - | ConvertFrom-Json
695+
$out.results[0].result.actualState.output | Should -Be $expected
696+
}
697+
698+
It 'substring function error handling: <expression>' -TestCases @(
699+
@{ expression = "[substring('hello', -1, 2)]"; expectedError = 'Start index cannot be negative' }
700+
@{ expression = "[substring('hello', 1, -1)]"; expectedError = 'Length cannot be negative' }
701+
@{ expression = "[substring('hello', 10, 1)]"; expectedError = 'Start index is beyond the end of the string' }
702+
@{ expression = "[substring('hello', 2, 10)]"; expectedError = 'Length extends beyond the end of the string' }
703+
) {
704+
param($expression, $expectedError)
705+
706+
$config_yaml = @"
707+
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
708+
resources:
709+
- name: Echo
710+
type: Microsoft.DSC.Debug/Echo
711+
properties:
712+
output: `"$expression`"
663713
"@
664714
$out = dsc -l trace config get -i $config_yaml 2>$TestDrive/error.log
665715
$LASTEXITCODE | Should -Not -Be 0

dsc_lib/locales/en-us.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,16 @@ invalidOriginalValue = "First argument must be an array or string"
479479
[functions.string]
480480
description = "Converts a value to a string"
481481

482+
[functions.substring]
483+
description = "Returns a substring that starts at the specified character position and contains the specified number of characters"
484+
invoked = "substring function"
485+
startIndexNegative = "Start index cannot be negative"
486+
startIndexTooLarge = "Start index is beyond the end of the string"
487+
startIndexValueTooLarge = "Start index value too large"
488+
lengthNegative = "Length cannot be negative"
489+
lengthTooLarge = "Length extends beyond the end of the string"
490+
lengthValueTooLarge = "Length value too large"
491+
482492
[functions.sub]
483493
description = "Subtracts the second number from the first"
484494
invoked = "sub function"

dsc_lib/src/functions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub mod skip;
6060
pub mod starts_with;
6161
pub mod string;
6262
pub mod sub;
63+
pub mod substring;
6364
pub mod system_root;
6465
pub mod r#true;
6566
pub mod union;
@@ -177,6 +178,7 @@ impl FunctionDispatcher {
177178
Box::new(starts_with::StartsWith{}),
178179
Box::new(string::StringFn{}),
179180
Box::new(sub::Sub{}),
181+
Box::new(substring::Substring{}),
180182
Box::new(system_root::SystemRoot{}),
181183
Box::new(r#true::True{}),
182184
Box::new(utc_now::UtcNow{}),

0 commit comments

Comments
 (0)