use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
#[derive(Clone, Debug)]
pub struct TypePath {
segments: Vec<String>,
qualified: String,
}
impl TypePath {
pub fn simple(name: impl Into<String>) -> Self {
let name = name.into();
TypePath {
segments: vec![name.clone()],
qualified: name,
}
}
pub fn from_segments(segments: Vec<String>) -> Self {
let qualified = segments.join("::");
TypePath {
segments,
qualified,
}
}
pub fn from_qualified(s: impl Into<String>) -> Self {
let s = s.into();
let segments: Vec<String> = s.split("::").map(|seg| seg.to_string()).collect();
TypePath {
segments,
qualified: s,
}
}
pub fn name(&self) -> &str {
self.segments.last().map(|s| s.as_str()).unwrap_or("")
}
pub fn module_segments(&self) -> &[String] {
if self.segments.len() > 1 {
&self.segments[..self.segments.len() - 1]
} else {
&[]
}
}
pub fn is_qualified(&self) -> bool {
self.segments.len() > 1
}
pub fn as_str(&self) -> &str {
&self.qualified
}
pub fn segments(&self) -> &[String] {
&self.segments
}
}
impl Deref for TypePath {
type Target = str;
fn deref(&self) -> &str {
&self.qualified
}
}
impl Borrow<str> for TypePath {
fn borrow(&self) -> &str {
&self.qualified
}
}
impl AsRef<str> for TypePath {
fn as_ref(&self) -> &str {
&self.qualified
}
}
impl PartialEq for TypePath {
fn eq(&self, other: &Self) -> bool {
self.qualified == other.qualified
}
}
impl Eq for TypePath {}
impl Hash for TypePath {
fn hash<H: Hasher>(&self, state: &mut H) {
self.qualified.hash(state);
}
}
impl PartialEq<str> for TypePath {
fn eq(&self, other: &str) -> bool {
self.qualified == other
}
}
impl PartialEq<&str> for TypePath {
fn eq(&self, other: &&str) -> bool {
self.qualified.as_str() == *other
}
}
impl PartialEq<String> for TypePath {
fn eq(&self, other: &String) -> bool {
self.qualified == *other
}
}
impl PartialEq<TypePath> for str {
fn eq(&self, other: &TypePath) -> bool {
self == other.qualified
}
}
impl PartialEq<TypePath> for &str {
fn eq(&self, other: &TypePath) -> bool {
*self == other.qualified.as_str()
}
}
impl PartialEq<TypePath> for String {
fn eq(&self, other: &TypePath) -> bool {
*self == other.qualified
}
}
impl fmt::Display for TypePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.qualified)
}
}
impl From<String> for TypePath {
fn from(s: String) -> Self {
TypePath::from_qualified(s)
}
}
impl From<&str> for TypePath {
fn from(s: &str) -> Self {
TypePath::from_qualified(s)
}
}
impl Serialize for TypePath {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.qualified.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for TypePath {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
Ok(TypePath::from_qualified(s))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_path() {
let p = TypePath::simple("Foo");
assert_eq!(p.as_str(), "Foo");
assert_eq!(p.name(), "Foo");
assert!(!p.is_qualified());
assert!(p.module_segments().is_empty());
}
#[test]
fn test_qualified_path() {
let p = TypePath::from_segments(vec!["foo".into(), "Bar".into()]);
assert_eq!(p.as_str(), "foo::Bar");
assert_eq!(p.name(), "Bar");
assert!(p.is_qualified());
assert_eq!(p.module_segments(), &["foo".to_string()]);
}
#[test]
fn test_deeply_qualified() {
let p = TypePath::from_segments(vec!["a".into(), "b".into(), "C".into()]);
assert_eq!(p.as_str(), "a::b::C");
assert_eq!(p.name(), "C");
assert_eq!(p.module_segments(), &["a".to_string(), "b".to_string()]);
}
#[test]
fn test_deref_str() {
let p = TypePath::simple("Foo");
let s: &str = &p;
assert_eq!(s, "Foo");
}
#[test]
fn test_eq_str() {
let p = TypePath::simple("Foo");
assert!(p == "Foo");
assert!("Foo" == p);
assert!(p == "Foo".to_string());
}
#[test]
fn test_from_string() {
let p: TypePath = "foo::Bar".to_string().into();
assert!(p.is_qualified());
assert_eq!(p.name(), "Bar");
}
#[test]
fn test_from_str() {
let p: TypePath = "Baz".into();
assert!(!p.is_qualified());
}
#[test]
fn test_serde_roundtrip() {
let p = TypePath::from_segments(vec!["mod".into(), "Type".into()]);
let json = serde_json::to_string(&p).unwrap();
assert_eq!(json, "\"mod::Type\"");
let p2: TypePath = serde_json::from_str(&json).unwrap();
assert_eq!(p, p2);
}
#[test]
fn test_hashmap_lookup() {
use std::collections::HashMap;
let mut map: HashMap<String, i32> = HashMap::new();
map.insert("foo::Bar".to_string(), 42);
let p = TypePath::from_qualified("foo::Bar");
assert_eq!(map.get(p.as_str()), Some(&42));
}
}