Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Sources/CSVParsing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation
extension Spritz {
/// List of possible decoding errors.
public enum ParsingError: Error {
/// The `csv` file was not found.
/// The `CSV` file was not found.
case fileNotFound
/// A data being parsed is corrupted.
case corruptedData(_ message: String)
Expand All @@ -26,16 +26,16 @@ extension Spritz {
internal static func parseCSV(for country: Country) throws -> [PlaceOfBirth] {
let fileName = country == .italy ? "comuni" : "stati"

guard let filepath = Spritz.bundle?.path(forResource: fileName, ofType: "csv") else {
guard let filePath = Spritz.bundle?.path(forResource: fileName, ofType: "csv") else {
throw Spritz.ParsingError.fileNotFound
}

guard let content = try? String(contentsOfFile: filepath) else {
guard let content = try? String(contentsOfFile: filePath) else {
throw Spritz.ParsingError.corruptedData("Could not stringify the content of the file.")
}

return try content.components(separatedBy: "\n").compactMap { newEntry -> PlaceOfBirth? in
// For some reason, there is always an empty line at the end of the csv, when parsing its components.
// For some reason, there is always an empty line at the end of the CSV, when parsing its components.
// This makes sure if that is the case, to return a nil which is filtered through the `compactMap`.
if newEntry.isEmpty { return nil }

Expand Down
4 changes: 2 additions & 2 deletions Sources/PlaceOfBirth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

import Foundation

/// A `typealias` for a statistical code used to represent a country or an italian municipality.
/// A `typealias` for a statistical code used to represent a country or an Italian municipality.
public typealias CodiceStatistico = String

/// A single italian municipality. There are over 7000 of them all over italy.
/// A single Italian municipality. There are over 7000 of them all over Italy.
/// Each one has a unique code, that code is used in the `Codice Fiscale`.
struct PlaceOfBirth: Hashable {
/// The `CodiceStatistico` of the municipality.
Expand Down
14 changes: 7 additions & 7 deletions Sources/Spritz.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import Foundation

/// The main class of the pod. It can not be instantiated, nor it should be since all methods are static.
/// It provides a variable `placesOfBirth` which is a tuple containing all places of birth divided by the two keys: `italian` and `foreign`.
/// It provides a variable `placesOfBirth` which is a tuple containing all places of birth divided by the two keys: `Italian` and `foreign`.
public class Spritz {
// This class does not need to be instantiated.
internal init() {}

// MARK: - Public Properties

/// A tuple with two keys `italian` and `foreign`, each containing an array with all the places in italy and countries respectively.
/// A tuple with two keys `Italian` and `foreign`, each containing an array with all the places in Italy and countries respectively.
public static var placesOfBirth: (italian: [String], foreign: [String]) {
(italianPlacesOfBirth.map{$0.name}, foreignPlacesOfBirth.map{$0.name})
}
Expand All @@ -38,7 +38,7 @@ public class Spritz {
/// - Parameters:
/// - codiceFiscale: The `Codice Fiscale` to control.
/// - fields: The `CodiceFiscaleFields` to check. Defaults to `.all`. If `.dateOfBirth` or `.sex` are excluded, both are not checked for they are related.
public static func isValid(_ codiceFiscale: String, inlcude fields: CodiceFiscaleFields = .all) throws -> Result<Bool, Spritz.ParsingError> {
public static func isValid(_ codiceFiscale: String, include fields: CodiceFiscaleFields = .all) throws -> Result<Bool, Spritz.ParsingError> {
let filteredFromOmocodia = try Spritz.filterOmocodia(in: codiceFiscale)
let array = filteredFromOmocodia.uppercased().map { String($0) }
guard array.count == 16 else { return .failure(.corruptedData("CF should be 16 character long")) }
Expand Down Expand Up @@ -121,7 +121,7 @@ public class Spritz {
/// - codiceFiscale: The `Codice Fiscale` to control.
/// - fields: The `CodiceFiscaleFields` to check. Defaults to `.all`. If `.dateOfBirth` or `.sex` are excluded, both are not checked for they are related.
public static func isValid(_ codiceFiscale: String, inlcude fields: CodiceFiscaleFields = .all) -> Bool {
(try? Spritz.isValid(codiceFiscale, inlcude: fields).get()) != nil
(try? Spritz.isValid(codiceFiscale, include: fields).get()) != nil
}

/// Returns a `Bool` based on a passed `Codice Fiscale` and information.
Expand All @@ -132,7 +132,7 @@ public class Spritz {
(try? Spritz.isValid(codiceFiscale, for: info).get()) != nil
}

/// Checks if the passed `Codice FIscale` is properly structured regardless of info.
/// Checks if the passed `Codice Fiscale` is properly structured regardless of info.
/// This is a very high level check and should not be used unless necessary for lack of data.
public static func isProperlyStructured(_ codiceFiscale: String) -> Bool {
let pattern = "^[a-zA-Z]{6}[0-9]{2}[abcdehlmprstABCDEHLMPRST]{1}[0-9]{2}([a-zA-Z]{1}[0-9]{3})[a-zA-Z]{1}$"
Expand All @@ -146,7 +146,7 @@ public class Spritz {
// MARK: - internal Properties

extension Spritz {
/// The vowels in the italian language.
/// The vowels in the Italian language.
internal static let italianVowels = "AEIOU"

/// The `Spritz` bundle.
Expand Down Expand Up @@ -193,7 +193,7 @@ extension Spritz {
internal extension Spritz {
/// Omocodia is when two or more people has the same first and last name,
/// born on the same day, of the same year from the same sex in the same municipality.
/// In such dase, starting from the most right number, one digit is changed from a number to a letter based on a special table.
/// In such case, starting from the most right number, one digit is changed from a number to a letter based on a special table.
/// The control letter however remains the same. Therefore we can strip the CF from the conversions and treat it like a normal CF.
static func filterOmocodia(in codiceFiscale: String) throws -> String {
var array = codiceFiscale.uppercased().map { String($0) }
Expand Down
12 changes: 6 additions & 6 deletions Sources/SpritzInformationProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public protocol SpritzInformationProvider {
var dateOfBirth: Date { get set }
/// The sex of the user.
var sex: Sex { get set }
/// The italian province or country where the user was born.
/// Should use full name. Do not use abreviation, or any sort of representation.
/// Refer to `comuni.csv` or `stati.csv` for the proper names as per italian bureaucracy.
/// The Italian province or country where the user was born.
/// Should use full name. Do not use abbreviation, or any sort of representation.
/// Refer to `comuni.csv` or `stati.csv` for the proper names as per Italian bureaucracy.
var placeOfBirth: String { get set }
}

Expand All @@ -44,9 +44,9 @@ public struct CodiceFiscaleFields: OptionSet {
public static let dateOfBirth = CodiceFiscaleFields(rawValue: 1 << 2)
/// The sex of the user.
public static let sex = CodiceFiscaleFields(rawValue: 1 << 3)
/// The italian province or country where the user was born.
/// Should use full name. Do not use abreviation, or any sort of representation.
/// Refer to `comuni.csv` or `stati.csv` for the proper names as per italian bureaucracy.
/// The Italian province or country where the user was born.
/// Should use full name. Do not use abbreviation, or any sort of representation.
/// Refer to `comuni.csv` or `stati.csv` for the proper names as per Italian bureaucracy.
public static let placeOfBirth = CodiceFiscaleFields(rawValue: 1 << 4)
/// A list containing all the possibile options.
public static let all: CodiceFiscaleFields = [.firstName, .lastName, .dateOfBirth, .sex, .placeOfBirth]
Expand Down
4 changes: 2 additions & 2 deletions Sources/String+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
import Foundation

internal extension String {
/// Mutates the string from all whietpsaces, special characters and accents.
/// Mutates the string from all whitespaces, special characters and accents.
mutating func stripForCF() {
self = strippedForCF()
}

/// Returns a copy of the string which is stripped from all whietpsaces, special characters and accents.
/// Returns a copy of the string which is stripped from all whitespaces, special characters and accents.
func strippedForCF() -> String {
self.filter { $0.isLetter }.folding(options: .diacriticInsensitive, locale: .current)
}
Expand Down
30 changes: 15 additions & 15 deletions Sources/Transformers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

internal extension Spritz {

/// Namespacing functions that do all the transformation
/// Name spacing functions that do all the transformation
enum Transformer {}
}

Expand Down Expand Up @@ -88,17 +88,17 @@ internal extension Spritz.Transformer {

/// Extract the code associated to the place of birth.
static func placeOfBirth(_ place: String) throws -> CodiceStatistico {
var occurence = Spritz.italianPlacesOfBirth.first { $0.name.lowercased().strippedForCF() == place.lowercased().strippedForCF() }
if let comune = occurence { return comune.code }
occurence = Spritz.foreignPlacesOfBirth.first { $0.name.lowercased().strippedForCF() == place.lowercased().strippedForCF() }
if let country = occurence { return country.code }
var occurrence = Spritz.italianPlacesOfBirth.first { $0.name.lowercased().strippedForCF() == place.lowercased().strippedForCF() }
if let comune = occurrence { return comune.code }
occurrence = Spritz.foreignPlacesOfBirth.first { $0.name.lowercased().strippedForCF() == place.lowercased().strippedForCF() }
if let country = occurrence { return country.code }
throw Spritz.ParsingError.corruptedData("Could not find the codice statistico for \(place). Make sure the spelling was correct.")
}

/// Calculates the CF final control character based on the first 15 characters.
/// - Parameter currentCF: The first 15 characters of the `Codice Fiscale`.
static func controlCharacter(for currentCF: String) -> String {
assert(currentCF.count == 15, "The passed `CF` should be 15 chracters long. Received \(currentCF) instead.")
assert(currentCF.count == 15, "The passed `CF` should be 15 characters long. Received \(currentCF) instead.")

let stringAsArrayOfCharacters = currentCF.map { String($0) }

Expand Down Expand Up @@ -184,7 +184,7 @@ internal extension Spritz.Transformer {

/// The letter equivalent of each month.
enum MonthRepresentation: Int, CaseIterable {
case A = 1, B, C, D, E, F, G, H, I, J, K, L
case A = 1, B, C, D, E, H, L, M, P, R, S, T

init?(stringValue: String) {
let found = MonthRepresentation.allCases.first { $0.asString == stringValue.uppercased()}
Expand All @@ -202,17 +202,17 @@ internal extension Spritz.Transformer {
var maxDaysPerMonth: Int {
switch self {
case .A: return 31
case .B: return 29 // we assum it is 29, because there is no way to know if the year is leap or not from the two digits alone.
case .B: return 29 // we assume it is 29, because there is no way to know if the year is leap or not from the two digits alone.
case .C: return 31
case .D: return 30
case .E: return 31
case .F: return 30
case .G: return 31
case .H: return 31
case .I: return 30
case .J: return 31
case .K: return 30
case .H: return 30
case .L: return 31
case .M: return 31
case .P: return 30
case .R: return 31
case .S: return 30
case .T: return 31
}
}
}
Expand Down Expand Up @@ -299,7 +299,7 @@ internal extension Spritz.Transformer {
}
}

/// The value of the ineger as a letter when substituted to overcome `Omocodia`.
/// The value of the integer as a letter when substituted to overcome `Omocodia`.
var equivalentForOmocodia: String {
switch self {
case .zero : return "L"
Expand Down
2 changes: 1 addition & 1 deletion Tests/SpritzTransformers+Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class SpritzValidatorTests: XCTestCase {

// timeinterval 880151077 = 21 novembre 1997
let letters2 = try! Spritz.Transformer.birthDateAndSex(sex: .female, birthdate: Date(timeIntervalSince1970: 880151077))
XCTAssertEqual(letters2, "97K61")
XCTAssertEqual(letters2, "97S61")
}

func testFetchingPlaceOfBirthCodeCorrectlyForeign() {
Expand Down