kotlin_poet_rs/spec/
type.rs

1use std::str::FromStr;
2
3use crate::io::RenderKotlin;
4use crate::spec::{ClassLikeTypeName, CodeBlock, FunctionType, Name, Package};
5use crate::spec::class_like_type::ClassLikeType;
6use crate::tokens;
7use crate::util::{SemanticConversionError, yolo_from_str};
8
9// region stdlib types codegen
10macro_rules! fn_basic_type_factory {
11    ($identifier:ident, $($package:tt).+, $class:ident) => {
12        #[doc = concat!(
13            "Creates `",
14            stringify!($($package).+),
15            ".",
16            stringify!($class)
17        )]
18        pub fn $identifier() -> Type {
19            use std::str::FromStr;
20
21            let package = Package::from_str(stringify!($($package).+)).unwrap();
22            let name = Name::from_str(stringify!($class)).unwrap();
23
24            Type::ClassLike(
25                ClassLikeType::new(
26                    ClassLikeTypeName::top_level(
27                        package,
28                        name
29                    )
30                )
31            )
32        }
33    };
34}
35
36macro_rules! fn_generic_type_factory {
37    ($identifier:ident, $($package:tt).+, $class:ident<$($generic:ident),+>) => {
38        #[doc = concat!(
39            "Creates `",
40            stringify!($($package).+),
41            ".",
42            stringify!($class)
43        )]
44        pub fn $identifier($($generic: Type,)+) -> Type {
45            use std::str::FromStr;
46
47            let package = Package::from_str(stringify!($($package).+)).unwrap();
48            let name = Name::from_str(stringify!($class)).unwrap();
49
50            let mut inner_type = ClassLikeType::new(
51                ClassLikeTypeName::top_level(
52                    package,
53                    name,
54                )
55            );
56
57            $(
58            inner_type = inner_type.generic_argument($generic);
59            )+
60
61            Type::ClassLike(
62                inner_type
63            )
64        }
65    };
66}
67// endregion stdlib types codegen
68
69/// Kotlin fully resolved / qualified type
70#[derive(PartialEq, Debug, Clone)]
71pub enum Type {
72    /// Type that behaves like class (e.g. `kotlin.String`, `kotlin.collections.List<String>`)
73    ClassLike(ClassLikeType),
74    /// Functional type (e.g. `(Int) -> String`)
75    Function(FunctionType),
76    /// Generic argument as type (e.g. `T`)
77    Generic(Name),
78}
79
80impl Type {
81    /// Creates generic type
82    pub fn generic<NameLike: Into<Name>>(name: NameLike) -> Type {
83        Type::Generic(name.into())
84    }
85
86    // Integer numbers
87    fn_basic_type_factory!(int, kotlin, Int);
88    fn_basic_type_factory!(long, kotlin, Long);
89    fn_basic_type_factory!(short, kotlin, Short);
90    fn_basic_type_factory!(byte, kotlin, Byte);
91
92    // Floating point numbers
93    fn_basic_type_factory!(float, kotlin, Float);
94    fn_basic_type_factory!(double, kotlin, Double);
95
96    // Logic
97    fn_basic_type_factory!(boolean, kotlin, Boolean);
98
99    // Text
100    fn_basic_type_factory!(char, kotlin, Char);
101    fn_basic_type_factory!(string, kotlin, String);
102
103    // Control Types
104    fn_basic_type_factory!(unit, kotlin, Unit);
105    fn_basic_type_factory!(any, kotlin, Any);
106    fn_basic_type_factory!(nothing, kotlin, Nothing);
107
108    // Collections
109    fn_generic_type_factory!(map, kotlin.collections, Map<key, value>);
110    fn_generic_type_factory!(list, kotlin.collections, List<value>);
111    fn_generic_type_factory!(set, kotlin.collections, Set<value>);
112    fn_generic_type_factory!(array, kotlin, Array<value>);
113}
114
115impl From<ClassLikeTypeName> for Type {
116    fn from(value: ClassLikeTypeName) -> Self {
117        Type::ClassLike(ClassLikeType::new(value))
118    }
119}
120
121impl From<ClassLikeType> for Type {
122    fn from(value: ClassLikeType) -> Self {
123        Type::ClassLike(value)
124    }
125}
126
127yolo_from_str!(Type);
128impl FromStr for Type {
129    type Err = SemanticConversionError;
130
131    fn from_str(s: &str) -> Result<Self, Self::Err> {
132        let clear = s.trim();
133
134        if !clear.contains(tokens::DOT) {
135            return Ok(
136                Type::Generic(
137                    Name::from_str(clear)?
138                )
139            );
140        }
141
142        if clear.contains(tokens::ROUND_BRACKET_LEFT) {
143            return Err(
144                SemanticConversionError::new(
145                    "Function types are not supported by Type::from_str"
146                )
147            );
148        }
149
150        Ok(
151            Type::ClassLike(
152                ClassLikeType::from_str(clear)?
153            )
154        )
155    }
156}
157
158impl RenderKotlin for Type {
159    fn render_into(&self, block: &mut CodeBlock) {
160        match self {
161            Type::ClassLike(class_like) => block.push_renderable(class_like),
162            Type::Generic(name) => block.push_renderable(name),
163            Type::Function(lambda) => block.push_renderable(lambda)
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use std::str::FromStr;
171
172    use crate::io::RenderKotlin;
173    use crate::spec::{Name, Type};
174
175    #[test]
176    fn render_generic_parameter() {
177        let name = Name::from_str("T").unwrap();
178        let parameter = Type::Generic(name);
179        assert_eq!(parameter.render_string(), "T");
180    }
181
182    #[test]
183    fn parse_fn_type() {
184        let new_type = Type::from_str("() -> String");
185        assert!(matches!(
186            new_type,
187            Err(_)
188        ));
189    }
190
191    #[test]
192    fn parse_generic() {
193        let new_type = Type::from_str("T");
194        assert_eq!(
195            new_type.unwrap(),
196            Type::generic("T")
197        );
198    }
199}