Swift: Deconstruct SPF: asMechanism with Qualifier

Hi,

In this article, I will walk through how I re-implemented the enum for Qualifier and MechanismKind. I will also deliver a working asMechanism() function. The diff is here

Enum Qualifier and MechanismKind

I reworked these to make them more self documenting and to also simplify the get function. This function could be refactored out.

Code

enum Qualifier: String {
    case Pass = "+";
    case Fail = "-";
    case SoftFail = "~";
    case Neutral = "?";
    case None = "";
    
    func get() -> String {
        return self.rawValue;
    }
}
enum MechanismKind: String {
    case Redirect = "redirect=";
    case Include = "include:"
    case A = "a";
    case MX = "mx";
    case Ip4 = "ip4:";
    case Ip6 = "ip6:"
    case All = "all";
    
    func get() -> String {
        return self.rawValue;
    }
}

Here we can see I have provided each enum case with a new rawValue and that the get function has been greatly simplified as there is no need for a switch statement. These raw values will be used by asMechanism to build the string which it returns.

asMechanism

The asMechanism function now has the ability to access the rawValue of kind and qualifier through their member functions get.

Code

func asMechanism() -> String {
    var mechanismString = String();
    mechanismString += self.qualifier.get();
    // Access the string for this mechanism's kind.
    mechanismString += self.kind.get();
    // Access the string for the mechanism
    mechanismString += self.mechanism;
    return mechanismString;
}

Testing asMechanism

I will list a few examples here.

func testAsMechanismIp4() {
    let Mech = Mechanism(k: MechanismKind.Ip4, q: Qualifier.None, m: "192.168.1.0/24");
    XCTAssertEqual(Mech.asMechanism(), "ip4:192.168.1.0/24");
}
func testAsMechanismIp4Pass() {
    let Mech = Mechanism(k: MechanismKind.Ip4, q: Qualifier.Pass, m: "192.168.1.0/24");
    XCTAssertEqual(Mech.asMechanism(), "+ip4:192.168.1.0/24");
}

func testAsMechanismIp6() {
    let Mech = Mechanism(k: MechanismKind.Ip6, q: Qualifier.None, m: "X:X:X:X/16");
    XCTAssertEqual(Mech.asMechanism(), "ip6:X:X:X:X/16");
}
func testAsMechanismRedirect() {
    let Mech = Mechanism(k: MechanismKind.Redirect, q: Qualifier.None, m: "_spf.example.com");
    XCTAssertEqual(Mech.asMechanism(), "redirect=_spf.example.com");
}
func testAsMechanismInclude() {
    let Mech = Mechanism(k: MechanismKind.Include, q: Qualifier.None, m: "_spf1.example.com");
    XCTAssertEqual(Mech.asMechanism(), "include:_spf1.example.com");
}
func testAsMechanismA() {
    let Mech = Mechanism(k: MechanismKind.A, q: Qualifier.None, m: "");
    XCTAssertEqual(Mech.asMechanism(), "a");
}
func testAsMechanismMx() {
    let Mech = Mechanism(k: MechanismKind.MX, q: Qualifier.None, m: "");
    XCTAssertEqual(Mech.asMechanism(), "mx");
}

Here is an example output within Xcode
Xcode Test Output
The Green check marks denote passing tests.

The code for this project can be found here.

This is the diff between Initial-asMechanism and QualifierSuppportAsMechanism.


See also