go_away/output/swift/
mod.rs

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