Skip to content

Commit 9ab4f59

Browse files
Don't clean up output directory (#36)
* prevent to remove root folder and grouped module arguments * migrate files type from String to URL * output directory already exist error * fix CI minor changes * typo error in linux - swagger generator
1 parent dfa0392 commit 9ab4f59

18 files changed

+412
-292
lines changed

BowOpenAPI/main.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@ extension BowOpenAPICommand {
2121
"""
2222
Could not generate API client:
2323
• SCHEMA '\(self.schema)'
24-
2524
\(apiError)
2625
2726
\(self.verbose ?
28-
" • LOG \n\n\(prodEnv.logPath.contentOfFile)\n" :
29-
" • LOG: \(prodEnv.logPath)")
27+
"• LOG \n\n\(prodEnv.logPath.contentOfFile)\n" :
28+
"• LOG: \(prodEnv.logPath)")
3029
"""
3130
},
3231
{ success in

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ macos: clean structure install
1616
.PHONY: fixtures
1717
fixtures:
1818
@rm -rf ./Tests/Fixtures/FixturesAPI
19-
$(TOOL_NAME) --name FixturesAPI --schema ./Tests/Fixtures/petstore.yaml --output ./Tests/Fixtures/FixturesAPI --verbose
19+
$(TOOL_NAME) --name FixturesAPI --schema ./Tests/Fixtures/petstore.yaml --output ./Tests/Fixtures --verbose
2020

2121
.PHONY: xcode
2222
xcode: macos fixtures

OpenApiGenerator/APIClient.swift

Lines changed: 65 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,80 +8,90 @@ public enum APIClient {
88

99
public static func bow(moduleName: String, schema: String, output: String) -> EnvIO<Environment, APIClientError, String> {
1010
let env = EnvIO<Environment, APIClientError, Environment>.var()
11-
let validated = EnvIO<Environment, APIClientError, String>.var()
12-
let template = EnvIO<Environment, APIClientError, URL>.var()
13-
let schema = schema.expandingTildeInPath
14-
let output = output.expandingTildeInPath
11+
let templates = EnvIO<Environment, APIClientError, URL>.var()
12+
let generated = EnvIO<Environment, APIClientError, String>.var()
1513

1614
return binding(
17-
env <- .ask(),
18-
validated <- validate(schema: schema),
19-
template <- env.get.generator.getTemplates().contramap(\.fileSystem),
20-
|<-bow(moduleName: moduleName, scheme: validated.get, output: output, template: template.get),
21-
yield: "RENDER SUCCEEDED")^
15+
env <- .ask(),
16+
templates <- env.get.generator.getTemplates(),
17+
generated <- bow(moduleName: moduleName, scheme: schema, output: output, templates: templates.get),
18+
yield: generated.get)^
2219
}
2320

24-
public static func bow(moduleName: String, scheme: String, output: String, template: URL) -> EnvIO<Environment, APIClientError, String> {
25-
EnvIO { env in
26-
let outputPath = OutputPath(sources: "\(output)/Sources",
27-
tests: "\(output)/XCTest")
28-
29-
return binding(
30-
|<-createStructure(outputPath: outputPath).provide(env.fileSystem),
31-
|<-env.generator.generate(moduleName: moduleName,
32-
schemePath: scheme,
33-
outputPath: outputPath,
34-
template: template,
35-
logPath: env.logPath).provide(env.fileSystem),
36-
|<-createSwiftPackage(moduleName: moduleName, outputPath: output, template: template).provide(env.fileSystem),
37-
yield: "RENDER SUCCEEDED")^
38-
}
21+
public static func bow(moduleName: String, scheme: String, output: String, templates: URL) -> EnvIO<Environment, APIClientError, String> {
22+
let outputURL = URL(fileURLWithPath: output.expandingTildeInPath)
23+
let schemeURL = URL(fileURLWithPath: scheme.expandingTildeInPath)
24+
let module = OpenAPIModule(name: moduleName,
25+
url: outputURL,
26+
schema: schemeURL,
27+
templates: templates)
28+
29+
return bow(module: module)
30+
}
31+
32+
public static func bow(module: OpenAPIModule) -> EnvIO<Environment, APIClientError, String> {
33+
let env = EnvIO<Environment, APIClientError, Environment>.var()
34+
let validated = EnvIO<Environment, APIClientError, OpenAPIModule>.var()
35+
36+
return binding(
37+
env <- .ask(),
38+
validated <- validate(module: module),
39+
|<-createStructure(module: validated.get),
40+
|<-env.get.generator.generate(module: validated.get),
41+
|<-createSwiftPackage(module: validated.get),
42+
yield: "RENDER SUCCEEDED")^
3943
}
4044

4145
// MARK: attributes
42-
private static func validate(schema: String) -> EnvIO<Environment, APIClientError, String> {
43-
EnvIO.invoke { _ in
44-
guard FileManager.default.fileExists(atPath: schema) else {
46+
private static func validate(module: OpenAPIModule) -> EnvIO<Environment, APIClientError, OpenAPIModule> {
47+
EnvIO.invoke { env in
48+
guard env.fileSystem.exist(item: module.schema) else {
4549
throw APIClientError(operation: "validate(schema:output:)",
4650
error: GeneratorError.invalidParameters)
4751
}
4852

49-
return schema
53+
return module
5054
}
5155
}
5256

5357
// MARK: steps
54-
internal static func createStructure(outputPath: OutputPath) -> EnvIO<FileSystem, APIClientError, ()> {
55-
EnvIO { fileSystem in
56-
let parentPath = outputPath.sources.parentPath
58+
internal static func createStructure(module: OpenAPIModule) -> EnvIO<Environment, APIClientError, Void> {
59+
EnvIO { env in
60+
guard !env.fileSystem.exist(item: module.url) else {
61+
return .raiseError(APIClientError(operation: "createStructure(atPath:)", error: GeneratorError.existOutput(directory: module.url)))^
62+
}
5763

58-
return fileSystem.removeDirectory(parentPath).handleError({ _ in })^
59-
.followedBy(fileSystem.createDirectory(atPath: parentPath))^
60-
.followedBy(fileSystem.createDirectory(atPath: outputPath.sources))^
61-
.followedBy(fileSystem.createDirectory(atPath: outputPath.tests))^
62-
.mapError { _ in APIClientError(operation: "createStructure(atPath:)", error: GeneratorError.structure) }
64+
return env.fileSystem.createDirectory(at: module.url, withIntermediateDirectories: true)^
65+
.followedBy(env.fileSystem.createDirectory(at: module.sources))^
66+
.followedBy(env.fileSystem.createDirectory(at: module.tests))^
67+
.mapError { _ in APIClientError(operation: "createStructure(atPath:)", error: GeneratorError.structure) }
6368
}
6469
}
6570

66-
internal static func createSwiftPackage(moduleName: String, outputPath: String, template: URL) -> EnvIO<FileSystem, APIClientError, ()> {
67-
EnvIO { fileSystem in
68-
fileSystem.copy(item: "Package.swift", from: template.path, to: outputPath)^
69-
}.followedBy(package(moduleName: moduleName, outputPath: outputPath))^
70-
.mapError(FileSystemError.toAPIClientError)
71-
}
72-
73-
internal static func package(moduleName: String, outputPath: String) -> EnvIO<FileSystem, FileSystemError, ()> {
74-
EnvIO { fileSystem in
75-
let content = IO<FileSystemError, String>.var()
76-
let fixedContent = IO<FileSystemError, String>.var()
77-
let path = outputPath + "/Package.swift"
78-
79-
return binding(
80-
content <- fileSystem.readFile(atPath: path),
81-
fixedContent <- IO.pure(content.get.replacingOccurrences(of: "{{ moduleName }}", with: moduleName)),
82-
|<-fileSystem.write(content: fixedContent.get, toFile: path),
83-
yield: ()
84-
)^
71+
internal static func createSwiftPackage(module: OpenAPIModule) -> EnvIO<Environment, APIClientError, Void> {
72+
func installPackage(module: OpenAPIModule) -> EnvIO<FileSystem, FileSystemError, Void> {
73+
EnvIO { fileSystem in
74+
fileSystem.copy(item: "Package.swift", from: module.templates, to: module.url)^
75+
}
8576
}
77+
78+
func updatePackageName(module: OpenAPIModule) -> EnvIO<FileSystem, FileSystemError, Void> {
79+
EnvIO { fileSystem in
80+
let content = IO<FileSystemError, String>.var()
81+
let fixedContent = IO<FileSystemError, String>.var()
82+
let output = module.url.appendingPathComponent("Package.swift")
83+
84+
return binding(
85+
content <- fileSystem.readFile(at: output),
86+
fixedContent <- IO.pure(content.get.replacingOccurrences(of: "{{ moduleName }}", with: module.name)),
87+
|<-fileSystem.write(content: fixedContent.get, toFile: output),
88+
yield: ())^
89+
}
90+
}
91+
92+
return installPackage(module: module)
93+
.followedBy(updatePackageName(module: module))^
94+
.mapError(FileSystemError.toAPIClientError)
95+
.contramap(\.fileSystem)^
8696
}
8797
}

OpenApiGenerator/Algebra/ClientGenerator.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,7 @@ import Foundation
44
import Bow
55
import BowEffects
66

7-
public struct OutputPath {
8-
let sources: String
9-
let tests: String
10-
}
11-
127
public protocol ClientGenerator {
13-
func getTemplates() -> EnvIO<FileSystem, APIClientError, URL>
14-
func generate(moduleName: String, schemePath: String, outputPath: OutputPath, template: URL, logPath: String) -> EnvIO<FileSystem, APIClientError, Void>
8+
func getTemplates() -> EnvIO<Environment, APIClientError, URL>
9+
func generate(module: OpenAPIModule) -> EnvIO<Environment, APIClientError, Void>
1510
}

OpenApiGenerator/Algebra/FileSystem.swift

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,56 +5,62 @@ import Bow
55
import BowEffects
66

77
public protocol FileSystem {
8-
func createDirectory(atPath: String) -> IO<FileSystemError, ()>
9-
func copy(itemPath: String, toPath: String) -> IO<FileSystemError, ()>
10-
func remove(itemPath: String) -> IO<FileSystemError, ()>
11-
func items(atPath path: String) -> IO<FileSystemError, [String]>
12-
func readFile(atPath path: String) -> IO<FileSystemError, String>
13-
func write(content: String, toFile path: String) -> IO<FileSystemError, ()>
8+
func createDirectory(at: URL, withIntermediateDirectories: Bool) -> IO<FileSystemError, Void>
9+
func copy(item: URL, to: URL) -> IO<FileSystemError, Void>
10+
func remove(item: URL) -> IO<FileSystemError, Void>
11+
func items(at: URL) -> IO<FileSystemError, [URL]>
12+
func readFile(at: URL) -> IO<FileSystemError, String>
13+
func write(content: String, toFile: URL) -> IO<FileSystemError, Void>
14+
func exist(item: URL) -> Bool
1415
}
1516

1617
public extension FileSystem {
17-
func copy(item: String, from input: String, to output: String) -> IO<FileSystemError, ()> {
18-
copy(itemPath: "\(input)/\(item)", toPath: "\(output)/\(item)")
18+
func createDirectory(at directory: URL) -> IO<FileSystemError, Void> {
19+
createDirectory(at: directory, withIntermediateDirectories: false)
1920
}
2021

21-
func copy(items: [String], from input: String, to output: String) -> IO<FileSystemError, ()> {
22-
items.traverse { (itemPath: String) in
23-
self.copy(item: itemPath.filename, from: input, to: output)
22+
func copy(item: String, from input: URL, to output: URL) -> IO<FileSystemError, Void> {
23+
copy(item: input.appendingPathComponent(item), to: output.appendingPathComponent(item))
24+
}
25+
26+
func copy(items: [String], from input: URL, to output: URL) -> IO<FileSystemError, Void> {
27+
items.traverse { (item: String) in
28+
self.copy(item: item.filename, from: input, to: output)
2429
}.void()^
2530
}
2631

27-
func remove(from folder: String, files: String...) -> IO<FileSystemError, ()> {
28-
files.traverse { file in self.remove(itemPath: "\(folder)/\(file)") }.void()^
32+
func remove(in directory: URL, files: [String]) -> IO<FileSystemError, Void> {
33+
files.traverse { file in self.remove(item: directory.appendingPathComponent(file)) }.void()^
2934
}
3035

31-
func removeDirectory(_ output: String) -> IO<FileSystemError, ()> {
32-
let outputURL = URL(fileURLWithPath: output, isDirectory: true)
33-
return remove(itemPath: outputURL.path)
36+
func removeDirectory(_ directory: URL) -> IO<FileSystemError, Void> {
37+
let outputURL = URL(fileURLWithPath: directory.path, isDirectory: true)
38+
return remove(item: outputURL)
3439
}
3540

36-
func removeFiles(_ files: String...) -> IO<FileSystemError, ()> {
37-
files.traverse(remove(itemPath:)).void()^
41+
func removeFiles(_ files: [URL]) -> IO<FileSystemError, Void> {
42+
files.traverse(remove(item:)).void()^
3843
}
3944

40-
func moveFile(from origin: String, to destination: String) -> IO<FileSystemError, Void> {
41-
copy(itemPath: origin, toPath: destination)
42-
.followedBy(removeFiles(origin))^
45+
func moveFile(from origin: URL, to destination: URL) -> IO<FileSystemError, Void> {
46+
copy(item: origin, to: destination)
47+
.followedBy(removeFiles([origin]))^
4348
.mapError { _ in .move(from: origin, to: destination) }
4449
}
4550

46-
func moveFiles(in input: String, to output: String) -> IO<FileSystemError, ()> {
47-
let items = IO<FileSystemError, [String]>.var()
51+
func moveFiles(in input: URL, to output: URL) -> IO<FileSystemError, Void> {
52+
let items = IO<FileSystemError, [URL]>.var()
4853

4954
return binding(
50-
items <- self.items(atPath: input),
51-
|<-self.copy(items: items.get, from: input, to: output),
55+
items <- self.items(at: input),
56+
|<-self.copy(items: items.get.map(\.path), from: input, to: output),
5257
|<-self.removeDirectory(input),
5358
yield: ()
5459
)^.mapError { _ in .move(from: input, to: output) }
5560
}
5661

57-
func rename(_ newName: String, itemAt: String) -> IO<FileSystemError, ()> {
58-
moveFile(from: itemAt, to: "\(itemAt.parentPath)/\(newName)")
62+
func rename(with newName: String, item: URL) -> IO<FileSystemError, Void> {
63+
let newItem = item.deletingLastPathComponent().appendingPathComponent(newName)
64+
return moveFile(from: item, to: newItem)
5965
}
6066
}

OpenApiGenerator/Error/FileSystemError.swift

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,35 @@
33
import Foundation
44

55
public enum FileSystemError: Error {
6-
case create(item: String)
7-
case copy(from: String, to: String)
8-
case remove(item: String)
9-
case move(from: String, to: String)
10-
case get(from: String)
11-
case read(file: String)
12-
case write(file: String)
6+
case create(item: URL)
7+
case copy(from: URL, to: URL)
8+
case remove(item: URL)
9+
case move(from: URL, to: URL)
10+
case get(from: URL)
11+
case read(file: URL)
12+
case invalidContent(info: String)
13+
case write(file: URL)
1314
}
1415

1516
extension FileSystemError: CustomStringConvertible {
1617
public var description: String {
1718
switch self {
1819
case .create(let item):
19-
return "cannot create item '\(item)'"
20+
return "cannot create item '\(item.path)'"
2021
case .copy(let from, let to):
21-
return "cannot copy item at '\(from)' to '\(to)'"
22+
return "cannot copy item at '\(from.path)' to '\(to.path)'"
2223
case .remove(let item):
23-
return "cannot remove item at '\(item)'"
24+
return "cannot remove item at '\(item.path)'"
2425
case .move(let from, let to):
25-
return "cannot move item from '\(from)' to '\(to)'"
26+
return "cannot move item from '\(from.path)' to '\(to.path)'"
2627
case .get(let from):
27-
return "cannot get items from '\(from)'"
28+
return "cannot get items from '\(from.path)'"
2829
case .read(let file):
29-
return "cannot read content of file '\(file)'"
30+
return "cannot read content of file '\(file.path)'"
31+
case .invalidContent(let info):
32+
return "invalid content file \(info)"
3033
case .write(let file):
31-
return "cannot write in file '\(file)'"
34+
return "cannot write in file '\(file.path)'"
3235
}
3336
}
3437
}

OpenApiGenerator/Error/GeneratorError.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public enum GeneratorError: Error {
77
case invalidParameters
88
case templateNotFound
99
case structure
10+
case existOutput(directory: URL)
1011
case generator
1112
}
1213

@@ -19,6 +20,8 @@ extension GeneratorError: CustomStringConvertible {
1920
return "templates for generating Bow client have not been found"
2021
case .structure:
2122
return "could not create project structure"
23+
case .existOutput(let directory):
24+
return "output directory '\(directory.path)' already exists"
2225
case .generator:
2326
return "command 'swagger-codegen' failed"
2427
}

0 commit comments

Comments
 (0)