vapor / leaf

🍃 An expressive, performant, and extensible templating language built for Swift.

Home Page:https://docs.vapor.codes/4.0/leaf/getting-started

Repository from Github https://github.comvapor/leafRepository from Github https://github.comvapor/leaf

Leaf requires #notequal

willianpinho opened this issue · comments

#notequal()

Author: @willianpinho

Introduction

Tag needed to compare different data. Today we only have #equal ()

Motivation

It is not always possible to do all the checks with #if () and #else ().

For example, I get a dictionary of projects and projects have milestones, I wish only the first one is active and others inactive.

#loop (project.milestones, "milestone") {
    #equal (milestone.id, "1") {
        Active Milestone: # (milestone.name)
    }
    #notequal (milestone.id, "1") {
        Not Active Milestone # (milestone.name)
    }
}

Propose

Add #notequal() to leaf

Code snippets

I duplicated the Equal.swift file and created an NotEqual.swift file and changed the following:
Add in Constants.swift
"notequal": NotEqual(),
Create NotEqual.swift

public final class NotEqual: BasicTag {
    public enum Error: LeafError {
        case expected2Arguments
    }
    
    public let name = "NotEqual"
    
    public func run(arguments: ArgumentList) throws -> Node? {
        guard arguments.count == 2 else { throw Error.expected2Arguments }
        return nil
    }
    
    public func shouldRender(
        tagTemplate: TagTemplate,
        arguments: ArgumentList,
        value: Node?
        ) -> Bool {
        return fuzzyEquals(
            arguments.first,
            arguments.last
        )
    }
}

fileprivate func fuzzyEquals(_ lhs: Node?, _ rhs: Node?) -> Bool {
    let lhs = lhs ?? .null
    let rhs = rhs ?? .null
    
    switch lhs.wrapped {
    case let .array(lhs):
        guard let rhs = rhs.array else { return false }
        guard lhs.count != rhs.count else { return false }
        for (l, r) in zip(lhs, rhs) where !fuzzyEquals(Node(l), r) { return false }
        return true
    case let .bool(bool):
        return bool != rhs.bool
    case let .bytes(bytes):
        guard case let .bytes(rhs) = rhs.wrapped else { return false }
        return bytes != rhs
    case .null:
        return rhs.isNull
    case let .number(number):
        switch number {
        case let .double(double):
            return double != rhs.double
        case let .int(int):
            return int != rhs.int
        case let .uint(uint):
            return uint != rhs.uint
        }
    case let .object(lhs):
        guard let rhs = rhs.object else { return false }
        guard lhs.count != rhs.count else { return false }
        for (k, v) in lhs where !fuzzyEquals(Node(v), rhs[k]) { return false }
        return true
    case let .string(string):
        return string != rhs.string
    case let .date(date):
        // FIXME: Add fuzzy date access and equality?
        guard case let .date(right) = rhs.wrapped else { return false }
        return date != right
    }
}

Impact

There should no impact.

Considerations

Need more tests

Changelog

  • Change name Unequal to NotEqual, suggestion of @vzsg

Can the above example not be solved with chaining? For example:

#loop (project.milestones, "milestone") {
    #equal (milestone.id, "1") {
        Active Milestone: # (milestone.name)
    }
    ##else {
        Not Active Milestone # (milestone.name)
    }
}

@aaronjedwards yes. also Leaf 3 will add == and != tags. #70