Skip to content

Commit 2692fbf

Browse files
Support elseif/else directive (#328)
* Create `ImportContent` which can recursively collect imports. * Change `ImportMap` type to make it more structured. * Add Clause type to IfMacroModel. * Parse IfConfigDeclSyntax recursively and extract imports inside them as structured Import type. * Render all entities of IfMacroModel. * Update import rendering logic to consider both top-level imports and conditional imports. * Update tests around IfMacro * Add public access modifier to fix build error. * Add testcases for IfMacro. * Add testcases for nested macro and duplicatedImports in macro. * Delete `ParsedImports` type, and use `[ImportContent]` * Remove public acl from SourceParser and related types. * Remove unnecessary changes * Change from function-focused to structure-focused modeling * Combine duplicate processes * Fix redundant logic * The `prefix` `suffix` became unnecessary with the introduction of ConditionalImportBlock. * remove unused propertry * Integrate separated logic between topLevel and nested * Add test and handle @testable in nested import * Remove unused property * stop using legacy style coding * Fix waning * Remove unnecessary changes * Rename ambiguous name --------- Co-authored-by: Iceman <okamura@qoncept.co.jp>
1 parent f7502e5 commit 2692fbf

13 files changed

Lines changed: 640 additions & 187 deletions

Sources/Mockolo/Executor.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ struct Executor: ParsableCommand {
192192
do {
193193
try generate(sourceDirs: srcDirs,
194194
sourceFiles: srcs,
195-
parser: SourceParser(),
196195
exclusionSuffixes: exclusionSuffixes,
197196
mockFilePaths: mockFilePaths,
198197
annotation: annotation,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// Copyright (c) 2018. Uber Technologies
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//
16+
17+
/// Represents import content: either a simple import statement or a nested conditional block
18+
indirect enum ImportContent {
19+
case simple(Import)
20+
case conditional(ConditionalImportBlock)
21+
}
22+
23+
/// Represents a conditional import block (#if/#elseif/#else/#endif)
24+
struct ConditionalImportBlock {
25+
/// Represents a single clause in a conditional import block
26+
struct Clause {
27+
var type: IfClauseType
28+
var contents: [ImportContent]
29+
}
30+
31+
let clauses: [Clause]
32+
let offset: Int64
33+
34+
init(clauses: [Clause], offset: Int64) {
35+
self.clauses = clauses
36+
self.offset = offset
37+
}
38+
}

Sources/MockoloFramework/Models/IfMacroModel.swift

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,65 @@
1414
// limitations under the License.
1515
//
1616

17+
/// Represents the type of a clause in an #if/#elseif/#else block
18+
enum IfClauseType {
19+
case `if`(_ condition: String)
20+
case elseif(_ condition: String)
21+
case `else`
22+
23+
var condition: String? {
24+
switch self {
25+
case .if(let condition), .elseif(let condition):
26+
return condition
27+
case .else:
28+
return nil
29+
}
30+
}
31+
}
32+
1733
final class IfMacroModel: Model {
18-
let name: String
34+
/// Represents a single clause in a conditional compilation block
35+
struct Clause {
36+
var type: IfClauseType
37+
var entities: [(String, Model)]
38+
}
39+
40+
let clauses: [Clause]
1941
let offset: Int64
20-
let entities: [(String, Model)]
2142

2243
var modelType: ModelType {
23-
return .macro
44+
.macro
45+
}
46+
47+
var name: String {
48+
clauses.first?.type.condition ?? ""
2449
}
2550

2651
var fullName: String {
27-
return entities.map {$0.0}.joined(separator: "_")
52+
clauses.flatMap(\.entities).map { $0.0 }.joined(separator: "_")
2853
}
29-
30-
init(name: String,
31-
offset: Int64,
32-
entities: [(String, Model)]) {
33-
self.name = name
34-
self.entities = entities
54+
55+
/// Creates an IfMacroModel with multiple clauses
56+
init(clauses: [Clause], offset: Int64) {
57+
self.clauses = clauses
3558
self.offset = offset
3659
}
37-
60+
61+
/// Initializer for simple #if blocks
62+
convenience init(name: String,
63+
offset: Int64,
64+
entities: [(String, Model)]) {
65+
let clause = Clause(type: .if(name), entities: entities)
66+
self.init(clauses: [clause], offset: offset)
67+
}
68+
3869
func render(
3970
context: RenderContext,
4071
arguments: GenerationArguments
4172
) -> String? {
42-
return applyMacroTemplate(
43-
name: name,
73+
applyMacroTemplate(
4474
context: context,
45-
arguments: arguments,
46-
entities: entities
75+
arguments: arguments
4776
)
4877
}
4978
}

Sources/MockoloFramework/Models/Import.swift

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -61,36 +61,25 @@ struct Import: CustomStringConvertible {
6161

6262
/// Name of the module
6363
var moduleName: String
64+
6465

6566
/// A modifier preceding the "import" keyword (e.g. public, internal, @testable)
6667
var modifier: Modifier?
67-
68-
/// An opaque string preceding the entire import statement (typically `#if FOO\n` for nested macro support)
69-
var prefix: String?
70-
71-
/// An opaque string following the entire import statement (typically `\n#endif` for nested macro support)
72-
var suffix: String?
73-
68+
7469
var description: String {
75-
let line: String
7670
if let modifier {
77-
line = "\(modifier.rawValue) import \(moduleName)"
71+
return "\(modifier.rawValue) import \(moduleName)"
7872
} else {
79-
line = "import \(moduleName)"
73+
return "import \(moduleName)"
8074
}
81-
return [prefix, line, suffix].compactMap { $0 }.joined()
8275
}
8376

8477
init(
8578
moduleName: String,
86-
modifier: Modifier? = nil,
87-
prefix: String? = nil,
88-
suffix: String? = nil
79+
modifier: Modifier? = nil
8980
) {
9081
self.moduleName = moduleName
9182
self.modifier = modifier
92-
self.prefix = prefix
93-
self.suffix = suffix
9483
}
9584
}
9685

@@ -104,7 +93,6 @@ extension Import {
10493
}
10594

10695
/// Creates an `Import` by parsing a `String` provided by `Generator`.
107-
/// It is typically a single line, but can be wrapped by `#if FOO\n...\n#endif` when it's a nested macro.
10896
init?(line: String) {
10997
guard let importSpaceRange = line.range(of: String.importSpace) else { return nil }
11098

@@ -121,9 +109,6 @@ extension Import {
121109
let modifierEndIndex = line.index(before: importSpaceRange.lowerBound)
122110
modifier = Modifier(rawValue: String(line[startIndex..<modifierEndIndex]))
123111
}
124-
125-
`prefix` = firstNewlineIndex.map { String(line[...$0]) }
126-
suffix = lastNewlineIndex.map { String(line[$0...]) }
127112
}
128113
}
129114

@@ -136,9 +121,7 @@ extension Array where Element == Import {
136121
/// - sorts by module name
137122
func resolved() -> [Import] {
138123
var modifierByModuleName = [String: Import.Modifier]()
139-
var prefixByModuleName = [String: String]()
140-
var suffixByModuleName = [String: String]()
141-
124+
142125
for imp in self {
143126
switch (imp.modifier, modifierByModuleName[imp.moduleName]) {
144127
case let (.acl(acl), .acl(existingACL)):
@@ -152,21 +135,12 @@ extension Array where Element == Import {
152135
default:
153136
break
154137
}
155-
156-
if let prefix = imp.prefix {
157-
prefixByModuleName[imp.moduleName] = prefix
158-
}
159-
if let suffix = imp.suffix {
160-
suffixByModuleName[imp.moduleName] = suffix
161-
}
162138
}
163139

164140
return Set(map(\.moduleName)).sorted().map {
165141
Import(
166142
moduleName: $0,
167-
modifier: modifierByModuleName[$0],
168-
prefix: prefixByModuleName[$0],
169-
suffix: suffixByModuleName[$0]
143+
modifier: modifierByModuleName[$0]
170144
)
171145
}
172146
}

Sources/MockoloFramework/Models/ParsedEntity.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ struct GenerationArguments {
150150
)
151151
}
152152

153-
public typealias ImportMap = [String: [String: [String]]]
153+
typealias ImportMap = [String: [ImportContent]]
154154

155155
/// Metadata for a type being mocked
156156
public final class Entity {

Sources/MockoloFramework/Operations/Generator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ enum InputError: Error {
2525
@discardableResult
2626
public func generate(sourceDirs: [String],
2727
sourceFiles: [String],
28-
parser: SourceParser,
2928
exclusionSuffixes: [String],
3029
mockFilePaths: [String]?,
3130
annotation: String,
@@ -47,7 +46,8 @@ public func generate(sourceDirs: [String],
4746
log("Source files or directories do not exist", level: .error)
4847
throw InputError.sourceFilesError
4948
}
50-
49+
50+
let parser = SourceParser()
5151
scanConcurrencyLimit = concurrencyLimit
5252
minLogLevel = loggingLevel
5353
var candidates = [(String, Int64)]()

0 commit comments

Comments
 (0)