Repository: CreateAPI/CreateAPI (swift)

Issue:
Make `ConfigOptions.access` an enum
https://github.com/CreateAPI/CreateAPI/blob/baa71afbcfc0c5ba591eab55e07c933357c8a381/Sources/CreateOptions/ConfigOptions.swift#L63-L64

The `access` property is currently just a string meaning that you can put anything in the config file and we'll end up generating non-functioning code. 

CreateAPI will only support `public` or `internal` access levels since `open` can't be applied everywhere and `private` would just generate code that is unusable. We could model this with an enum instead.

One thing to note is that while we could specify `access: internal` in the config, we'd want to generate code without access level specifiers (i.e the same as an empty string) since the `internal` keyword itself in generated code is redundant.

For reference, we've used enums elsewhere before in `ConfigOptions` so the same pattern can be followed:

https://github.com/CreateAPI/CreateAPI/blob/baa71afbcfc0c5ba591eab55e07c933357c8a381/Sources/CreateOptions/ConfigOptions.swift#L88-L97

Pull request:
- Closes #66 

Straightforward change. Big change to tests though apparently.

Patch:
diff --git a/Docs/ConfigOptions.md b/Docs/ConfigOptions.md
index 37391832..8f613bb3 100644
--- a/Docs/ConfigOptions.md
+++ b/Docs/ConfigOptions.md
@@ -102,10 +102,12 @@ Below you can find the complete documentation for all available options.
 
 ## access
 
-**Type:** String<br />
-**Default:** `"public"`
+**Type:** Access<br />
+**Default:** `.public`
 
 Access level modifier for all generated declarations
+- `public`
+- `internal`
 
 <br/>
 
diff --git a/Sources/CreateAPI/Generator/Templates.swift b/Sources/CreateAPI/Generator/Templates.swift
index 2c1387f5..f640f979 100644
--- a/Sources/CreateAPI/Generator/Templates.swift
+++ b/Sources/CreateAPI/Generator/Templates.swift
@@ -4,11 +4,12 @@ import Foundation
 
 // TODO: When parsing additionalProperties remove known keys
 final class Templates {
+    
     let options: GenerateOptions
 
     var access: String {
-        guard !options.access.isEmpty else { return "" }
-        return options.access + " "
+        guard options.access != .internal else { return ""}
+        return options.access.rawValue + " "
     }
 
     init(options: GenerateOptions) {
@@ -34,14 +35,16 @@ final class Templates {
     }
 
     func `struct`(name: TypeName, contents: [String], protocols: Protocols) -> String {
-        let lhs = [options.access, "struct", name.rawValue].filter { !$0.isEmpty }
+        let access = options.access == .internal ? "" : options.access.rawValue
+        let lhs = [access, "struct", name.rawValue].filter { !$0.isEmpty }
         let rhs = protocols.sorted()
         return declaration(lhs: lhs, rhs: rhs, contents: contents)
     }
 
     func `class`(name: TypeName, isFinal: Bool, contents: [String], protocols: Protocols) -> String {
         let type = isFinal ? "final class" : "class"
-        let lhs = [options.access, type, name.rawValue].filter { !$0.isEmpty }
+        let access = options.access == .internal ? "" : options.access.rawValue
+        let lhs = [access, type, name.rawValue].filter { !$0.isEmpty }
         let rhs = ([options.entities.baseClass] + protocols.sorted()).compactMap { $0 }
         return declaration(lhs: lhs, rhs: rhs, contents: contents)
     }
diff --git a/Sources/CreateOptions/ConfigOptions+DecodableWithDefault.swift b/Sources/CreateOptions/ConfigOptions+DecodableWithDefault.swift
index 89ad81d5..961637cc 100644
--- a/Sources/CreateOptions/ConfigOptions+DecodableWithDefault.swift
+++ b/Sources/CreateOptions/ConfigOptions+DecodableWithDefault.swift
@@ -26,9 +26,9 @@ extension ConfigOptions: Decodable {
     public init(from decoder: Decoder) throws {
         let container = try StringCodingContainer<KnownKeys>(decoder: decoder)
 
-        access = try container.decode(String.self,
+        access = try container.decode(Access.self,
             forKey: .access,
-            defaultValue: "public"
+            defaultValue: .public
         )
 
         annotateDeprecations = try container.decode(Bool.self,
diff --git a/Sources/CreateOptions/ConfigOptions.swift b/Sources/CreateOptions/ConfigOptions.swift
index 19a05640..2ded841a 100644
--- a/Sources/CreateOptions/ConfigOptions.swift
+++ b/Sources/CreateOptions/ConfigOptions.swift
@@ -65,9 +65,16 @@ import Foundation
 public struct ConfigOptions: Encodable {
     /// The default generator options
     public static let `default` = ConfigOptions()
+    
+    /// Available access controls
+    public enum Access: String, Codable {
+        case `internal`, `public`
+    }
 
     /// Access level modifier for all generated declarations
-    public var access: String = "public"
+    /// - `public`
+    /// - `internal`
+    public var access: Access = .public
 
     /// Add `@available(*, deprecated)` attribute to deprecated types and properties
     public var annotateDeprecations: Bool = true // sourcery: replacementFor = isAddingDeprecations


Test patch:
diff --git a/Tests/CreateAPITests/GenerateOptionsTests.swift b/Tests/CreateAPITests/GenerateOptionsTests.swift
index 2d0f175e..9d884910 100644
--- a/Tests/CreateAPITests/GenerateOptionsTests.swift
+++ b/Tests/CreateAPITests/GenerateOptionsTests.swift
@@ -359,6 +359,24 @@ final class GenerateOptionsTests: GenerateBaseTests {
         // THEN
         try compare(package: "petstore-change-namespace-when-operations-style")
     }
+    
+    func testPetstoreInternalAccessControl() throws {
+        // GIVEN
+        let command = try Generate.parse([
+            pathForSpec(named: "edgecases", ext: "yaml"),
+            "--output", temp.url.path,
+            "--package", "edgecases-change-access-control",
+            "--config", config("""
+            access: internal
+            """, ext: "yaml")
+            ])
+            
+        // WHEN
+        try command.run()
+        
+        // THEN
+        try compare(package: "edgecases-change-access-control")
+    }
         
     func testEdgecasesRenamePrperties() throws {
         // GIVEN
@@ -420,26 +438,6 @@ final class GenerateOptionsTests: GenerateBaseTests {
         // THEN
         try compare(package: "edgecases-yaml-config")
     }
-    
-    func testEdgecasesChangeAccessControl() throws {
-        // GIVEN
-        let command = try Generate.parse([
-            pathForSpec(named: "edgecases", ext: "yaml"),
-            "--output", temp.url.path,
-            "--package", "edgecases-change-access-control",
-            "--config", config("""
-            {
-                "access": ""
-            }
-            """)
-        ])
-                
-        // WHEN
-        try command.run()
-        
-        // THEN
-        try compare(package: "edgecases-change-access-control")
-    }
                     
     func testEdgecasesDisableAcronyms() throws {
         // GIVEN