Move to root

This commit is contained in:
ensan
2023-07-23 00:34:27 +09:00
parent 6c0efe4d92
commit 98bc180c17
85 changed files with 22 additions and 74 deletions

View File

@@ -0,0 +1,69 @@
//
// AppVersion.swift
// azooKey
//
// Created by ensan on 2022/07/02.
// Copyright © 2022 ensan. All rights reserved.
//
import Foundation
/// AppVersion is a struct that represents a version of an app.
/// It is a wrapper of String that conforms to Codable, Equatable, Comparable, Hashable, LosslessStringConvertible, CustomStringConvertible.
/// It is initialized with a string that represents a version of an app.
/// The string must be in the format of "major.minor.patch".
/// The string must not contain any other characters than numbers and dots.
public struct AppVersion: Codable, Equatable, Comparable, Hashable, LosslessStringConvertible, CustomStringConvertible {
/// ParseError is an enum that represents an error that occurs when parsing a string to an AppVersion.
private enum ParseError: Error {
case nonIntegerValue
}
/// Initializes an AppVersion with a string that represents a version of an app.
public init?(_ description: String) {
if let versionSequence = try? description.split(separator: ".").map({ (value: Substring) throws -> Int in
guard let value = Int(value) else { throw ParseError.nonIntegerValue }
return value
}) {
if versionSequence.count < 1 {
self.majorVersion = 0
} else {
self.majorVersion = versionSequence[0]
}
if versionSequence.count < 2 {
self.minorVersion = 0
} else {
self.minorVersion = versionSequence[1]
}
if versionSequence.count < 3 {
self.patchVersion = 0
} else {
self.patchVersion = versionSequence[2]
}
} else {
return nil
}
}
/// Compares two AppVersions.
public static func < (lhs: AppVersion, rhs: AppVersion) -> Bool {
for (l, r) in zip([lhs.majorVersion, lhs.minorVersion, lhs.patchVersion], [rhs.majorVersion, rhs.minorVersion, rhs.patchVersion]) {
if l == r {
continue
}
return l < r
}
return false
}
public var majorVersion: Int
public var minorVersion: Int
public var patchVersion: Int
public var description: String {
"\(majorVersion).\(minorVersion).\(patchVersion)"
}
}

View File

@@ -0,0 +1,128 @@
//
// ArrayUtils.swift
//
//
// Created by ensan on 2023/04/30.
//
import Algorithms
import Foundation
@resultBuilder
public struct ArrayBuilder {
public static func buildBlock<T>(_ values: T...) -> [T] {
values
}
}
public extension Sequence {
/// Returns a sequence that contains the elements of this sequence followed by the elements of the given sequence.
/// - Parameters:
/// - sequence: A sequence of elements to chain.
/// - Returns: A sequence that contains the elements of this sequence followed by the elements of the given sequence.
@inlinable func chained<S: Sequence<Element>>(_ sequence: S) -> Chain2Sequence<Self, S> {
chain(self, sequence)
}
}
public extension Collection {
/// Returns a `Set` containing the elements of this sequence with transformed values.
/// - Parameters:
/// - transform: A closure that transforms each element of this sequence into a value that can be hashed.
/// - Returns: A `Set` containing the elements of this sequence.
@inlinable func mapSet<T>(transform closure: (Element) throws -> T) rethrows -> Set<T> {
var set = Set<T>()
set.reserveCapacity(self.count)
for item in self {
set.update(with: try closure(item))
}
return set
}
/// Returns a `Set` containing the elements of this sequence with transformed values.
/// - Parameters:
/// - transform: A closure that transforms each element of this sequence into a sequence of values that can be hashed.
/// - Returns: A `Set` containing the elements of this sequence.
@inlinable func flatMapSet<T: Sequence>(transform closure: (Element) throws -> T) rethrows -> Set<T.Element> {
var set = Set<T.Element>()
for item in self {
set.formUnion(try closure(item))
}
return set
}
/// Returns a `Set` containing the non-nil elements of this sequence with transformed values.
/// - Parameters:
/// - transform: A closure that transforms each element of this sequence into an optional value that can be hashed.
/// - Returns: A `Set` containing the non-nil elements of this sequence.
@inlinable func compactMapSet<T>(transform closure: (Element) throws -> T?) rethrows -> Set<T> {
var set = Set<T>()
set.reserveCapacity(self.count)
for item in self {
if let value = try closure(item) {
set.update(with: value)
}
}
return set
}
}
public extension MutableCollection {
/// Calls the given closure with a pointer to the array's mutable contiguous storage.
/// - Parameter
/// - transform: A closure that takes a pointer to the array's mutable contiguous storage.
@inlinable mutating func mutatingForeach(transform closure: (inout Element) throws -> Void) rethrows {
for index in self.indices {
try closure(&self[index])
}
}
}
public extension Collection {
/// Returns a SubSequence containing the elements of this sequence up to the first element that does not satisfy the given predicate.
/// - Parameters:
/// - condition: A closure that takes an element of the sequence as its argument and returns a Boolean value indicating whether the element should be included.
/// - Returns: A SubSequence containing the elements of this sequence up to the first element that does not satisfy the given predicate.
@inlinable func suffix(while condition: (Element) -> Bool) -> SubSequence {
var left = self.endIndex
while left != self.startIndex, condition(self[self.index(left, offsetBy: -1)]) {
left = self.index(left, offsetBy: -1)
}
return self[left ..< self.endIndex]
}
}
public extension Collection where Self.Element: Equatable {
/// Returns a Bool value indicating whether the collection has the given suffix.
/// - Parameters:
/// - suffix: A collection to search for at the end of this collection.
/// - Returns: A Bool value indicating whether the collection has the given suffix.
@inlinable func hasSuffix(_ suffix: some Collection<Element>) -> Bool {
if self.count < suffix.count {
return false
}
let count = suffix.count
for (i, value) in suffix.enumerated() {
if self[self.index(self.endIndex, offsetBy: i - count)] != value {
return false
}
}
return true
}
/// Returns an Array containing the common prefix of this collection and the given collection.
/// - Parameters:
/// - collection: A collection to search for a common prefix with this collection.
/// - Returns: An Array containing the common prefix of this collection and the given collection.
@inlinable func commonPrefix(with collection: some Collection<Element>) -> [Element] {
var prefix: [Element] = []
for (i, value) in self.enumerated() where i < collection.count {
if value == collection[collection.index(collection.startIndex, offsetBy: i)] {
prefix.append(value)
} else {
break
}
}
return prefix
}
}

