diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2b666c9..ba6ea251 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: minimum-version-check: strategy: matrix: - rust_toolchain: [1.51.0] + rust_toolchain: [1.56.0] os: [ubuntu-latest, macOS-latest, windows-latest] name: minimum version check using Rust ${{ matrix.rust_toolchain }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} diff --git a/README.md b/README.md index 246cf6c1..81525bba 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A Rust implementation of the Concise data definition language (CDDL). CDDL is an This crate includes a handwritten parser and lexer for CDDL, and its development has been heavily inspired by the techniques outlined in Thorsten Ball's book ["Writing An Interpretor In Go"](https://interpreterbook.com/). The AST has been built to closely match the rules defined by the ABNF grammar in [Appendix B.](https://tools.ietf.org/html/rfc8610#appendix-B) of the spec. All CDDL must use UTF-8 for its encoding per the spec. -This crate supports validation of both CBOR and JSON data structures. An extremely basic REPL is included as well. This crate's minimum supported Rust version (MSRV) is 1.51.0. +This crate supports validation of both CBOR and JSON data structures. An extremely basic REPL is included as well. This crate's minimum supported Rust version (MSRV) is 1.56.0. Also bundled into this repository is a basic language server implementation and extension for Visual Studio Code for editing CDDL. The implementation is backed by the compiled WebAssembly target included in this crate. @@ -100,7 +100,7 @@ docker run -i --rm -v $PWD:/data -w /data ghcr.io/anweiss/cddl-cli:0.9.0-beta.1 ## Website -You can also find a simple RFC 8610 conformance tool at [https://cddl.anweiss.tech](https://cddl.anweiss.tech). This same codebase has been compiled for use in the browser via WebAssembly. The web tool does not yet support the additional control operators defined by [RFC 9165](https://datatracker.ietf.org/doc/html/rfc9165). +You can also find a simple RFC 8610 conformance tool at [https://cddl.anweiss.tech](https://cddl.anweiss.tech). This same codebase has been compiled for use in the browser via WebAssembly. ## Visual Studio Code extension @@ -400,3 +400,4 @@ Both JSON and CBOR validation are dependent on their respective heap allocated ` Below are some known projects that leverage this crate: - [https://github.com/Emurgo/cddl-codegen](https://github.com/Emurgo/cddl-codegen) +- [https://github.com/p2panda/p2panda](https://github.com/p2panda/p2panda) diff --git a/src/ast.rs b/src/ast.rs index 68599a24..334f9eed 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1514,10 +1514,10 @@ impl<'a> From> for Type2<'a> { let span = (0, 0, 0); match rv { - RangeValue::IDENT(ident) => Type2::Typename { + RangeValue::IDENT(ident, socket) => Type2::Typename { ident: Identifier { - ident: ident.0, - socket: ident.1, + ident, + socket, #[cfg(feature = "ast-span")] span, }, diff --git a/src/lexer.rs b/src/lexer.rs index 4ced60e0..39455a19 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -485,19 +485,19 @@ impl<'a> Lexer<'a> { Ok(( self.position, - Token::TAG((Some(t as u8), Some(self.read_number(idx)?.1))), + Token::TAG(Some(t as u8), Some(self.read_number(idx)?.1)), )) } _ => { self.position.range = (token_offset, self.position.index + 1); - Ok((self.position, Token::TAG((Some(t as u8), None)))) + Ok((self.position, Token::TAG(Some(t as u8), None))) } } } _ => { self.position.range = (token_offset, self.position.index + 1); - Ok((self.position, Token::TAG((None, None)))) + Ok((self.position, Token::TAG(None, None))) } }, (_, '\'') => { @@ -1082,25 +1082,25 @@ mod tests { "; this is another comment", ), (NEWLINE, ""), - (IDENT(("mynumber".into(), None)), "mynumber"), + (IDENT("mynumber".into(), None), "mynumber"), (ASSIGN, "="), (VALUE(Value::FLOAT(10.5)), "10.5"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("mytag".into(), None)), "mytag"), + (IDENT("mytag".into(), None), "mytag"), (ASSIGN, "="), - (TAG((Some(6), Some(1234))), "#6.1234"), + (TAG(Some(6), Some(1234)), "#6.1234"), (LPAREN, "("), (TSTR, "tstr"), (RPAREN, ")"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("myfirstrule".into(), None)), "myfirstrule"), + (IDENT("myfirstrule".into(), None), "myfirstrule"), (ASSIGN, "="), (VALUE(Value::TEXT("myotherrule".into())), "\"myotherrule\""), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("mybytestring".into(), None)), "mybytestring"), + (IDENT("mybytestring".into(), None), "mybytestring"), (ASSIGN, "="), ( VALUE(Value::BYTE(ByteValue::UTF8(b"hello there".as_ref().into()))), @@ -1108,7 +1108,7 @@ mod tests { ), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("mybase16rule".into(), None)), "mybase16rule"), + (IDENT("mybase16rule".into(), None), "mybase16rule"), (ASSIGN, "="), ( VALUE(Value::BYTE(ByteValue::B16( @@ -1118,7 +1118,7 @@ mod tests { ), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("mybase64rule".into(), None)), "mybase64rule"), + (IDENT("mybase64rule".into(), None), "mybase64rule"), (ASSIGN, "="), ( VALUE(Value::BYTE(ByteValue::B64( @@ -1128,47 +1128,47 @@ mod tests { ), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("mysecondrule".into(), None)), "mysecondrule"), + (IDENT("mysecondrule".into(), None), "mysecondrule"), (ASSIGN, "="), - (IDENT(("mynumber".into(), None)), "mynumber"), + (IDENT("mynumber".into(), None), "mynumber"), (RANGEOP(true), ".."), (VALUE(Value::FLOAT(100.5)), "100.5"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("myintrule".into(), None)), "myintrule"), + (IDENT("myintrule".into(), None), "myintrule"), (ASSIGN, "="), (VALUE(Value::INT(-10)), "-10"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("mysignedfloat".into(), None)), "mysignedfloat"), + (IDENT("mysignedfloat".into(), None), "mysignedfloat"), (ASSIGN, "="), (VALUE(Value::FLOAT(-10.5)), "-10.5"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("myintrange".into(), None)), "myintrange"), + (IDENT("myintrange".into(), None), "myintrange"), (ASSIGN, "="), (VALUE(Value::INT(-10)), "-10"), (RANGEOP(true), ".."), (VALUE(Value::UINT(10)), "10"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("mycontrol".into(), None)), "mycontrol"), + (IDENT("mycontrol".into(), None), "mycontrol"), (ASSIGN, "="), - (IDENT(("mynumber".into(), None)), "mynumber"), + (IDENT("mynumber".into(), None), "mynumber"), (GT, ".gt"), (VALUE(Value::UINT(0)), "0"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("@terminal-color".into(), None)), "@terminal-color"), + (IDENT("@terminal-color".into(), None), "@terminal-color"), (ASSIGN, "="), - (IDENT(("basecolors".into(), None)), "basecolors"), + (IDENT("basecolors".into(), None), "basecolors"), (TCHOICE, "/"), - (IDENT(("othercolors".into(), None)), "othercolors"), + (IDENT("othercolors".into(), None), "othercolors"), (COMMENT(" an inline comment".into()), "; an inline comment"), (NEWLINE, ""), - (IDENT(("messages".into(), None)), "messages"), + (IDENT("messages".into(), None), "messages"), (ASSIGN, "="), - (IDENT(("message".into(), None)), "message"), + (IDENT("message".into(), None), "message"), (LANGLEBRACKET, "<"), (VALUE(Value::TEXT("reboot".into())), "\"reboot\""), (COMMA, ","), @@ -1176,18 +1176,18 @@ mod tests { (RANGLEBRACKET, ">"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("address".into(), None)), "address"), + (IDENT("address".into(), None), "address"), (ASSIGN, "="), (LBRACE, "{"), - (IDENT(("delivery".into(), None)), "delivery"), + (IDENT("delivery".into(), None), "delivery"), (RBRACE, "}"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("delivery".into(), None)), "delivery"), + (IDENT("delivery".into(), None), "delivery"), (ASSIGN, "="), (LPAREN, "("), (NEWLINE, ""), - (IDENT(("street".into(), None)), "street"), + (IDENT("street".into(), None), "street"), (COLON, ":"), (TSTR, "tstr"), (COMMA, ","), @@ -1197,32 +1197,32 @@ mod tests { (ARROWMAP, "=>"), (UINT, "uint"), (COMMA, ","), - (IDENT(("city".into(), None)), "city"), + (IDENT("city".into(), None), "city"), (GCHOICE, "//"), (NEWLINE, ""), - (IDENT(("po-box".into(), None)), "po-box"), + (IDENT("po-box".into(), None), "po-box"), (COLON, ":"), (UINT, "uint"), (COMMA, ","), - (IDENT(("city".into(), None)), "city"), + (IDENT("city".into(), None), "city"), (GCHOICE, "//"), (NEWLINE, ""), - (IDENT(("per-pickup".into(), None)), "per-pickup"), + (IDENT("per-pickup".into(), None), "per-pickup"), (COLON, ":"), (TRUE, "true"), (NEWLINE, ""), (RPAREN, ")"), (NEWLINE, ""), (NEWLINE, ""), - (IDENT(("city".into(), None)), "city"), + (IDENT("city".into(), None), "city"), (ASSIGN, "="), (LPAREN, "("), (NEWLINE, ""), - (IDENT(("name".into(), None)), "name"), + (IDENT("name".into(), None), "name"), (COLON, ":"), (TSTR, "tstr"), (NEWLINE, ""), - (IDENT(("zip-code".into(), None)), "zip-code"), + (IDENT("zip-code".into(), None), "zip-code"), (COLON, ":"), (UINT, "uint"), (NEWLINE, ""), @@ -1230,7 +1230,7 @@ mod tests { (ASTERISK, "*"), (VALUE(Value::UINT(3)), "3"), ( - IDENT(("tcp-option".into(), Some(SocketPlug::GROUP))), + IDENT("tcp-option".into(), Some(SocketPlug::GROUP)), "$$tcp-option", ), (COMMA, ","), diff --git a/src/lib.rs b/src/lib.rs index 8fd6a7e4..09fd5e6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,7 +30,7 @@ //! //! This crate supports validation of both CBOR and JSON data structures. An //! extremely basic REPL is included as well. This crate's minimum supported -//! Rust version (MSRV) is 1.51.0. +//! Rust version (MSRV) is 1.56.0. //! //! Also bundled into this repository is a basic language server implementation //! and extension for Visual Studio Code for editing CDDL. The implementation is @@ -136,7 +136,7 @@ //! or using Docker: //! //! ```sh -//! docker run -i --rm -v $PWD:/cddl -w /cddl ghcr.io/anweiss/cddl-cli:latest validate --cddl reputon.cddl --stdin < reputon.json +//! docker run -i --rm -v $PWD:/data -w /data ghcr.io/anweiss/cddl-cli:0.9.0-beta.1 validate --cddl reputon.cddl --stdin < reputon.json //! ``` //! //! ## Website @@ -217,9 +217,9 @@ //! //! **`--feature additional-controls`** //! -//! Enable validation support for the additional control operators proposed in -//! [https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cddl-control-05](https://datatracker.ietf.org/doc/html/draft-ietf-cbor-cddl-control-05). -//! Enabled by default. +//! Enable validation support for the additional control operators defined in +//! [RFC 9165](https://datatracker.ietf.org/doc/html/rfc9165). Enabled by +//! default. //! //! ### Parsing CDDL //! @@ -406,7 +406,7 @@ //! //! let json = r#""v""#; //! -//! #[cfg(feature = "additional-controls")] +//! #[cfg(not(feature = "additional-controls"))] //! assert!(validate_json_from_str(cddl, json, Some(&["json"])).is_ok()) //! ``` //! @@ -492,7 +492,6 @@ //! //! let cbor = b"\x02"; //! -//! #[cfg(feature = "additional-controls")] //! assert!(validate_cbor_from_slice(cddl, cbor, Some(&["cbor"])).is_ok()) //! ``` //! @@ -520,6 +519,7 @@ //! Below are some known projects that leverage this crate: //! //! - [https://github.com/Emurgo/cddl-codegen](https://github.com/Emurgo/cddl-codegen) +//! - [https://github.com/p2panda/p2panda](https://github.com/p2panda/p2panda) //! #![allow(dead_code)] diff --git a/src/parser.rs b/src/parser.rs index 90c2135c..2a27111c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -280,7 +280,7 @@ where while !is_possible_rule { self.next_token()?; - if let Token::IDENT(_) = self.cur_token { + if let Token::IDENT(..) = self.cur_token { match self.peek_token { Token::ASSIGN | Token::TCHOICEALT | Token::GCHOICEALT => is_possible_rule = true, _ => continue, @@ -450,7 +450,7 @@ where let begin_rule_col = self.lexer_position.column; let ident = match &self.cur_token { - Token::IDENT(i) => self.identifier_from_ident_token(*i), + Token::IDENT(i, s) => self.identifier_from_ident_token(*i, *s), _ => { #[cfg(feature = "ast-span")] { @@ -828,8 +828,8 @@ where self.advance_newline()?; match &self.cur_token { - Token::IDENT(ident) => { - let param = self.identifier_from_ident_token(*ident); + Token::IDENT(ident, socket) => { + let param = self.identifier_from_ident_token(*ident, *socket); self.next_token()?; @@ -1273,7 +1273,7 @@ where } // typename [genericarg] - Token::IDENT(ident) => { + Token::IDENT(ident, socket) => { #[cfg(feature = "ast-span")] let begin_type2_range = self.lexer_position.range.0; #[cfg(feature = "ast-span")] @@ -1281,7 +1281,7 @@ where // optional genericarg detected if self.peek_token_is(&Token::LANGLEBRACKET) { - let ident = self.identifier_from_ident_token(*ident); + let ident = self.identifier_from_ident_token(*ident, *socket); let ga = self.parse_genericargs()?; #[cfg(feature = "ast-span")] @@ -1302,7 +1302,7 @@ where } Ok(Type2::Typename { - ident: self.identifier_from_ident_token(*ident), + ident: self.identifier_from_ident_token(*ident, *socket), generic_args: None, #[cfg(feature = "ast-span")] span: ( @@ -1462,9 +1462,9 @@ where self.advance_newline()?; let ident = if let Some(ident) = self.cur_token.in_standard_prelude() { - Some(self.identifier_from_ident_token((ident, None))) - } else if let Token::IDENT(ident) = &self.cur_token { - Some(self.identifier_from_ident_token(*ident)) + Some(self.identifier_from_ident_token(ident, None)) + } else if let Token::IDENT(ident, socket) = &self.cur_token { + Some(self.identifier_from_ident_token(*ident, *socket)) } else { None }; @@ -1549,8 +1549,8 @@ where ), }) } - Token::IDENT(ident) => { - let ident = self.identifier_from_ident_token(*ident); + Token::IDENT(ident, socket) => { + let ident = self.identifier_from_ident_token(*ident, *socket); if self.peek_token_is(&Token::LANGLEBRACKET) { self.next_token()?; @@ -1607,13 +1607,13 @@ where // Tag::MAJORTYPE(mt) => Ok(Type2::DataMajorType(*mt)), // Tag::ANY => Ok(Type2::Any), // }, - Token::TAG(t) => { + Token::TAG(mt, constraint) => { #[cfg(feature = "ast-span")] let begin_type2_range = self.lexer_position.range.0; #[cfg(feature = "ast-span")] let begin_type2_line = self.lexer_position.line; - match *t { + match (*mt, *constraint) { // Tagged data item containing the given type as the tagged value (Some(6), tag) => { self.next_token()?; @@ -1695,7 +1695,7 @@ where match self.cur_token.in_standard_prelude() { Some(s) => { - let ident = self.identifier_from_ident_token((s, None)); + let ident = self.identifier_from_ident_token(s, None); #[cfg(feature = "ast-span")] { self.parser_position.range = self.lexer_position.range; @@ -1890,7 +1890,7 @@ where && !self.peek_token_is(&Token::COLON) && !self.peek_token_is(&Token::ARROWMAP) && !self.cur_token_is(Token::EOF) - && !matches!(self.cur_token, Token::IDENT(_)) + && !matches!(self.cur_token, Token::IDENT(..)) { #[cfg(feature = "ast-span")] { @@ -2400,7 +2400,8 @@ where fn parse_memberkey_from_ident( &mut self, is_optional: bool, - ident: (&'a str, Option), + ident: &'a str, + socket: Option, #[cfg(feature = "ast-span")] begin_memberkey_range: usize, #[cfg(feature = "ast-span")] begin_memberkey_line: usize, ) -> Result>> { @@ -2421,9 +2422,9 @@ where let end_t1_range = self.lexer_position.range.1; #[cfg(feature = "ast-span")] - let mut ident = self.identifier_from_ident_token((ident.0, ident.1)); + let mut ident = self.identifier_from_ident_token(ident, socket); #[cfg(not(feature = "ast-span"))] - let ident = self.identifier_from_ident_token((ident.0, ident.1)); + let ident = self.identifier_from_ident_token(ident, socket); #[cfg(feature = "ast-span")] { ident.span = (begin_memberkey_range, end_t1_range, begin_memberkey_line); @@ -2586,7 +2587,8 @@ where if let Some(t) = self.cur_token.in_standard_prelude() { return self.parse_memberkey_from_ident( is_optional, - (t, None), + t, + None, #[cfg(feature = "ast-span")] begin_memberkey_range, #[cfg(feature = "ast-span")] @@ -2595,12 +2597,14 @@ where } match &self.cur_token { - Token::IDENT(ident) => { + Token::IDENT(ident, socket) => { let ident = *ident; + let socket = *socket; self.parse_memberkey_from_ident( is_optional, ident, + socket, #[cfg(feature = "ast-span")] begin_memberkey_range, #[cfg(feature = "ast-span")] @@ -3297,11 +3301,12 @@ where /// Create `ast::Identifier` from `Token::IDENT(ident)` fn identifier_from_ident_token( &self, - ident: (&'a str, Option), + ident: &'a str, + socket: Option, ) -> Identifier<'a> { Identifier { - ident: ident.0, - socket: ident.1, + ident, + socket, #[cfg(feature = "ast-span")] span: ( self.lexer_position.range.0, diff --git a/src/token.rs b/src/token.rs index daa58433..bc90bfd0 100644 --- a/src/token.rs +++ b/src/token.rs @@ -17,12 +17,22 @@ pub enum Token<'a> { /// End of file EOF, - /// Identifier with optional `SocketPlug` - IDENT((&'a str, Option)), + /// Identifier + IDENT( + /// Identifier + &'a str, + /// Socket/plug + Option, + ), /// Value VALUE(Value<'a>), /// CBOR tag '#' - TAG((Option, Option)), + TAG( + /// Major type + Option, + /// Optional constraint + Option, + ), // Operators /// Assignment operator '=' @@ -61,9 +71,15 @@ pub enum Token<'a> { /// Range operator. Inclusive '..' if true, otherwise exclusive '...'s RANGEOP(bool), - /// Range tuple with lower bound, upper bound, and bool indicating whether or - /// not the range is inclusive - RANGE((RangeValue<'a>, RangeValue<'a>, bool)), + /// Range + RANGE( + /// Lower bound + RangeValue<'a>, + /// Upper bound + RangeValue<'a>, + /// Inclusive + bool, + ), /// Left opening parend LPAREN, @@ -116,34 +132,22 @@ pub enum Token<'a> { /// (PCREs). See PCRE, #[cfg(feature = "additional-controls")] - /// .cat control operator - /// Proposed control extension for string concatenation. See - /// . + /// .cat control operator (rfc 9165) CAT, #[cfg(feature = "additional-controls")] - /// .det control operator - /// Proposed control extension for string concatenation with dedenting. See - /// . + /// .det control operator (rfc 9165) DET, #[cfg(feature = "additional-controls")] - /// .plus control operator - /// Proposed control extension for numeric addition. See - /// . + /// .plus control operator (rfc 9165) PLUS, #[cfg(feature = "additional-controls")] - /// .abnf control operator - /// Proposed control extension for embedded ABNF as UTF-8. See - /// + /// .abnf control operator (rfc 9165) ABNF, #[cfg(feature = "additional-controls")] - /// .abnfb control operator - /// Proposed control extension for embedded ABNF as a sequence of bytes. See - /// + /// .abnfb control operator (rfc 9165) ABNFB, #[cfg(feature = "additional-controls")] - /// .feature control operator - /// Proposed control extension for features. See - /// + /// .feature control operator (rfc 9165) FEATURE, /// group to choice enumeration '&' @@ -296,8 +300,13 @@ impl<'a> Token<'a> { /// Range value #[derive(Debug, PartialEq, Clone)] pub enum RangeValue<'a> { - /// Identifier with optional socket/plug prefix - IDENT((&'a str, Option)), + /// Identifier + IDENT( + /// Identifier + &'a str, + /// Socket/plug + Option, + ), /// Integer INT(isize), /// Unsigned integer @@ -311,7 +320,7 @@ impl<'a> TryFrom> for RangeValue<'a> { fn try_from(t: Token<'a>) -> Result { match t { - Token::IDENT(ident) => Ok(RangeValue::IDENT(ident)), + Token::IDENT(ident, socket) => Ok(RangeValue::IDENT(ident, socket)), Token::VALUE(value) => match value { Value::INT(i) => Ok(RangeValue::INT(i)), Value::UINT(ui) => Ok(RangeValue::UINT(ui)), @@ -337,7 +346,7 @@ impl<'a> RangeValue<'a> { impl<'a> fmt::Display for RangeValue<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - RangeValue::IDENT(ident) => write!(f, "{}", ident.0), + RangeValue::IDENT(ident, _) => write!(f, "{}", ident), RangeValue::INT(i) => write!(f, "{}", i), RangeValue::UINT(i) => write!(f, "{}", i), RangeValue::FLOAT(fl) => write!(f, "{}", fl), @@ -469,7 +478,7 @@ impl<'a> fmt::Display for SocketPlug { impl<'a> fmt::Display for Token<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Token::IDENT((ident, socket_plug)) => { + Token::IDENT(ident, socket_plug) => { if let Some(sp) = socket_plug { return write!(f, "{}{}", sp, ident); } @@ -542,9 +551,9 @@ impl<'a> fmt::Display for Token<'a> { write!(f, "...") } } - Token::RANGE((l, u, i)) => match l { - RangeValue::IDENT(_) if *i => write!(f, "{} .. {}", l, u), - RangeValue::IDENT(_) => write!(f, "{} ... {}", l, u), + Token::RANGE(l, u, i) => match l { + RangeValue::IDENT(..) if *i => write!(f, "{} .. {}", l, u), + RangeValue::IDENT(..) => write!(f, "{} ... {}", l, u), _ => { if *i { write!(f, "{}..{}", l, u) @@ -553,7 +562,7 @@ impl<'a> fmt::Display for Token<'a> { } } }, - Token::TAG((mt, tag)) => { + Token::TAG(mt, tag) => { if let Some(m) = mt { if let Some(t) = tag { return write!(f, "#{}.{}", m, t); @@ -721,15 +730,15 @@ pub fn lookup_ident(ident: &str) -> Token { if c == '$' { if let Some(c) = ident.chars().nth(1) { if c == '$' { - return Token::IDENT((&ident[2..], Some(SocketPlug::GROUP))); + return Token::IDENT(&ident[2..], Some(SocketPlug::GROUP)); } } - return Token::IDENT((&ident[1..], Some(SocketPlug::TYPE))); + return Token::IDENT(&ident[1..], Some(SocketPlug::TYPE)); } } - Token::IDENT((ident, None)) + Token::IDENT(ident, None) } } }