Skip to content

Commit

Permalink
Merge pull request #10 from azriel91/feature/9/relax-sized-bound
Browse files Browse the repository at this point in the history
Feature/9/relax sized bound
  • Loading branch information
azriel91 authored Oct 13, 2020
2 parents 031d46f + bbacb9c commit 15f02b4
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 70 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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])
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tynm"
version = "0.1.4"
version = "0.1.5"
authors = ["Azriel Hoh <[email protected]>"]
edition = "2018"
description = "Returns type names in shorter form."
Expand Down
41 changes: 16 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Option<String>>(),
"core::option::Option<alloc::string::String>",
);

// === tynm === //
// Simple type name:
assert_eq!(tynm::type_name::<Option<String>>(), "Option<String>",);

// Type name with 1 module segment, starting from the most significant module.
assert_eq!(
tynm::type_namem::<Option<String>>(1),
"core::..::Option<alloc::..::String>",
);

// Type name with 1 module segment, starting from the least significant module.
#[rustfmt::skip]
assert_eq!(
tynm::type_namen::<Option<String>>(1),
"..::option::Option<..::string::String>",
std::any::type_name::<Option<String>>(), "core::option::Option<alloc::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::<rust_out::two::three::Struct>(1, 1),
"rust_out::..::three::Struct",
);
let tuples = vec![
(tynm::type_name::<Option<String>>(), "Option<String>"),
(tynm::type_namem::<Option<String>>(1), "core::..::Option<alloc::..::String>"),
(tynm::type_namen::<Option<String>>(1), "..::option::Option<..::string::String>"),
// 1 segment from most and least significant modules.
(tynm::type_namemn::<rust_out::two::three::Struct>(1, 1), "rust_out::..::three::Struct"),
// traits
(tynm::type_name::<dyn core::fmt::Debug>(), "dyn Debug"),
];

tuples
.iter()
.for_each(|(left, right)| assert_eq!(left, right));

```

## Motivation
Expand Down
87 changes: 55 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Option<String>>(),
//! "core::option::Option<alloc::string::String>",
//! std::any::type_name::<Option<String>>(), "core::option::Option<alloc::string::String>"
//! );
//!
//! // === tynm === //
//! // Simple type name:
//! assert_eq!(tynm::type_name::<Option<String>>(), "Option<String>",);
//! #[rustfmt::skip]
//! let tuples = vec![
//! (tynm::type_name::<Option<String>>(), "Option<String>"),
//! (tynm::type_namem::<Option<String>>(1), "core::..::Option<alloc::..::String>"),
//! (tynm::type_namen::<Option<String>>(1), "..::option::Option<..::string::String>"),
//! // 1 segment from most and least significant modules.
//! (tynm::type_namemn::<rust_out::two::three::Struct>(1, 1), "rust_out::..::three::Struct"),
//! // traits
//! (tynm::type_name::<dyn core::fmt::Debug>(), "dyn Debug"),
//! ];
//!
//! // Type name with 1 module segment, starting from the most significant module.
//! assert_eq!(
//! tynm::type_namem::<Option<String>>(1),
//! "core::..::Option<alloc::..::String>",
//! );
//! 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::<Option<String>>(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::<rust_out::two::three::Struct>(1, 1),
//! "rust_out::..::three::Struct",
//! );
//! # #[rustfmt::skip]
//! # mod rust_out { pub mod two { pub mod three { pub struct Struct; } } }
//! ```
//!
//! # Motivation
Expand Down Expand Up @@ -61,7 +54,10 @@ mod types;
/// ```rust
/// assert_eq!(tynm::type_name::<Option<String>>(), "Option<String>",);
/// ```
pub fn type_name<T>() -> String {
pub fn type_name<T>() -> String
where
T: ?Sized,
{
type_namemn::<T>(0, 0)
}

Expand All @@ -83,7 +79,10 @@ pub fn type_name<T>() -> String {
/// "core::..::Option<alloc::..::String>",
/// );
/// ```
pub fn type_namem<T>(m: usize) -> String {
pub fn type_namem<T>(m: usize) -> String
where
T: ?Sized,
{
type_namemn::<T>(m, 0)
}

Expand All @@ -105,7 +104,10 @@ pub fn type_namem<T>(m: usize) -> String {
/// "..::option::Option<..::string::String>",
/// );
/// ```
pub fn type_namen<T>(n: usize) -> String {
pub fn type_namen<T>(n: usize) -> String
where
T: ?Sized,
{
type_namemn::<T>(0, n)
}

Expand All @@ -128,7 +130,10 @@ pub fn type_namen<T>(n: usize) -> String {
/// "..::option::Option<..::string::String>",
/// );
/// ```
pub fn type_namemn<T>(m: usize, n: usize) -> String {
pub fn type_namemn<T>(m: usize, n: usize) -> String
where
T: ?Sized,
{
let type_name_qualified = std::any::type_name::<T>();

let type_name = TypeName::from(type_name_qualified);
Expand All @@ -139,7 +144,7 @@ pub fn type_namemn<T>(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() {
Expand Down Expand Up @@ -221,10 +226,28 @@ mod tests {
let string = format!("{}", display);
assert_eq!(type_namemn::<T>(1, 0), string);
}

#[test]
fn type_name_usize_mn() {
assert_eq!(type_namem::<usize>(std::usize::MAX), "::usize");
assert_eq!(type_namemn::<usize>(std::usize::MAX, std::usize::MAX), "::usize");
assert_eq!(type_namem::<usize>(std::usize::MAX), "::usize");
assert_eq!(
type_namemn::<usize>(std::usize::MAX, std::usize::MAX),
"::usize"
);
}

#[test]
fn type_name_unsized() {
assert_eq!(type_name::<dyn core::fmt::Debug>(), "dyn Debug");
}

#[test]
fn type_name_unsized_mn() {
assert_eq!(type_namem::<dyn core::fmt::Debug>(1), "dyn core::..::Debug");
assert_eq!(type_namen::<dyn core::fmt::Debug>(1), "dyn ..::fmt::Debug");
assert_eq!(
type_namemn::<dyn core::fmt::Debug>(0, 1),
"dyn ..::fmt::Debug"
);
}
}
73 changes: 61 additions & 12 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<IResult<&str, TypeName>> {
if let Some(prim_type) = PRIMITIVE_TYPES
.iter()
.find(|prim_type| input.starts_with(*prim_type))
Expand All @@ -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
Expand All @@ -211,14 +238,36 @@ 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."),
'!' => nom::character::complete::char('!')(input)
.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 {
Expand Down
Loading

0 comments on commit 15f02b4

Please sign in to comment.