Skip to content

Commit

Permalink
Lots more support for config files
Browse files Browse the repository at this point in the history
  • Loading branch information
mattmassicotte committed Nov 2, 2023
1 parent b7fe06d commit 0dfcb70
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 7 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,21 @@ targets: [

Just point the `xclint` binary at your `.xcodeproj`. Check out its `-h` flag for more usage.

⚠️ `.xclint.yml` support is a WIP. It does not work correctly at the moment.

```
# xclint /path/to/MyProject.xcodeproj
```

This will run a default set of rules. But, you can customize its behavior with a `.xclint.yml` file. The basic structure borrows a lot from [SwiftLint](https://github.com/realm/SwiftLint).

```yaml
embedded_build_settings: warning
disabled_rules: ["dont", "run", "these"]
# Some rules may not be appropriate for all projects. You must opt-in those.
opt_in_rules:
- embedded_build_setting # checks for build settings in the project file
- groups_sorted # checks that all group contents are alphabetically sorted

# Other rules make sense for all projects by default. You must opt-out of those.
disabled_rules:
- build_files_ordered. # checks that the ordering of techincally-unordered collections Xcode writes out is preserved
```
## Alternatives
Expand Down
24 changes: 22 additions & 2 deletions Sources/XCLinting/ConfigurationFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import Foundation

public struct Configuration: Hashable {
public var disabledRules: Set<String>
public var optInRules: Set<String>
public var rules: [String: RuleConfiguration]

public init(
rules: [String: RuleConfiguration] = [:],
disabledRules: Set<String> = Set()
disabledRules: Set<String> = Set(),
optInRules: Set<String> = Set()
) {
self.rules = rules
self.disabledRules = disabledRules
self.optInRules = optInRules
}
}

Expand Down Expand Up @@ -44,9 +47,11 @@ extension Configuration: Decodable {
/// Contains all the top-level well-defined keys used in a configuration file
enum PredefinedKeys: String {
case disabledRules = "disabled_rules"
case optInRules = "opt_in_rules"
}

case disabledRules
case optInRules
case ruleIdentifier(String)

init?(intValue: Int) {
Expand All @@ -57,6 +62,8 @@ extension Configuration: Decodable {
switch stringValue {
case PredefinedKeys.disabledRules.rawValue:
self = .disabledRules
case PredefinedKeys.optInRules.rawValue:
self = .optInRules
default:
self = .ruleIdentifier(stringValue)
}
Expand All @@ -66,6 +73,8 @@ extension Configuration: Decodable {
switch self {
case .disabledRules:
PredefinedKeys.disabledRules.rawValue
case .optInRules:
PredefinedKeys.optInRules.rawValue
case let .ruleIdentifier(value):
value
}
Expand All @@ -80,13 +89,16 @@ extension Configuration: Decodable {
let container = try decoder.container(keyedBy: CodingKeys.self)

self.disabledRules = Set()
self.optInRules = Set()

var decodedRules = [String: RuleConfiguration]()

for key in container.allKeys {
switch key {
case .disabledRules:
self.disabledRules = try container.decode(Set<String>.self, forKey: key)
case .optInRules:
self.optInRules = try container.decode(Set<String>.self, forKey: key)
case let .ruleIdentifier(name):
let ruleConfig = try container.decode(RuleConfiguration.self, forKey: key)

Expand All @@ -105,13 +117,21 @@ extension Configuration: Decodable {
throw XCLintError.unrecognizedRuleName(id)
}
}

for id in optInRules {
if allIdentifiers.contains(id) == false {
throw XCLintError.unrecognizedRuleName(id)
}
}
}
}

extension Configuration {
public var enabledRules: Set<String> {
let defaultIdentifiers = XCLinter.defaultRuleIdentifiers

return defaultIdentifiers.subtracting(disabledRules)
return defaultIdentifiers
.union(optInRules)
.subtracting(disabledRules)
}
}
1 change: 0 additions & 1 deletion Sources/XCLinting/XCLinter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ extension XCLinter.Environment {

extension XCLinter {
public static let defaultRuleIdentifiers: Set<String> = [
"embedded_build_setting",
"build_files_ordered"
]

Expand Down
14 changes: 14 additions & 0 deletions Tests/XCLintTests/ConfigurationFileTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ final class ConfigurationTests: XCTestCase {
XCTAssertEqual(config, expected)
}

func testReadOptInRules() throws {
let string = """
{
"opt_in_rules": ["a", "b", "c"]
}
"""

let config = try JSONDecoder().decode(Configuration.self, from: Data(string.utf8))

let expected = Configuration(optInRules: Set(["a", "b", "c"]))

XCTAssertEqual(config, expected)
}

func testReadRules() throws {
let string = """
{
Expand Down

0 comments on commit 0dfcb70

Please sign in to comment.