go_away/output/kotlin/
mod.rs

1use crate::{
2    output::prelude::*,
3    types::{self, Alias, NewType, Struct},
4};
5
6use self::{enums::Enum, structs::KotlinStruct, unions::Union};
7
8use super::go::FieldType;
9
10mod data_classes;
11mod enums;
12mod kserializer;
13mod structs;
14mod unions;
15
16#[cfg(test)]
17mod tests;
18
19/// An enum representing the possible top-level types in Kotlin
20///
21/// This shouldn't be instaniated directly but passed using turbofish operator
22/// to registry_to_output enabling it to write out in Kotlin
23pub enum KotlinType<'a> {
24    /// A struct variant
25    Struct(&'a Struct),
26    /// A new type variant
27    NewType(&'a NewType),
28    /// A type alias variant
29    Alias(&'a Alias),
30    /// A simple enum variant (does not contain data)
31    Enum(&'a types::Enum),
32    /// A union variant (enums with data)
33    Union(&'a types::Union),
34}
35
36impl fmt::Display for KotlinType<'_> {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            KotlinType::Struct(details) => {
40                let struct_ = KotlinStruct::new(&details.name).with_fields(&details.fields);
41                writeln!(f, "{struct_}")?;
42            }
43            KotlinType::NewType(details) => {
44                let struct_ = KotlinStruct::newtype(&details.name, &details.inner);
45                writeln!(f, "{struct_}")?;
46            }
47            KotlinType::Alias(details) => {
48                writeln!(
49                    f,
50                    "typealias {} = {}",
51                    details.name,
52                    details.inner.kotlin_type()
53                )?;
54            }
55            KotlinType::Enum(details) => {
56                let enum_ = Enum::new(&details.name).with_variants(&details.variants);
57                writeln!(f, "{enum_}")?;
58            }
59            KotlinType::Union(details) => {
60                let union_ = Union::new(&details.name, details.representation.clone())
61                    .with_variants(&details.variants);
62                writeln!(f, "{union_}")?;
63            }
64        }
65        Ok(())
66    }
67}
68
69impl FieldType {
70    fn kotlin_type(&self) -> String {
71        use crate::types::Primitive;
72
73        match self {
74            FieldType::Named(type_ref) => type_ref.name().to_string(),
75            FieldType::Optional(inner) => format!("{}?", inner.kotlin_type()),
76            FieldType::List(inner) => format!("List<{}>", inner.kotlin_type()),
77            FieldType::Map { key, value } => {
78                format!("Map<{}, {}>", key.kotlin_type(), value.kotlin_type())
79            }
80            FieldType::Primitive(Primitive::String) => "String".to_string(),
81            FieldType::Primitive(Primitive::Float) => "Double".to_string(),
82            FieldType::Primitive(Primitive::Int) => "Long".to_string(),
83            FieldType::Primitive(Primitive::Bool) => "Boolean".to_string(),
84            FieldType::Primitive(Primitive::Time) => {
85                // Also: is this a datetime or just a time.  Might need to expand the primitive support somewhat...
86                todo!("Need to implement time support for kotlin")
87            }
88        }
89    }
90
91    fn default_str(&self) -> &'static str {
92        match self {
93            FieldType::Optional(_) => " = null",
94            _ => "",
95        }
96    }
97
98    fn serializer(&self) -> String {
99        match self {
100            FieldType::Optional(inner) => {
101                format!("{}.nullable", inner.serializer())
102            }
103            FieldType::List(inner) => {
104                format!("ListSerializer({})", inner.serializer())
105            }
106            FieldType::Map { key, value } => {
107                format!(
108                    "MapSerializer({}, {})",
109                    key.serializer(),
110                    value.serializer()
111                )
112            }
113            FieldType::Named(_) | FieldType::Primitive(_) => {
114                format!("{}.serializer()", self.kotlin_type())
115            }
116        }
117    }
118}
119
120fn to_camel_case(s: &str) -> String {
121    let mut buf = String::with_capacity(s.len());
122    let mut prev_is_underscore = false;
123    let mut first = true;
124    for c in s.chars() {
125        if c == '_' {
126            prev_is_underscore = true;
127        } else if prev_is_underscore {
128            buf.push(c.to_ascii_uppercase());
129            prev_is_underscore = false;
130        } else if c.is_uppercase() && !first {
131            buf.push(c);
132        } else {
133            buf.push(c.to_ascii_lowercase());
134        }
135        first = false;
136    }
137    buf
138}
139
140fn to_screaming_snake_case(s: &str) -> String {
141    let mut buf = String::with_capacity(s.len());
142    let mut first = true;
143    for c in s.chars() {
144        if c.is_uppercase() {
145            if !first {
146                buf.push('_');
147            }
148            buf.push(c);
149        } else {
150            buf.push(c.to_ascii_uppercase());
151        }
152        first = false;
153    }
154    buf
155}