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

feat: Add a TryFrom<&str> implementation for enumerations #224

Merged
merged 2 commits into from Feb 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/completion.rs
Expand Up @@ -587,4 +587,14 @@ mod tests {
"TypeParameter"
);
}

#[test]
fn test_try_from_enum() {
use std::convert::TryInto;
assert_eq!("Text".try_into(), Ok(CompletionItemKind::TEXT));
assert_eq!(
"TypeParameter".try_into(),
Ok(CompletionItemKind::TYPE_PARAMETER)
);
}
}
65 changes: 53 additions & 12 deletions src/lib.rs
Expand Up @@ -15,20 +15,43 @@ able to parse any URI, such as `urn:isbn:0451450523`.

*/
#![allow(non_upper_case_globals)]
#![forbid(unsafe_code)]

#[forbid(unsafe_code)]
#[macro_use]
extern crate bitflags;

use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::{collections::HashMap, fmt::Debug};

use serde::{de, de::Error as Error_, Deserialize, Serialize};
use serde_json::Value;
pub use url::Url;

use std::collections::HashMap;
// Large enough to contain any enumeration name defined in this crate
type PascalCaseBuf = [u8; 32];
const fn fmt_pascal_case_const(name: &str) -> (PascalCaseBuf, usize) {
let mut buf = [0; 32];
let mut buf_i = 0;
let mut name_i = 0;
let name = name.as_bytes();
while name_i < name.len() {
let first = name[name_i];
name_i += 1;

buf[buf_i] = first;
buf_i += 1;

while name_i < name.len() {
let rest = name[name_i];
name_i += 1;
if rest == b'_' {
break;
}

use serde::de;
use serde::de::Error as Error_;
use serde_json::Value;
buf[buf_i] = rest.to_ascii_lowercase();
buf_i += 1;
}
}
(buf, buf_i)
}

fn fmt_pascal_case(f: &mut std::fmt::Formatter<'_>, name: &str) -> std::fmt::Result {
for word in name.split('_') {
Expand All @@ -43,7 +66,7 @@ fn fmt_pascal_case(f: &mut std::fmt::Formatter<'_>, name: &str) -> std::fmt::Res
}

macro_rules! lsp_enum {
(impl $typ: ty { $( $(#[$attr:meta])* pub const $name: ident : $enum_type: ty = $value: expr; )* }) => {
(impl $typ: ident { $( $(#[$attr:meta])* pub const $name: ident : $enum_type: ty = $value: expr; )* }) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure why, also the other : ty is still here

impl $typ {
$(
$(#[$attr])*
Expand All @@ -61,6 +84,23 @@ macro_rules! lsp_enum {
}
}
}

impl std::convert::TryFrom<&str> for $typ {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match () {
$(
_ if {
const X: (crate::PascalCaseBuf, usize) = crate::fmt_pascal_case_const(stringify!($name));
let (buf, len) = X;
&buf[..len] == value.as_bytes()
Copy link
Contributor

Choose a reason for hiding this comment

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

pretty nice - this commit reduces the instruction count of the CompletionItemKind::try_from() from 4000 to just above 300 (in release mode, debug has around 10x more). Now let's hope that future rust will do this without the explicit const :)

} => Ok(Self::$name),
)*
_ => Err("unknown enum variant"),
}
}
}

}
}

Expand Down Expand Up @@ -668,10 +708,10 @@ pub struct ConfigurationItem {
}

mod url_map {
use super::*;

use std::fmt;

use super::*;

pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<Option<HashMap<Url, Vec<TextEdit>>>, D::Error>
Expand Down Expand Up @@ -2415,9 +2455,10 @@ impl SymbolTag {

#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};

use super::*;

pub(crate) fn test_serialization<SER>(ms: &SER, expected: &str)
where
SER: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug,
Expand Down