Skip to content

Commit

Permalink
Allow parsing scalars and mappings into the same type
Browse files Browse the repository at this point in the history
Some YAML schemas seem to support specifying values either as a single
string, or as a dictionary.

One practical example is the `image` option in `.gitlab-ci.yml` files:

https://docs.gitlab.com/ee/ci/yaml/#image

GitLab accepts either

```yaml
test-job:
  image: ruby:3.0
```

or

```yaml
test-job:
  image:
    name: ruby:3.0
```

This change enables us to support this functionality, by calling
`fromString` only on scalar nodes; mappings are deserialized as usual.
  • Loading branch information
VPanteleev-S7 committed Jan 15, 2025
1 parent 384a064 commit 6c6a49d
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 2 deletions.
11 changes: 9 additions & 2 deletions source/configy/read.d
Original file line number Diff line number Diff line change
Expand Up @@ -718,8 +718,15 @@ package FR.Type parseField (alias FR)
}

else static if (hasFromString!(FR.Type))
return wrapConstruct(FR.Type.fromString(node.as!string), path, Location.get(node));

{
if (node.nodeID == NodeID.mapping)
return node.parseMapping!(FR)(path, defaultValue, ctx, null);
else
if (node.nodeID == NodeID.scalar)
return wrapConstruct(FR.Type.fromString(node.as!string), path, Location.get(node));
else
throw new TypeConfigException(node, "a mapping (object) or a scalar", path);
}
else static if (hasStringCtor!(FR.Type))
return wrapConstruct(FR.Type(node.as!string), path, Location.get(node));

Expand Down
30 changes: 30 additions & 0 deletions source/configy/test.d
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,36 @@ unittest
assert(v2.v2.str == "hello world");
}

/// Test calling fromString on scalars only
unittest
{
static struct Job {
static struct Image {
string name;
static Image fromString(string s) {
Image v;
v.name = s;
return v;
}
}
Image image;
}
// alias Config = Job[string]; // TODO, we should support top-level associative arrays
struct Config { Job job; }

auto c1 = parseConfigString!Config(`
job:
image: ruby:3.0
`, "/dev/null");
auto c2 = parseConfigString!Config(`
job:
image:
name: ruby:3.0
`, "/dev/null");
assert(c1.job.image.name == "ruby:3.0");
assert(c2.job.image.name == "ruby:3.0");
}

/// Don't call `opCmp` / `opEquals` as they might not be CTFEable
/// Also various tests around static arrays
unittest
Expand Down

0 comments on commit 6c6a49d

Please sign in to comment.