Skip to content

Commit 75d6f43

Browse files
committed
feat(attributes): Add a simple 'Only' helper to help with enums
This use case was recently encountered and there was no clear solution for it. Adding it in Configy seems rather simple and would probably cover other users.
1 parent c666654 commit 75d6f43

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

doc/FAQ.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,26 @@ to be validated, one can implement a `void validate() const` method which throws
282282
an exception in the event of a validation failure.
283283
The library will rethrow this `Exception` with the file/line information pointing
284284
to the section itself, and not any individual field.
285+
286+
### Limit the set of acceptable values / Use an `enum`
287+
288+
Configy uses `std.conv : to` for converting `enum`, which means they always get converted
289+
to their symbolic name. The following two types will behave the same and expect values
290+
`APAC`, `EMEA`, and `Americas` in the YAML file.
291+
```D
292+
enum Location : string {
293+
APAC = "APAC",
294+
EMEA = "EMEA",
295+
Americas = "North America",
296+
}
297+
298+
enum Location2 {
299+
APAC,
300+
EMEA,
301+
Americas,
302+
}
303+
```
304+
Symbolic names, unlike enum values, must be unique - Hence why they are not taken into account.
305+
To work around this, one may use `configy.attributes : Only`, which accepts a list of strings.
306+
`Location` would then be expressed as `Only!(["APAC", "EMEA", "North America"])` instead.
307+
It is also trivial to implement such a type if a project has specific needs.

source/configy/attributes.d

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,72 @@ public interface ConfigParser (T)
332332
/// Internal use only
333333
protected const(Context) context () const @safe pure nothrow @nogc;
334334
}
335+
336+
/*******************************************************************************
337+
338+
Specify that a field only accept a limited set of string values.
339+
340+
This is similar to how `enum` symbolic names are treated, however the `enum`
341+
symbolic names may not contain spaces or special character.
342+
343+
Params:
344+
Values = Permissible values (case sensitive)
345+
346+
*******************************************************************************/
347+
348+
public struct Only (string[] Values) {
349+
public string value;
350+
351+
alias value this;
352+
353+
public static Only fromString (scope string str) {
354+
import std.algorithm.searching : canFind;
355+
import std.exception : enforce;
356+
import std.format;
357+
358+
enforce(Values.canFind(str),
359+
"%s is not a valid value for this field, valid values are: %(%s, %)"
360+
.format(str, Values));
361+
return Only(str);
362+
}
363+
}
364+
365+
///
366+
unittest {
367+
import configy.attributes : Only, Optional;
368+
import configy.read : parseConfigString;
369+
370+
static struct CountryConfig {
371+
Only!(["France", "Malta", "South Korea"]) country;
372+
// Compose with other attributes too
373+
@Optional Only!(["citizen", "resident", "alien"]) status;
374+
}
375+
static struct Config {
376+
CountryConfig[] countries;
377+
}
378+
379+
auto conf = parseConfigString!Config(`countries:
380+
- country: France
381+
status: citizen
382+
- country: Malta
383+
- country: South Korea
384+
status: alien
385+
`, "/dev/null");
386+
387+
assert(conf.countries.length == 3);
388+
assert(conf.countries[0].country == `France`);
389+
assert(conf.countries[0].status == `citizen`);
390+
assert(conf.countries[1].country == `Malta`);
391+
assert(conf.countries[1].status is null);
392+
assert(conf.countries[2].country == `South Korea`);
393+
assert(conf.countries[2].status == `alien`);
394+
395+
import configy.exceptions : ConfigException;
396+
397+
try parseConfigString!Config(`countries:
398+
- country: France
399+
status: expatriate
400+
`, "/etc/config");
401+
catch (ConfigException exc)
402+
assert(exc.toString() == `/etc/config(2:12): countries[0].status: expatriate is not a valid value for this field, valid values are: "citizen", "resident", "alien"`);
403+
}

0 commit comments

Comments
 (0)