jgsamudio
7/25/2018 - 9:21 PM

Directory.swift

protocol MenuItem {
    var name: String { get }
}
struct FileItem: MenuItem {
    let name: String
    let sourceFileLoader: SourceFileLoader
}
struct Directory: MenuItem {

    /// Name of the directory. Used to display on the outline view.
    let name: String

    /// Children of the directory.
    let children: [MenuItem]

    /// Non empty children items only.
    let nonEmptyChildren: [MenuItem]

    /// Total count of the directory's children.
    var childrenCount: Int {
        var count = 0
        for child in children {
            if let directory = child as? Directory {
                count += directory.isEmptyDirectory ? 0 : 1
            } else {
                count += 1
            }
        }
        return count
    }

    /// Determines if the directory is empty.
    var isEmptyDirectory: Bool {
        var isEmpty = true
        for child in children {
            if let directory = child as? Directory {
                isEmpty = isEmpty && directory.isEmptyDirectory
            } else {
                return false
            }
        }
        return isEmpty
    }

    init(name: String, children: [MenuItem]) {
        self.name = name
        self.children = children
        nonEmptyChildren = children.filter { !(($0 as? Directory)?.isEmptyDirectory ?? false) }
    }

    /// Filters the children items with the given parameters.
    ///
    /// - Parameter filter: String to filter children by.
    /// - Returns: Updated directory object with children filtered.
    func filter(_ filter: String) -> Directory {
        guard !filter.isEmpty else {
            return self
        }
        let filteredChildren = children.compactMap { matchedChild(filter: filter, child: $0) }
        return Directory(name: name, children: filteredChildren)
    }

    /// Returns the matched child of the filter provided.
    ///
    /// - Parameters:
    ///   - filter: Filter to check for.
    ///   - child: Child used to check its name against.
    /// - Returns: Optional menu item.
    func matchedChild(filter: String, child: MenuItem) -> MenuItem? {
        if let directory = child as? Directory {
            return directory.filter(filter)
        } else if let fileItem = child as? FileItem, child.name.lowercased().contains(filter.lowercased()) {
            return fileItem
        }
        return nil
    }

}