View File

@@ -0,0 +1,314 @@
//
// extension Character.swift
// Keyboard
//
// Created by ensan on 2020/09/03.
// Copyright © 2020 ensan. All rights reserved.
//
import Foundation
public enum CharacterUtils {
///
private static let kogakiKana: Set<Character> = [
"", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", ""
]
///
private static let dakutenKana: Set<Character> = [
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
]
///
static func isKogana(_ character: Character) -> Bool {
kogakiKana.contains(character)
}
/// (a-z, A-Z)
@inlinable public static func isRomanLetter(_ character: Character) -> Bool {
character.isASCII && character.isCased
}
///
public static func kogaki(_ character: Character) -> Character {
switch character {
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
default: return character
}
}
///
public static func ogaki(_ character: Character) -> Character {
switch character {
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
case "":return ""
default: return character
}
}
///
public static func isDakuten(_ character: Character) -> Bool {
dakutenKana.contains(character)
}
///
public static func dakuten(_ character: Character) -> Character {
switch character {
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
default: return character
}
}
///
public static func mudakuten(_ character: Character) -> Character {
switch character {
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
default: return character
}
}
///
public static func isHandakuten(_ character: Character) -> Bool {
[
"", "", "", "", "",
"", "", "", "", ""
].contains(character)
}
///
public static func handakuten(_ character: Character) -> Character {
switch character {
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
default: return character
}
}
///
public static func muhandakuten(_ character: Character) -> Character {
switch character {
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
case"":return ""
default: return character
}
}
///
public static func requestChange(_ character: Character) -> String {
if character.isLowercase {
return character.uppercased()
}
if character.isUppercase {
return character.lowercased()
}
if Set(["", "", "", "", "", "", "", ""]).contains(character) {
return String(kogaki(character))
}
if Set(["", "", "", "", "", "", "", ""]).contains(character) {
return String(ogaki(character))
}
if Set(["", "", "", "", "", "", "", "", "", "", "", "", "", ""]).contains(character) {
return String(dakuten(character))
}
if Set(["", "", "", "", "", "", "", "", "", "", "", "", "", ""]).contains(character) {
return String(mudakuten(character))
}
if Set(["", ""]).contains(character) {
return String(kogaki(character))
}
if Set(["", ""]).contains(character) {
return String(dakuten(ogaki(character)))
}
if Set(["", ""]).contains(character) {
return String(mudakuten(character))
}
if Set(["", "", "", "", ""]).contains(character) {
return String(dakuten(character))
}
if Set(["", "", "", "", ""]).contains(character) {
return String(handakuten(mudakuten(character)))
}
if Set(["", "", "", "", ""]).contains(character) {
return String(muhandakuten(character))
}
return String(character)
}
}
public extension Character {
/// Returns the Katakanized version of the character.
@inlinable func toKatakana() -> Character {
if self.unicodeScalars.count != 1 {
return self
}
let scalar = self.unicodeScalars.first!
if 0x3041 <= scalar.value && scalar.value <= 0x3096 {
return Character(UnicodeScalar(scalar.value + 96)!)
} else {
return self
}
}
/// Returns the Hiraganized version of the character.
@inlinable func toHiragana() -> Character {
if self.unicodeScalars.count != 1 {
return self
}
let scalar = self.unicodeScalars.first!
if 0x30A1 <= scalar.value && scalar.value <= 0x30F6 {
return Character(UnicodeScalar(scalar.value - 96)!)
} else {
return self
}
}
}

