Skip to content

Nested internally tagged enums wrong line report #802

@soywod

Description

@soywod

Given the following nested structure (small extract from a user config):

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct Config {
    backend: BackendConfig,
}

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum BackendConfig {
    Imap(ImapConfig),
    Smtp(SmtpConfig),
}

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct ImapConfig {
    auth: ImapAuthConfig,
}

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum ImapAuthConfig {
    Password(PasswordConfig),
}

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct SmtpConfig {
    auth: SmtpAuthConfig,
}

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum SmtpAuthConfig {
    Password(PasswordConfig),
}

#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct PasswordConfig {
    cmd: String,
}

With externally tagged enums

The following TOML string is properly parsed:

backend.imap.auth.password.cmd = "pass show password"

But this structure "allows" users to declare multiple backends, which is not possible:

backend.imap.auth.password.cmd = "pass show password"
backend.smtp.auth.password.cmd = "pass show password"
TOML parse error at line 2, column 1
  |
2 | backend.imap.auth.password.cmd = "pass show password"
  | ^^^^^^^
wanted exactly 1 element, more than 1 element

This representation is too permissive, it is too misleading for users and the error message is not explicit.

With internally tagged enums

By adding a #[serde(tag = "type")], the following string can be parsed:

backend.type = "imap"
backend.auth.type = "password"
backend.auth.cmd = "pass show password"

But the following wrong string gives a bad information about the error position: it always points to the first enum:

backend.type = "imap"
backend.auth.type = "wrong" # < wrong type
backend.auth.cmd = "pass show password"
TOML parse error at line 2, column 1
  |
2 | backend.type = "imap"
  | ^^^^^^^
unknown variant `wrong`, expected `password` or `oauth2`

This representation is perfect for users, and is the one I would like to keep. But the error line seems broken.

With adjacently tagged enums

By adding a #[serde(tag = "type", content = "content")], the following string can be parsed:

backend.type = "imap"
backend.content.auth.type = "password"
backend.content.auth.content.cmd = "pass show password"

And the previous mistake is now well reported:

backend.type = "imap"
backend.content.auth.type = "wrong"
backend.content.auth.content.cmd = "pass show password"
TOML parse error at line 3, column 29
  |
3 | backend.content.auth.type = "wrong"
  |                             ^^^^^^^
unknown variant `wrong`, expected `password` or `oauth2`

This representation reports well wrong information, but is way too verbose for users.


To summarize, I would like to keep the internally tagged enum representation but the reporting seems broken. Any idea what is going on?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions