diff --git a/CHANGELOG.md b/CHANGELOG.md index a644879..9349186 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.1.5 (2020-10-13) + +* Support parsing `dyn Trait`. ([#9], [#10]) + +[#9]: https://github.com/azriel91/tynm/issues/9 +[#10]: https://github.com/azriel91/tynm/pulls/10 + ## 0.1.4 (2020-03-07) * Don't overflow when segment count exceeds `usize::MAX`. ([#7]) diff --git a/Cargo.toml b/Cargo.toml index fae6b7d..7878c23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tynm" -version = "0.1.4" +version = "0.1.5" authors = ["Azriel Hoh "] edition = "2018" description = "Returns type names in shorter form." diff --git a/README.md b/README.md index 00282da..f934981 100644 --- a/README.md +++ b/README.md @@ -8,35 +8,26 @@ Returns type names with a specifiable number of module segments as a `String`. ```rust -// === std library === // -assert_eq!( - std::any::type_name::>(), - "core::option::Option", -); - -// === tynm === // -// Simple type name: -assert_eq!(tynm::type_name::>(), "Option",); - -// Type name with 1 module segment, starting from the most significant module. -assert_eq!( - tynm::type_namem::>(1), - "core::..::Option", -); - -// Type name with 1 module segment, starting from the least significant module. +#[rustfmt::skip] assert_eq!( - tynm::type_namen::>(1), - "..::option::Option<..::string::String>", + std::any::type_name::>(), "core::option::Option" ); -// Type name with 1 module segment from both the most and least significant modules. #[rustfmt::skip] -mod rust_out { pub mod two { pub mod three { pub struct Struct; } } } -assert_eq!( - tynm::type_namemn::(1, 1), - "rust_out::..::three::Struct", -); +let tuples = vec![ + (tynm::type_name::>(), "Option"), + (tynm::type_namem::>(1), "core::..::Option"), + (tynm::type_namen::>(1), "..::option::Option<..::string::String>"), + // 1 segment from most and least significant modules. + (tynm::type_namemn::(1, 1), "rust_out::..::three::Struct"), + // traits + (tynm::type_name::(), "dyn Debug"), +]; + +tuples + .iter() + .for_each(|(left, right)| assert_eq!(left, right)); + ``` ## Motivation diff --git a/src/lib.rs b/src/lib.rs index 9ac869f..f9be721 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,35 +3,28 @@ //! Returns type names with a specifiable number of module segments as a `String`. //! //! ```rust -//! // === std library === // +//! #[rustfmt::skip] //! assert_eq!( -//! std::any::type_name::>(), -//! "core::option::Option", +//! std::any::type_name::>(), "core::option::Option" //! ); //! -//! // === tynm === // -//! // Simple type name: -//! assert_eq!(tynm::type_name::>(), "Option",); +//! #[rustfmt::skip] +//! let tuples = vec![ +//! (tynm::type_name::>(), "Option"), +//! (tynm::type_namem::>(1), "core::..::Option"), +//! (tynm::type_namen::>(1), "..::option::Option<..::string::String>"), +//! // 1 segment from most and least significant modules. +//! (tynm::type_namemn::(1, 1), "rust_out::..::three::Struct"), +//! // traits +//! (tynm::type_name::(), "dyn Debug"), +//! ]; //! -//! // Type name with 1 module segment, starting from the most significant module. -//! assert_eq!( -//! tynm::type_namem::>(1), -//! "core::..::Option", -//! ); +//! tuples +//! .iter() +//! .for_each(|(left, right)| assert_eq!(left, right)); //! -//! // Type name with 1 module segment, starting from the least significant module. -//! assert_eq!( -//! tynm::type_namen::>(1), -//! "..::option::Option<..::string::String>", -//! ); -//! -//! // Type name with 1 module segment from both the most and least significant modules. -//! #[rustfmt::skip] -//! mod rust_out { pub mod two { pub mod three { pub struct Struct; } } } -//! assert_eq!( -//! tynm::type_namemn::(1, 1), -//! "rust_out::..::three::Struct", -//! ); +//! # #[rustfmt::skip] +//! # mod rust_out { pub mod two { pub mod three { pub struct Struct; } } } //! ``` //! //! # Motivation @@ -61,7 +54,10 @@ mod types; /// ```rust /// assert_eq!(tynm::type_name::>(), "Option",); /// ``` -pub fn type_name() -> String { +pub fn type_name() -> String +where + T: ?Sized, +{ type_namemn::(0, 0) } @@ -83,7 +79,10 @@ pub fn type_name() -> String { /// "core::..::Option", /// ); /// ``` -pub fn type_namem(m: usize) -> String { +pub fn type_namem(m: usize) -> String +where + T: ?Sized, +{ type_namemn::(m, 0) } @@ -105,7 +104,10 @@ pub fn type_namem(m: usize) -> String { /// "..::option::Option<..::string::String>", /// ); /// ``` -pub fn type_namen(n: usize) -> String { +pub fn type_namen(n: usize) -> String +where + T: ?Sized, +{ type_namemn::(0, n) } @@ -128,7 +130,10 @@ pub fn type_namen(n: usize) -> String { /// "..::option::Option<..::string::String>", /// ); /// ``` -pub fn type_namemn(m: usize, n: usize) -> String { +pub fn type_namemn(m: usize, n: usize) -> String +where + T: ?Sized, +{ let type_name_qualified = std::any::type_name::(); let type_name = TypeName::from(type_name_qualified); @@ -139,7 +144,7 @@ pub fn type_namemn(m: usize, n: usize) -> String { mod tests { use std::collections::HashMap; - use super::{type_name, type_namem, type_namemn, TypeName}; + use super::{type_name, type_namem, type_namemn, type_namen, TypeName}; #[test] fn type_name_primitives() { @@ -221,10 +226,28 @@ mod tests { let string = format!("{}", display); assert_eq!(type_namemn::(1, 0), string); } - + #[test] fn type_name_usize_mn() { - assert_eq!(type_namem::(std::usize::MAX), "::usize"); - assert_eq!(type_namemn::(std::usize::MAX, std::usize::MAX), "::usize"); + assert_eq!(type_namem::(std::usize::MAX), "::usize"); + assert_eq!( + type_namemn::(std::usize::MAX, std::usize::MAX), + "::usize" + ); + } + + #[test] + fn type_name_unsized() { + assert_eq!(type_name::(), "dyn Debug"); + } + + #[test] + fn type_name_unsized_mn() { + assert_eq!(type_namem::(1), "dyn core::..::Debug"); + assert_eq!(type_namen::(1), "dyn ..::fmt::Debug"); + assert_eq!( + type_namemn::(0, 1), + "dyn ..::fmt::Debug" + ); } } diff --git a/src/parser.rs b/src/parser.rs index cc7212a..10774ef 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -9,7 +9,8 @@ use nom::{ }; use crate::types::{ - TypeName, TypeNameArray, TypeNameReference, TypeNameSlice, TypeNameStruct, TypeNameTuple, + TypeName, TypeNameArray, TypeNameReference, TypeNameSlice, TypeNameStruct, TypeNameTrait, + TypeNameTuple, }; /// List of known primitive types @@ -156,12 +157,7 @@ pub fn parse_unit_or_tuple(input: &str) -> IResult<&str, TypeName> { alt((parse_unit, parse_tuple))(input) } -pub fn named_primitive_or_struct(input: &str) -> IResult<&str, TypeName> { - // Check for primitive types first, otherwise we cannot distinguish the `std::u32` module from - // `u32` (type). - // - // We shouldn't have to worry about `use crate::u32; u32::SomeType` as the type name input - // should be the fully qualified crate name as determined by Rust. +pub fn named_primitive(input: &str) -> Option> { if let Some(prim_type) = PRIMITIVE_TYPES .iter() .find(|prim_type| input.starts_with(*prim_type)) @@ -177,32 +173,63 @@ pub fn named_primitive_or_struct(input: &str) -> IResult<&str, TypeName> { if is_primitive_type { // Treat this as a type. - return Ok(( + Some(Ok(( remainder, TypeName::Struct(TypeNameStruct { module_path: vec![], simple_name: prim_type, type_params: vec![], }), - )); + ))) + } else { + None } + } else { + None } +} +pub fn struct_type(input: &str) -> IResult<&str, TypeNameStruct> { // Parse this as a module name tuple((module_path, type_simple_name, type_parameters))(input).map( |(s, (module_path, simple_name, type_params))| { ( s, - TypeName::Struct(TypeNameStruct { + TypeNameStruct { module_path, simple_name, type_params, - }), + }, ) }, ) } +pub fn named_primitive_or_struct(input: &str) -> IResult<&str, TypeName> { + // Check for primitive types first, otherwise we cannot distinguish the `std::u32` module from + // `u32` (type). + // + // We shouldn't have to worry about `use crate::u32; u32::SomeType` as the type name input + // should be the fully qualified crate name as determined by Rust. + if let Some(result) = named_primitive(input) { + result + } else { + struct_type(input) + .map(|(input, type_name_struct)| (input, TypeName::Struct(type_name_struct))) + } +} + +pub fn trait_type(input: &str) -> IResult<&str, TypeName> { + struct_type(input).map(|(input, type_name_struct)| { + ( + input, + TypeName::Trait(TypeNameTrait { + inner: type_name_struct, + }), + ) + }) +} + /// Parses a type name. pub fn type_name(input: &str) -> IResult<&str, TypeName> { // Primitive types begin with lowercase letters, but we have to detect them at this level, as @@ -211,7 +238,8 @@ pub fn type_name(input: &str) -> IResult<&str, TypeName> { // // In addition, types may begin with symbols, and we should detect them here and branch to the // relevant parsing functions. - if let Some(first_char) = input.chars().next() { + let mut chars = input.chars(); + if let Some(first_char) = chars.next() { match first_char { '[' => array_or_slice(input), '*' => unimplemented!("`tynm` is not implemented for pointer types."), @@ -219,6 +247,27 @@ pub fn type_name(input: &str) -> IResult<&str, TypeName> { .map(|(input, _)| (input, TypeName::Never)), '&' => parse_reference(input), '(' => parse_unit_or_tuple(input), + 'd' => { + let mut split = input.splitn(2, ' '); + if let Some("dyn") = split.next() { + if let Some(remainder) = split.next() { + trait_type(remainder) + } else { + // We only have "dyn" as a token. User may have specified r#dyn as a struct name or function + // name, but this is unusual. For now, we treat it as a struct. + Ok(( + "", + TypeName::Struct(TypeNameStruct { + module_path: vec![], + simple_name: "dyn", + type_params: vec![], + }), + )) + } + } else { + named_primitive_or_struct(input) + } + } _ => named_primitive_or_struct(input), } } else { diff --git a/src/types.rs b/src/types.rs index eeb9907..4512e13 100644 --- a/src/types.rs +++ b/src/types.rs @@ -45,6 +45,7 @@ pub enum TypeName<'s> { Slice(TypeNameSlice<'s>), Struct(TypeNameStruct<'s>), Tuple(TypeNameTuple<'s>), + Trait(TypeNameTrait<'s>), Unit, } @@ -157,6 +158,7 @@ impl<'s> TypeName<'s> { Self::Slice(type_name_slice) => type_name_slice.write_str(buffer, m, n), Self::Struct(type_name_struct) => type_name_struct.write_str(buffer, m, n), Self::Tuple(type_name_tuple) => type_name_tuple.write_str(buffer, m, n), + Self::Trait(type_name_trait) => type_name_trait.write_str(buffer, m, n), Self::Unit => buffer.write_str("()"), } } @@ -510,6 +512,94 @@ impl<'s> TypeNameTuple<'s> { } } +/// Type name of a trait. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TypeNameTrait<'s> { + /// Share implementation with [`TypeNameStruct`] + pub(crate) inner: TypeNameStruct<'s>, +} + +impl<'s> TypeNameTrait<'s> { + /// Returns the module path of the type. + pub fn module_path(&self) -> &[&'s str] { + &self.inner.module_path + } + + /// Returns the simple name of the type, excluding type parameters. + pub fn simple_name(&self) -> &'s str { + self.inner.simple_name + } + + /// Returns the type parameters of this type. + pub fn type_params(&self) -> &[TypeName<'s>] { + &self.inner.type_params + } + + /// Writes the type name string to the given buffer. + /// + /// If the left and right module segments overlap, the overlapping segments will only be printed + /// once. + /// + /// # Parameters + /// + /// * `buffer`: Buffer to write to. + /// * `m`: Number of module segments to include, beginning from the left (most significant). + /// * `n`: Number of module segments to include, beginning from the right (least significant). + pub fn write_str(&self, buffer: &mut W, m: usize, n: usize) -> Result<(), Error> + where + W: Write, + { + buffer.write_str("dyn ")?; + self.inner.write_str(buffer, m, n) + } + + /// Writes the module path to the given buffer. + /// + /// If the left and right module segments overlap, the overlapping segments will only be printed + /// once. + /// + /// # Parameters + /// + /// * `buffer`: Buffer to write to. + /// * `m`: Number of module segments to include, beginning from the left (most significant). + /// * `n`: Number of module segments to include, beginning from the right (least significant). + pub fn write_module_path(&self, buffer: &mut W, m: usize, n: usize) -> Result<(), Error> + where + W: Write, + { + self.inner.write_module_path(buffer, m, n) + } + + /// Writes the simple name to the given buffer. + /// + /// # Parameters + /// + /// * `buffer`: Buffer to write to. + pub fn write_simple_name(&self, buffer: &mut W) -> Result<(), Error> + where + W: Write, + { + self.inner.write_simple_name(buffer) + } + + /// Writes type parameters to the given buffer. + /// + /// If the left and right module segments overlap, the overlapping segments will only be printed + /// once. + /// + /// # Parameters + /// + /// * `buffer`: Buffer to write to. + /// * `m`: Number of module segments to include, beginning from the left (most significant). + /// * `n`: Number of module segments to include, beginning from the right (least significant). + pub fn write_type_params(&self, buffer: &mut W, m: usize, n: usize) -> Result<(), Error> + where + W: Write, + { + self.inner.write_type_params(buffer, m, n) + } +} + impl<'s> From<&'s str> for TypeName<'s> { fn from(std_type_name: &'s str) -> Self { parser::type_name(std_type_name)