View File

@@ -0,0 +1,18 @@
//
// CodableSupport.swift
// azooKey
//
// Created by ensan on 2021/03/17.
// Copyright © 2021 ensan. All rights reserved.
//
import Foundation
public extension Encodable {
/// Encodes this value into the given container.
/// - Parameters:
/// - container: The container to encode this value into.
func containerEncode<CodingKeys: CodingKey>(container: inout KeyedEncodingContainer<CodingKeys>, key: CodingKeys) throws {
try container.encode(self, forKey: key)
}
}

View File

@@ -0,0 +1,26 @@
//
// extension Data.swift
// azooKey
//
// Created by ensan on 2022/10/22.
// Copyright © 2022 ensan. All rights reserved.
//
import Foundation
extension Data {
/// Converts this data to an array of the given type.
/// - Parameter:
/// - type: The type to convert this data to.
/// - Returns: An array of the given type.
@inlinable public func toArray<T>(of type: T.Type) -> [T] {
self.withUnsafeBytes {pointer -> [T] in
Array(
UnsafeBufferPointer(
start: pointer.baseAddress!.assumingMemoryBound(to: type),
count: pointer.count / MemoryLayout<T>.size
)
)
}
}
}

View File

@@ -0,0 +1,79 @@
//
// Debug.swift
//
//
// Created by ensan on 2023/04/30.
//
import Foundation
/// Prints the given items to the standard output if the build setting "DEBUG" is set.
/// - Parameter:
/// - items: The items to print.
/// - Note: This function is always preferred over `print` in the codebase.
@_disfavoredOverload
@inlinable public func debug(_ items: Any...) {
#if DEBUG
var result = ""
for value in items {
if result.isEmpty {
result.append("\(value)")
} else {
result.append(" ")
result.append("\(value)")
}
}
print(result)
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any) {
#if DEBUG
print(item1())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any, _ item3: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2(), item3())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any, _ item3: @autoclosure () -> Any, _ item4: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2(), item3(), item4())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any, _ item3: @autoclosure () -> Any, _ item4: @autoclosure () -> Any, _ item5: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2(), item3(), item4(), item5())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any, _ item3: @autoclosure () -> Any, _ item4: @autoclosure () -> Any, _ item5: @autoclosure () -> Any, _ item6: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2(), item3(), item4(), item5(), item6())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any, _ item3: @autoclosure () -> Any, _ item4: @autoclosure () -> Any, _ item5: @autoclosure () -> Any, _ item6: @autoclosure () -> Any, _ item7: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2(), item3(), item4(), item5(), item6(), item7())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any, _ item3: @autoclosure () -> Any, _ item4: @autoclosure () -> Any, _ item5: @autoclosure () -> Any, _ item6: @autoclosure () -> Any, _ item7: @autoclosure () -> Any, _ item8: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2(), item3(), item4(), item5(), item6(), item7(), item8())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any, _ item3: @autoclosure () -> Any, _ item4: @autoclosure () -> Any, _ item5: @autoclosure () -> Any, _ item6: @autoclosure () -> Any, _ item7: @autoclosure () -> Any, _ item8: @autoclosure () -> Any, _ item9: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2(), item3(), item4(), item5(), item6(), item7(), item8(), item9())
#endif
}
@inlinable public func debug(_ item1: @autoclosure () -> Any, _ item2: @autoclosure () -> Any, _ item3: @autoclosure () -> Any, _ item4: @autoclosure () -> Any, _ item5: @autoclosure () -> Any, _ item6: @autoclosure () -> Any, _ item7: @autoclosure () -> Any, _ item8: @autoclosure () -> Any, _ item9: @autoclosure () -> Any, _ item10: @autoclosure () -> Any) {
#if DEBUG
print(item1(), item2(), item3(), item4(), item5(), item6(), item7(), item8(), item9(), item10())
#endif
}

