Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to deserialize enums from their string representation #223

Closed

Conversation

krobelus
Copy link
Contributor

Enums are normally serialized as integers, but in some parts of
kak-lsp we like to use strings.
I'm working on a feature to select specific kinds of symbols.
The editor support script wants to send something like ["Function",
"Method"], which I want to deserialize into SymbolKinds.

Add an implementation of TryFrom<&String> for enums. This is the dual
of the Debug.

An alternative solution would be to allow clients to enumerate all
enum variants, is there a way to do that?

I initially tried to implement TryFrom<&str> but got weird errors
when the input string does not have static lifetime, like

SymbolKind::try_from(&("Function".to_string()))

   Compiling lsp-types v0.91.1 (/home/johannes/git/lsp-types)
   Compiling kak-lsp v11.0.1-snapshot (/home/johannes/git/kak-lsp)
error[E0277]: the trait bound `lsp_types::SymbolKind: From<&std::string::String>` is not satisfied
   --> src/language_features/document_symbol.rs:169:25
    |
169 |         SymbolKind::try_from(&("Function".to_string()))
    |         ^^^^^^^^^^^^^^^^^^^^ the trait `From<&std::string::String>` is not implemented for `lsp_types::SymbolKind`
    |
    = note: required because of the requirements on the impl of `Into<lsp_types::SymbolKind>` for `&std::string::String`
    = note: required because of the requirements on the impl of `TryFrom<&std::string::String>` for `lsp_types::SymbolKind`
note: required by `try_from`
   --> /home/johannes/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:477:5
    |
477 |     fn try_from(value: T) -> Result<Self, Self::Error>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `lsp_types::SymbolKind: From<&std::string::String>` is not satisfied
   --> src/language_features/document_symbol.rs:169:25
    |
169 |         SymbolKind::try_from(&("Function".to_string()))
    |         ^^^^^^^^^^^^^^^^^^^^ the trait `From<&std::string::String>` is not implemented for `lsp_types::SymbolKind`
    |
    = note: required because of the requirements on the impl of `Into<lsp_types::SymbolKind>` for `&std::string::String`
    = note: required because of the requirements on the impl of `TryFrom<&std::string::String>` for `lsp_types::SymbolKind`

Enums are normally serialized as integers, but in some parts of
kak-lsp we like to use strings.
I'm working on a feature to select specific kinds of symbols.
The editor support script wants to send something like ["Function",
"Method"], which I want to deserialize into SymbolKinds.

Add an implementation of TryFrom<&String> for enums. This is the dual
of the Debug.

An alternative solution would be to allow clients to enumerate all
enum variants, is there a way to do that?

I initially tried to implement TryFrom<&str> but got weird errors
when the input string does not have static lifetime, like

	SymbolKind::try_from(&("Function".to_string()))

   Compiling lsp-types v0.91.1 (/home/johannes/git/lsp-types)
   Compiling kak-lsp v11.0.1-snapshot (/home/johannes/git/kak-lsp)
error[E0277]: the trait bound `lsp_types::SymbolKind: From<&std::string::String>` is not satisfied
   --> src/language_features/document_symbol.rs:169:25
    |
169 |         SymbolKind::try_from(&("Function".to_string()))
    |         ^^^^^^^^^^^^^^^^^^^^ the trait `From<&std::string::String>` is not implemented for `lsp_types::SymbolKind`
    |
    = note: required because of the requirements on the impl of `Into<lsp_types::SymbolKind>` for `&std::string::String`
    = note: required because of the requirements on the impl of `TryFrom<&std::string::String>` for `lsp_types::SymbolKind`
note: required by `try_from`
   --> /home/johannes/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:477:5
    |
477 |     fn try_from(value: T) -> Result<Self, Self::Error>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `lsp_types::SymbolKind: From<&std::string::String>` is not satisfied
   --> src/language_features/document_symbol.rs:169:25
    |
169 |         SymbolKind::try_from(&("Function".to_string()))
    |         ^^^^^^^^^^^^^^^^^^^^ the trait `From<&std::string::String>` is not implemented for `lsp_types::SymbolKind`
    |
    = note: required because of the requirements on the impl of `Into<lsp_types::SymbolKind>` for `&std::string::String`
    = note: required because of the requirements on the impl of `TryFrom<&std::string::String>` for `lsp_types::SymbolKind`
Comment on lines +65 to +75
impl std::convert::TryFrom<&String> for $typ {
type Error = &'static str;
fn try_from(value: &String) -> Result<Self, Self::Error> {
match value {
$(
_ if *value == format!("{:?}", Self::$name) => Ok(Self::$name),
)*
_ => Err("unknown enum variant"),
}
}
}
Copy link
Member

@Marwes Marwes Nov 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
impl std::convert::TryFrom<&String> for $typ {
type Error = &'static str;
fn try_from(value: &String) -> Result<Self, Self::Error> {
match value {
$(
_ if *value == format!("{:?}", Self::$name) => Ok(Self::$name),
)*
_ => Err("unknown enum variant"),
}
}
}
impl std::convert::TryFrom<&str> for $typ {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
$(
stringify!($name) => Ok(Self::$name),
)*
_ => Err("unknown enum variant"),
}
}
}

No need to allocate for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I was thinking to add a &'static str member to avoid the allocation.

Your suggestion is even more natural - though it requires FUNCTION as input instead of Function.
I don't have a strong opinion on which one is better. I guess we could keep symmetry with Debug while still only allocating once (converting value). Not sure what's better long-term.

@Marwes
Copy link
Member

Marwes commented Nov 21, 2021

&str vs &String should give the same kind of lifetime errors if you are getting them so I am not sure what is going on there. Would be good with a test to sanity check that it works as well.

@sidkshatriya
Copy link
Contributor

sidkshatriya commented Nov 21, 2021

The errors you get when you define impl TryFrom<&str> for SymbolKind are not due to the TryFrom code you wrote I think but due to how you are invoking try_from().

This will work (here "Function" is a &'static str)

let res = SymbolKind::try_from("Function");

So will this:

// You won't really write code like this but this is just to show that if you
// passed a &str to try_from it would work
// Mostly you should already have a &str, perhaps from a function parameter...
let fun = "Function".to_string();
let res = SymbolKind::try_from(fun.as_str());

But as you report, this does not:

let res = SymbolKind::try_from(&("Function".to_string()));

Rust is just not being smart here to use the Deref trait to convert the &String -> &str

But if you want to do it as a one liner, this will work:

// This approach is pointless but here to show it can be done... 
let res = SymbolKind::try_from("Function".to_string().deref())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants