SwiftyLab / MetaCodable

Supercharge Swift's Codable implementations with macros meta-programming.
https://swiftpackageindex.com/SwiftyLab/MetaCodable/main/documentation/metacodable
MIT License
629 stars 23 forks source link

[Feature Request] 1. Default value by InitializerClauseSyntax 2. Add `GroupedDefault` for multiple bindings 3. Some builtin type infer #70

Open SouHanaQiao opened 7 months ago

SouHanaQiao commented 7 months ago

This is an amazing project about swift macros.

In the past, some implementations I needed could only be achieved by customizing official codecs such as JSONDecoder/JSONEncoder. I want to make a small contribution to the improvement of this project. For example, the following features provide discussion.

1. Default value by InitializerClauseSyntax

Provide default value by InitializerClauseSyntax (decl.binding.initializer?.value).

Example:

struct Example {
    @Default(10)
    let int: Int   /// defaul value is 10
    var int2: Int = 10 /// default value is 10
    @Default(10)
    var int3: Int = 20 /// default value is 10 when apply `Default` attribute
    var int4: Int = 4, int5: Int = 5 /// `int4` default value is 4, `int5` default value is 5
}

2. Add GroupedDefault for multiple bindings

I add a GroupedDefault member attribute like Default but attach to multiple bindings:

@attached(peer)
@available(swift 5.9)
public macro GroupedDefault<each T>(_ defaults: repeat each T) =
    #externalMacro(module: "MacroPlugin", type: "GroupedDefault")

Using Example:

struct Student {
    @GroupedDefault("Simon", 22)
    let name: String, age: Int
    @Default(60)
    let grade: Int
}

3. We can infer some types like Int, Bool, String, Float, Double with var age = 10, var string = "hello"refer to MemberwiseInit project.

struct SomeInferTest {
    var int = 10 /// Int
    var float = 0.1 as Float /// Float
    var double = 0.11 /// Double
    var string = "hello" /// String
    var bool = Bool() /// Bool
    var date = Date() /// Date
}

So we can add default value like this:

struct Student {
    var name = "unknown", age = 0
    var grade = 60
}

Same as:

struct Student {
    var name: String = "unknown", age: Int = 0
    var grade: Int = 60
}

and

struct Student {
    @GroupedDefault("unknown", 0)
    let name: String, age: Int
    @Default(60)
    let grade: Int
}

4.Just generate one init function.

@MemberInit
struct Student {
    let name: String, age: Int
    @Default(60)
    let grade: Int
    var isBoy: Bool = false 
    var isGirl: Bool = true
}

Generate 4 init function in past like this:

init(name: String, age: Int, grade: Int = 60) {
    self.name = name
    self.age = age
    self.grade = grade
}

init(name: String, age: Int, grade: Int = 60, isBoy: Bool) {
    self.name = name
    self.age = age
    self.grade = grade
    self.isBoy = isBoy
}

init(name: String, age: Int, grade: Int = 60, isGirl: Bool) {
    self.name = name
    self.age = age
    self.grade = grade
    self.isGirl = isGirl
}

init(name: String, age: Int, grade: Int = 60, isBoy: Bool, isGirl: Bool) {
    self.name = name
    self.age = age
    self.grade = grade
    self.isBoy = isBoy
    self.isGirl = isGirl
}

But now expand 1 init function like this:

init(name: String, age: Int, grade: Int = 60, isBoy: Bool = false, isGirl: Bool = true) {
    self.name = name
    self.age = age
    self.grade = grade
    self.isBoy = isBoy
    self.isGirl = isGirl
}
soumyamahunt commented 7 months ago

Thanks @SouHanaQiao for this contribution. Please create separate PRs for each feature contribution, this will help in reviewing PRs and also auto changelog generation happens per PR.