View File

@@ -0,0 +1,18 @@
//
// Modify.swift
// azooKey
//
// Created by ensan on 2022/10/10.
// Copyright © 2022 ensan. All rights reserved.
//
import Foundation
/// Modifies the given value and returns the result.
/// - Parameters:
/// - value: The value to modify.
/// - process: The process to modify the value.
/// - Note: This function should be used when specific subscript setter is called for multiple times.
@inlinable public func withMutableValue<T>(_ value: inout T, process: (inout T) -> Void) {
process(&value)
}

View File

@@ -0,0 +1,117 @@
//
// extension StringProtocol.swift
// Keyboard
//
// Created by ensan on 2020/10/16.
// Copyright © 2020 ensan. All rights reserved.
//
import Foundation
public extension StringProtocol {
///
/// - note: `false`
@inlinable
var onlyRomanAlphabetOrNumber: Bool {
!isEmpty && range(of: "[^a-zA-Z0-9]", options: .regularExpression) == nil
}
///
/// - note: `false`
@inlinable
var onlyRomanAlphabet: Bool {
!isEmpty && range(of: "[^a-zA-Z]", options: .regularExpression) == nil
}
///
/// - note: `false`
/// 40
@inlinable
var containsRomanAlphabet: Bool {
for value in self.utf8 {
if (UInt8(ascii: "a") <= value && value <= UInt8(ascii: "z")) || (UInt8(ascii: "A") <= value && value <= UInt8(ascii: "Z")) {
return true
}
}
return false
}
///
/// - note: `false`
@inlinable
var isEnglishSentence: Bool {
!isEmpty && range(of: "[^0-9a-zA-Z\n !'_<>\\[\\]{}*@`\\^|~=\"#$%&\\+\\(\\),\\-\\./:;?\\\\]", options: .regularExpression) == nil
}
///
@inlinable
var isKana: Bool {
!isEmpty && range(of: "[^ぁ-ゖァ-ヶ]", options: .regularExpression) == nil
}
/// Returns a String value in which Hiraganas are all converted to Katakana.
/// - Returns: A String value in which Hiraganas are all converted to Katakana.
@inlinable func toKatakana() -> String {
// utf162utf16
let result = self.utf16.map { scalar -> UInt16 in
if 0x3041 <= scalar && scalar <= 0x3096 {
return scalar + 96
} else {
return scalar
}
}
return String(utf16CodeUnits: result, count: result.count)
}
/// Returns a String value in which Katakana are all converted to Hiragana.
/// - Returns: A String value in which Katakana are all converted to Hiragana.
@inlinable func toHiragana() -> String {
// utf162utf16
let result = self.utf16.map { scalar -> UInt16 in
if 0x30A1 <= scalar && scalar <= 0x30F6 {
return scalar - 96
} else {
return scalar
}
}
return String(utf16CodeUnits: result, count: result.count)
}
/// Returns an Index value that is the specified distance from the start index.
/// - Parameter:
/// - offset: The distance to offset from the start index.
/// - Returns: An Index value that is the specified distance from the start index.
@inlinable
func indexFromStart(_ offset: Int) -> Index {
self.index(self.startIndex, offsetBy: offset)
}
// :
/*
\ -> \\
\0 -> \0
\n -> \n
\t -> \t
, -> \c
" -> \d
*/
// please use these letters in order to avoid user-inputting text crash
func escaped() -> String {
var result = self.replacingOccurrences(of: "\\", with: "\\b")
result = result.replacingOccurrences(of: "\0", with: "\\0")
result = result.replacingOccurrences(of: "\n", with: "\\n")
result = result.replacingOccurrences(of: "\t", with: "\\t")
result = result.replacingOccurrences(of: ",", with: "\\c")
result = result.replacingOccurrences(of: " ", with: "\\s")
result = result.replacingOccurrences(of: "\"", with: "\\d")
return result
}
func unescaped() -> String {
var result = self.replacingOccurrences(of: "\\d", with: "\"")
result = result.replacingOccurrences(of: "\\s", with: " ")
result = result.replacingOccurrences(of: "\\c", with: ",")
result = result.replacingOccurrences(of: "\\t", with: "\t")
result = result.replacingOccurrences(of: "\\n", with: "\n")
result = result.replacingOccurrences(of: "\\0", with: "\0")
result = result.replacingOccurrences(of: "\\b", with: "\\")
return result
}
}