diff --git a/src/completion.rs b/src/completion.rs index c2d2c02..5f9277a 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -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) + ); + } } diff --git a/src/lib.rs b/src/lib.rs index 6f690bb..12b22b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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('_') { @@ -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; )* }) => { impl $typ { $( $(#[$attr])* @@ -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 { + match () { + $( + _ if { + const X: (crate::PascalCaseBuf, usize) = crate::fmt_pascal_case_const(stringify!($name)); + let (buf, len) = X; + &buf[..len] == value.as_bytes() + } => Ok(Self::$name), + )* + _ => Err("unknown enum variant"), + } + } + } + } } @@ -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>>, D::Error> @@ -2415,9 +2455,10 @@ impl SymbolTag { #[cfg(test)] mod tests { - use super::*; use serde::{Deserialize, Serialize}; + use super::*; + pub(crate) fn test_serialization(ms: &SER, expected: &str) where SER: Serialize + for<'de> Deserialize<'de> + PartialEq + std::fmt::Debug,