kemchenj
10/8/2017 - 11:44 AM

String Subscript Sugar

字符串的下标取值语法糖

import Foundation

let str = """
床前明月光,疑是地上霜。
举头望明月,低头思故乡。
"""

str[3...]
str[...3]
str[..<3]
str[3...11]

str[(-11)...]
str[...(-11)]
import Foundation

fileprivate
extension String {
    
    func validIndex(original: Int) -> String.Index {
        switch original {
        case ...startIndex.encodedOffset : return startIndex
        case endIndex.encodedOffset...   : return endIndex
        default                          : return index(startIndex, offsetBy: original)
        }
    }
    
    func validStartIndex(original: Int) -> String.Index? {
        guard original <= endIndex.encodedOffset else { return nil }
        return validIndex(original:original)
    }
    
    func validEndIndex(original: Int) -> String.Index? {
        guard original >= startIndex.encodedOffset else { return nil }
        return validIndex(original:original)
    }
}

public
extension String {
    
    subscript(_ range: Range<Int>) -> String {
        guard
            let startIndex = validStartIndex(original: range.lowerBound),
            let endIndex   = validEndIndex(original: range.upperBound),
            startIndex < endIndex
        else {
            return ""
        }
        
        return String(self[startIndex..<endIndex])
    }
    
    subscript(_ range: CountableRange<Int>) -> String {
        guard
            let startIndex = validStartIndex(original: range.lowerBound),
            let endIndex   = validEndIndex(original: range.upperBound),
            startIndex < endIndex
        else {
            return ""
        }
        
        return String(self[startIndex..<endIndex])
    }
    
    subscript(_ range: ClosedRange<Int>) -> String {
        guard
            let startIndex = validStartIndex(original: range.lowerBound),
            let endIndex   = validEndIndex(original: range.upperBound)
        else {
            return ""
        }
        
        return String(self[startIndex...endIndex])
    }
    
    subscript(_ range: CountableClosedRange<Int>) -> String {
        guard
            let startIndex = validStartIndex(original: range.lowerBound),
            let endIndex   = validEndIndex(original: range.upperBound)
        else {
            return ""
        }
        
        return String(self[startIndex...endIndex])
    }
    
    subscript(_ range: PartialRangeFrom<Int>) -> String {
        guard let startIndex = validStartIndex(original: range.lowerBound) else {
            return ""
        }
        
        return String(self[startIndex...])
    }
    
    subscript(_ range: CountablePartialRangeFrom<Int>) -> String {
        guard let startIndex = validStartIndex(original: range.lowerBound) else {
            return ""
        }
        
        return String(self[startIndex...])
    }
    
    subscript(_ range: PartialRangeThrough<Int>) -> String {
        guard let endIndex = validEndIndex(original: range.upperBound) else {
            return ""
        }
        
        return String(self[...endIndex])
    }
    
    subscript(_ range: PartialRangeUpTo<Int>) -> String {
        guard
            let endIndex = validEndIndex(original: range.upperBound),
            endIndex > startIndex
        else {
            return ""
        }
        
        return String(self[..<endIndex])
    }
}