From c1e4ae2ab96a00eceff88e4aff2ffa4758f61544 Mon Sep 17 00:00:00 2001 From: Eric Meisel Date: Sat, 4 Jul 2020 23:22:52 -0500 Subject: [PATCH] Address instances, use PureConfig, introduce StringGenBuilder (#12) * Address instances, use PureConfig, introduce StringGenBuilder * None-out the mima check * set locale to US for tests (until other locales are supported) * use java_opts * fix env --- .github/workflows/scala.yml | 4 +- .scalafix.conf | 1 - build.sbt | 8 +- project/LibraryDependencies.scala | 1 + project/ScalacSettings.scala | 2 + src/main/resources/default.conf | 1 - src/main/resources/en.conf | 2 + src/main/resources/en/address.conf | 292 ++++++++++++++++++ src/main/resources/{default => en}/name.conf | 0 src/main/resources/en_US.conf | 1 + src/main/resources/en_US/address.conf | 74 +++++ .../faker/compat/LazyListCompat.scala | 2 +- src/main/scala/faker/Faker.scala | 38 +++ src/main/scala/faker/ResourceLoader.scala | 28 +- src/main/scala/faker/StringGenBuilder.scala | 118 +++++++ .../scala/faker/address/BuildingNumber.scala | 25 ++ src/main/scala/faker/address/City.scala | 22 ++ src/main/scala/faker/address/CityPrefix.scala | 20 ++ src/main/scala/faker/address/CitySuffix.scala | 20 ++ src/main/scala/faker/address/Country.scala | 19 ++ .../scala/faker/address/DefaultCountry.scala | 20 ++ .../scala/faker/address/FullAddress.scala | 19 ++ src/main/scala/faker/address/Latitude.scala | 14 + src/main/scala/faker/address/Longitude.scala | 14 + src/main/scala/faker/address/PostalCode.scala | 19 ++ .../faker/address/SecondaryAddress.scala | 25 ++ src/main/scala/faker/address/State.scala | 32 ++ .../scala/faker/address/StreetAddress.scala | 19 ++ src/main/scala/faker/address/StreetName.scala | 19 ++ .../scala/faker/address/StreetPrefix.scala | 20 ++ .../scala/faker/address/StreetSuffix.scala | 20 ++ src/main/scala/faker/internet/Avatar.scala | 2 +- .../scala/faker/internet/DomainSuffix.scala | 11 +- .../scala/faker/internet/EmailAddress.scala | 2 +- src/main/scala/faker/internet/Image.scala | 4 +- src/main/scala/faker/internet/Password.scala | 2 +- .../faker/internet/SafeEmailAddress.scala | 2 +- src/main/scala/faker/internet/UserAgent.scala | 17 +- src/main/scala/faker/lorem/LoremWord.scala | 12 +- src/main/scala/faker/name/FirstName.scala | 12 +- src/main/scala/faker/name/LastName.scala | 12 +- src/main/scala/faker/name/Prefix.scala | 15 +- src/main/scala/faker/name/Suffix.scala | 14 +- src/main/scala/faker/name/Title.scala | 29 +- src/main/scala/faker/syntax/string.scala | 20 ++ src/test/scala/faker/FakerSpec.scala | 86 ++++++ .../faker/address/AddressArbitrarySpec.scala | 25 ++ 47 files changed, 1080 insertions(+), 84 deletions(-) create mode 100644 src/main/resources/en.conf create mode 100644 src/main/resources/en/address.conf rename src/main/resources/{default => en}/name.conf (100%) create mode 100644 src/main/resources/en_US.conf create mode 100644 src/main/resources/en_US/address.conf create mode 100644 src/main/scala/faker/StringGenBuilder.scala create mode 100644 src/main/scala/faker/address/BuildingNumber.scala create mode 100644 src/main/scala/faker/address/City.scala create mode 100644 src/main/scala/faker/address/CityPrefix.scala create mode 100644 src/main/scala/faker/address/CitySuffix.scala create mode 100644 src/main/scala/faker/address/Country.scala create mode 100644 src/main/scala/faker/address/DefaultCountry.scala create mode 100644 src/main/scala/faker/address/FullAddress.scala create mode 100644 src/main/scala/faker/address/Latitude.scala create mode 100644 src/main/scala/faker/address/Longitude.scala create mode 100644 src/main/scala/faker/address/PostalCode.scala create mode 100644 src/main/scala/faker/address/SecondaryAddress.scala create mode 100644 src/main/scala/faker/address/State.scala create mode 100644 src/main/scala/faker/address/StreetAddress.scala create mode 100644 src/main/scala/faker/address/StreetName.scala create mode 100644 src/main/scala/faker/address/StreetPrefix.scala create mode 100644 src/main/scala/faker/address/StreetSuffix.scala create mode 100644 src/main/scala/faker/syntax/string.scala create mode 100644 src/test/scala/faker/address/AddressArbitrarySpec.scala diff --git a/.github/workflows/scala.yml b/.github/workflows/scala.yml index f4c30b1c..2d452355 100644 --- a/.github/workflows/scala.yml +++ b/.github/workflows/scala.yml @@ -8,9 +8,7 @@ on: jobs: build: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Set up JDK 1.8 @@ -19,3 +17,5 @@ jobs: java-version: 1.8 - name: Validate build run: sbt validate + env: + JAVA_OPTS: -Duser.country=US -Duser.language=en diff --git a/.scalafix.conf b/.scalafix.conf index fbfad1b8..57fd2031 100644 --- a/.scalafix.conf +++ b/.scalafix.conf @@ -12,7 +12,6 @@ DisableSyntax { noFinalVal = true noXml = true noDefaultArgs = true - noUniversalEquality = true } OrganizeImports { diff --git a/build.sbt b/build.sbt index 3d075809..12f4fb21 100644 --- a/build.sbt +++ b/build.sbt @@ -23,6 +23,7 @@ credentials ++= ( libraryDependencies ++= Seq( ScalaCheck, TypesafeConfig, + PureConfig, ApacheCommons, ScalaTest % Test, ScalaTestPlusScalaCheck % Test @@ -32,9 +33,10 @@ scalacOptions ++= (ScalaVersionADT.fromString(scalaVersion.value) match { case `2.12` => ScalacSettings.`2.12` case `2.13` => ScalacSettings.`2.13` }) -mimaPreviousArtifacts := Set( - "io.github.etspaceman" %% "scalacheck-faker" % "1.0.1" -) +val mimaVersion: Option[String] = None +mimaPreviousArtifacts := + mimaVersion.map("io.github.etspaceman" %% "scalacheck-faker" % _).toSet + homepage := Some(url("https://github.com/etspaceman/scalacheck-faker")) licenses := Seq( "MIT" -> url("https://github.com/etspaceman/scalacheck-faker/LICENSE") diff --git a/project/LibraryDependencies.scala b/project/LibraryDependencies.scala index dc8fb4df..c88c0923 100644 --- a/project/LibraryDependencies.scala +++ b/project/LibraryDependencies.scala @@ -9,4 +9,5 @@ object LibraryDependencies { "org.scalatestplus" %% "scalacheck-1-14" % "3.1.0.0" val KindProjector = "org.typelevel" % "kind-projector" % "0.11.0" val OrganizeImports = "com.github.liancheng" %% "organize-imports" % "0.3.1-RC3" + val PureConfig = "com.github.pureconfig" %% "pureconfig" % "0.13.0" } diff --git a/project/ScalacSettings.scala b/project/ScalacSettings.scala index b5f01630..16cccb8c 100644 --- a/project/ScalacSettings.scala +++ b/project/ScalacSettings.scala @@ -100,5 +100,7 @@ object ScalacSettings { "-Ywarn-unused:patvars", "-Ywarn-unused:privates" ).contains(x) + ) ++ Seq( + "-Ywarn-unused" ) } diff --git a/src/main/resources/default.conf b/src/main/resources/default.conf index 4de27c60..5370275c 100644 --- a/src/main/resources/default.conf +++ b/src/main/resources/default.conf @@ -1,3 +1,2 @@ include "default/lorem.conf" -include "default/name.conf" include "default/internet.conf" \ No newline at end of file diff --git a/src/main/resources/en.conf b/src/main/resources/en.conf new file mode 100644 index 00000000..3a9102cd --- /dev/null +++ b/src/main/resources/en.conf @@ -0,0 +1,2 @@ +include "en/address.conf" +include "en/name.conf" \ No newline at end of file diff --git a/src/main/resources/en/address.conf b/src/main/resources/en/address.conf new file mode 100644 index 00000000..048492c0 --- /dev/null +++ b/src/main/resources/en/address.conf @@ -0,0 +1,292 @@ +include "name.conf" + +address { + city-prefixes = [North, East, West, South, New, Lake, Port] + city-suffixes = [town, ton, land, ville, berg, burgh, borough, bury, view, port, mouth, stad, furt, chester, mouth, fort, haven, side, shire] + city-builder = { + options = [ + {parts = [{type = "seq-string-part", value = ${address.city-prefixes}, suffix = " "}, {type = "seq-string-part", value = ${name.first.names}}, {type = "seq-string-part", value = ${address.city-suffixes}}]}, + {parts = [{type = "seq-string-part", value = ${address.city-prefixes}, suffix = " "}, {type = "seq-string-part", value = ${name.first.names}}]}, + {parts = [{type = "seq-string-part", value = ${name.first.names}}, {type = "seq-string-part", value = ${address.city-suffixes}}]}, + {parts = [{type = "seq-string-part", value = ${name.last.names}}, {type = "seq-string-part", value = ${address.city-suffixes}}]} + ] + } + countries = [ + {code = "AF", name = "Afghanistan"} + {code = "AL", name = "Albania"} + {code = "DZ", name = "Algeria"} + {code = "AS", name = "American Samoa"} + {code = "AD", name = "Andorra"} + {code = "AO", name = "Angola"} + {code = "AI", name = "Anguilla"} + {code = "AQ", name = "Antarctica"} + {code = "AG", name = "Antigua And Barbuda"} + {code = "AR", name = "Argentina"} + {code = "AM", name = "Armenia"} + {code = "AW", name = "Aruba"} + {code = "AU", name = "Australia"} + {code = "AT", name = "Austria"} + {code = "AX", name = "Aland Islands"} + {code = "AZ", name = "Azerbaijan"} + {code = "BS", name = "Bahamas"} + {code = "BH", name = "Bahrain"} + {code = "BD", name = "Bangladesh"} + {code = "BB", name = "Barbados"} + {code = "BY", name = "Belarus"} + {code = "BE", name = "Belgium"} + {code = "BZ", name = "Belize"} + {code = "BJ", name = "Benin"} + {code = "BM", name = "Bermuda"} + {code = "BT", name = "Bhutan"} + {code = "BO", name = "Bolivia"} + {code = "BQ", name = "Bonaire"} + {code = "BA", name = "Bosnia And Herzegovina"} + {code = "BW", name = "Botswana"} + {code = "BV", name = "Bouvet Island"} + {code = "BR", name = "Brazil"} + {code = "IO", name = "British Indian Ocean Territory"} + {code = "BN", name = "Brunei Darussalam"} + {code = "BG", name = "Bulgaria"} + {code = "BF", name = "Burkina Faso"} + {code = "BI", name = "Burundi"} + {code = "KH", name = "Cambodia"} + {code = "CM", name = "Cameroon"} + {code = "CA", name = "Canada"} + {code = "CV", name = "Cape Verde"} + {code = "KY", name = "Cayman Islands"} + {code = "CF", name = "Central African Republic"} + {code = "TD", name = "Chad"} + {code = "CL", name = "Chile"} + {code = "CN", name = "China"} + {code = "CX", name = "Christmas Island"} + {code = "CC", name = "Cocos (keeling) Islands"} + {code = "CO", name = "Colombia"} + {code = "KM", name = "Comoros"} + {code = "CG", name = "Congo"} + {code = "CD", name = "Congo, The Democratic Republic Of The"} + {code = "CK", name = "Cook Islands"} + {code = "CR", name = "Costa Rica"} + {code = "CI", name = "Cote D'ivoire"} + {code = "HR", name = "Croatia"} + {code = "CU", name = "Cuba"} + {code = "CW", name = "Curacao"} + {code = "CY", name = "Cyprus"} + {code = "CZ", name = "Czech Republic"} + {code = "DK", name = "Denmark"} + {code = "DJ", name = "Djibouti"} + {code = "DM", name = "Dominica"} + {code = "DO", name = "Dominican Republic"} + {code = "TP", name = "East Timor"} + {code = "EC", name = "Ecuador"} + {code = "EG", name = "Egypt"} + {code = "SV", name = "El Salvador"} + {code = "GQ", name = "Equatorial Guinea"} + {code = "ER", name = "Eritrea"} + {code = "EE", name = "Estonia"} + {code = "ET", name = "Ethiopia"} + {code = "FK", name = "Falkland Islands (malvinas)"} + {code = "FO", name = "Faroe Islands"} + {code = "FJ", name = "Fiji"} + {code = "FI", name = "Finland"} + {code = "FR", name = "France"} + {code = "GF", name = "French Guiana"} + {code = "PF", name = "French Polynesia"} + {code = "TF", name = "French Southern Territories"} + {code = "GA", name = "Gabon"} + {code = "GM", name = "Gambia"} + {code = "GE", name = "Georgia"} + {code = "DE", name = "Germany"} + {code = "GH", name = "Ghana"} + {code = "GI", name = "Gibraltar"} + {code = "GR", name = "Greece"} + {code = "GL", name = "Greenland"} + {code = "GD", name = "Grenada"} + {code = "GP", name = "Guadeloupe"} + {code = "GU", name = "Guam"} + {code = "GT", name = "Guatemala"} + {code = "GG", name = "Guernsey"} + {code = "GN", name = "Guinea"} + {code = "GW", name = "Guinea-bissau"} + {code = "GY", name = "Guyana"} + {code = "HT", name = "Haiti"} + {code = "HM", name = "Heard Island And Mcdonald Islands"} + {code = "VA", name = "Holy See (vatican City State)"} + {code = "HN", name = "Honduras"} + {code = "HK", name = "Hong Kong"} + {code = "HU", name = "Hungary"} + {code = "IS", name = "Iceland"} + {code = "IN", name = "India"} + {code = "ID", name = "Indonesia"} + {code = "IR", name = "Iran, Islamic Republic Of"} + {code = "IQ", name = "Iraq"} + {code = "IE", name = "Ireland"} + {code = "IM", name = "Isle of Man"} + {code = "IL", name = "Israel"} + {code = "IT", name = "Italy"} + {code = "JM", name = "Jamaica"} + {code = "JP", name = "Japan"} + {code = "JE", name = "Jersey"} + {code = "JO", name = "Jordan"} + {code = "KZ", name = "Kazakstan"} + {code = "KE", name = "Kenya"} + {code = "KI", name = "Kiribati"} + {code = "KP", name = "Korea, Democratic People's Republic Of"} + {code = "KR", name = "Korea, Republic Of"} + {code = "KV", name = "Kosovo"} + {code = "KW", name = "Kuwait"} + {code = "KG", name = "Kyrgyzstan"} + {code = "LA", name = "Lao People's Democratic Republic"} + {code = "LV", name = "Latvia"} + {code = "LB", name = "Lebanon"} + {code = "LS", name = "Lesotho"} + {code = "LR", name = "Liberia"} + {code = "LY", name = "Libyan Arab Jamahiriya"} + {code = "LI", name = "Liechtenstein"} + {code = "LT", name = "Lithuania"} + {code = "LU", name = "Luxembourg"} + {code = "MO", name = "Macau"} + {code = "MK", name = "Macedonia, The Former Yugoslav Republic Of"} + {code = "MG", name = "Madagascar"} + {code = "MW", name = "Malawi"} + {code = "MY", name = "Malaysia"} + {code = "MV", name = "Maldives"} + {code = "ML", name = "Mali"} + {code = "MT", name = "Malta"} + {code = "MH", name = "Marshall Islands"} + {code = "MQ", name = "Martinique"} + {code = "MR", name = "Mauritania"} + {code = "MU", name = "Mauritius"} + {code = "YT", name = "Mayotte"} + {code = "MX", name = "Mexico"} + {code = "FM", name = "Micronesia, Federated States Of"} + {code = "MD", name = "Moldova, Republic Of"} + {code = "MC", name = "Monaco"} + {code = "MN", name = "Mongolia"} + {code = "MS", name = "Montserrat"} + {code = "ME", name = "Montenegro"} + {code = "MA", name = "Morocco"} + {code = "MZ", name = "Mozambique"} + {code = "MM", name = "Myanmar"} + {code = "NA", name = "Namibia"} + {code = "NR", name = "Nauru"} + {code = "NP", name = "Nepal"} + {code = "NL", name = "Netherlands"} + {code = "AN", name = "Netherlands Antilles"} + {code = "NC", name = "New Caledonia"} + {code = "NZ", name = "New Zealand"} + {code = "NI", name = "Nicaragua"} + {code = "NE", name = "Niger"} + {code = "NG", name = "Nigeria"} + {code = "NU", name = "Niue"} + {code = "NF", name = "Norfolk Island"} + {code = "MP", name = "Northern Mariana Islands"} + {code = "NO", name = "Norway"} + {code = "OM", name = "Oman"} + {code = "PK", name = "Pakistan"} + {code = "PW", name = "Palau"} + {code = "PS", name = "Palestinian Territory, Occupied"} + {code = "PA", name = "Panama"} + {code = "PG", name = "Papua New Guinea"} + {code = "PY", name = "Paraguay"} + {code = "PE", name = "Peru"} + {code = "PH", name = "Philippines"} + {code = "PN", name = "Pitcairn"} + {code = "PL", name = "Poland"} + {code = "PT", name = "Portugal"} + {code = "PR", name = "Puerto Rico"} + {code = "QA", name = "Qatar"} + {code = "RE", name = "Reunion"} + {code = "RO", name = "Romania"} + {code = "RU", name = "Russian Federation"} + {code = "RW", name = "Rwanda"} + {code = "BL", name = "Saint Barthelemy"} + {code = "SH", name = "Saint Helena"} + {code = "KN", name = "Saint Kitts And Nevis"} + {code = "LC", name = "Saint Lucia"} + {code = "MF", name = "Saint Martin"} + {code = "PM", name = "Saint Pierre And Miquelon"} + {code = "VC", name = "Saint Vincent And The Grenadines"} + {code = "WS", name = "Samoa"} + {code = "SM", name = "San Marino"} + {code = "ST", name = "Sao Tome And Principe"} + {code = "SA", name = "Saudi Arabia"} + {code = "SN", name = "Senegal"} + {code = "RS", name = "Serbia"} + {code = "SC", name = "Seychelles"} + {code = "SL", name = "Sierra Leone"} + {code = "SG", name = "Singapore"} + {code = "SX", name = "Sint Maarten"} + {code = "SK", name = "Slovakia"} + {code = "SI", name = "Slovenia"} + {code = "SB", name = "Solomon Islands"} + {code = "SO", name = "Somalia"} + {code = "ZA", name = "South Africa"} + {code = "GS", name = "South Georgia And The South Sandwich Islands"} + {code = "SS", name = "South Sudan"} + {code = "ES", name = "Spain"} + {code = "LK", name = "Sri Lanka"} + {code = "SD", name = "Sudan"} + {code = "SR", name = "Suriname"} + {code = "SJ", name = "Svalbard And Jan Mayen"} + {code = "SZ", name = "Swaziland"} + {code = "SE", name = "Sweden"} + {code = "CH", name = "Switzerland"} + {code = "SY", name = "Syrian Arab Republic"} + {code = "TW", name = "Taiwan, Province Of China"} + {code = "TJ", name = "Tajikistan"} + {code = "TZ", name = "Tanzania, United Republic Of"} + {code = "TH", name = "Thailand"} + {code = "TL", name = "Timor-Leste"} + {code = "TG", name = "Togo"} + {code = "TK", name = "Tokelau"} + {code = "TO", name = "Tonga"} + {code = "TT", name = "Trinidad And Tobago"} + {code = "TN", name = "Tunisia"} + {code = "TR", name = "Turkey"} + {code = "TM", name = "Turkmenistan"} + {code = "TC", name = "Turks And Caicos Islands"} + {code = "TV", name = "Tuvalu"} + {code = "UG", name = "Uganda"} + {code = "UA", name = "Ukraine"} + {code = "AE", name = "United Arab Emirates"} + {code = "GB", name = "United Kingdom"} + {code = "US", name = "United States"} + {code = "UM", name = "United States Minor Outlying Islands"} + {code = "UY", name = "Uruguay"} + {code = "UZ", name = "Uzbekistan"} + {code = "VU", name = "Vanuatu"} + {code = "VE", name = "Venezuela"} + {code = "VN", name = "Viet Nam"} + {code = "VG", name = "Virgin Islands, British"} + {code = "VI", name = "Virgin Islands, U.s."} + {code = "WF", name = "Wallis And Futuna"} + {code = "EH", name = "Western Sahara"} + {code = "YE", name = "Yemen"} + {code = "ZM", name = "Zambia"} + {code = "ZW", name = "Zimbabwe"} + ] + secondary-addresses = ["Apt. ###", "Suite ###"] + building-numbers = ["#####", "####", "###"] + street-suffixes = [Alley, Avenue, Branch, Bridge, Brook, Brooks, Burg, Burgs, Bypass, Camp, Canyon, Cape, Causeway, Center, Centers, Circle, Circles, Cliff, Cliffs, Club, Common, Corner, Corners, Course, Court, Courts, Cove, Coves, Creek, Crescent, Crest, Crossing, Crossroad, Curve, Dale, Dam, Divide, Drive, Drive, Drives, Estate, Estates, Expressway, Extension, Extensions, Fall, Falls, Ferry, Field, Fields, Flat, Flats, Ford, Fords, Forest, Forge, Forges, Fork, Forks, Fort, Freeway, Garden, Gardens, Gateway, Glen, Glens, Green, Greens, Grove, Groves, Harbor, Harbors, Haven, Heights, Highway, Hill, Hills, Hollow, Inlet, Inlet, Island, Island, Islands, Islands, Isle, Isle, Junction, Junctions, Key, Keys, Knoll, Knolls, Lake, Lakes, Land, Landing, Lane, Light, Lights, Loaf, Lock, Locks, Locks, Lodge, Lodge, Loop, Mall, Manor, Manors, Meadow, Meadows, Mews, Mill, Mills, Mission, Mission, Motorway, Mount, Mountain, Mountain, Mountains, Mountains, Neck, Orchard, Oval, Overpass, Park, Parks, Parkway, Parkways, Pass, Passage, Path, Pike, Pine, Pines, Place, Plain, Plains, Plains, Plaza, Plaza, Point, Points, Port, Port, Ports, Ports, Prairie, Prairie, Radial, Ramp, Ranch, Rapid, Rapids, Rest, Ridge, Ridges, River, Road, Road, Roads, Roads, Route, Row, Rue, Run, Shoal, Shoals, Shore, Shores, Skyway, Spring, Springs, Springs, Spur, Spurs, Square, Square, Squares, Squares, Station, Station, Stravenue, Stravenue, Stream, Stream, Street, Street, Streets, Summit, Summit, Terrace, Throughway, Trace, Track, Trafficway, Trail, Trail, Tunnel, Tunnel, Turnpike, Turnpike, Underpass, Union, Unions, Valley, Valleys, Via, Viaduct, View, Views, Village, Village, Villages, Ville, Vista, Vista, Walk, Walks, Wall, Way, Ways, Well, Wells] + street-prefixes = ["N.", "S.", "E.", "W."] + street-name-builder = { + options = [ + {parts = [{type = "seq-string-part", value = ${name.first.names}, suffix = " "}, {type = "seq-string-part", value = ${address.street-suffixes}}]}, + {parts = [{type = "seq-string-part", value = ${name.last.names}, suffix = " "}, {type = "seq-string-part", value = ${address.street-suffixes}}]}, + {parts = [{type = "seq-string-part", value = ${address.street-prefixes}, suffix = " "}, {type = "seq-string-part", value = ${name.first.names}, suffix = " "}, {type = "seq-string-part", value = ${address.street-suffixes}}]}, + {parts = [{type = "seq-string-part", value = ${address.street-prefixes}, suffix = " "}, {type = "seq-string-part", value = ${name.last.names}, suffix = " "}, {type = "seq-string-part", value = ${address.street-suffixes}}]} + ] + } + street-address-builder = { + options = [ + { + parts = [ + {type = "seq-string-part", value = ${address.building-numbers}, suffix = " "}, + {type = "string-builder-part", value = ${address.street-name-builder}} + ] + } + ] + } + postal-code-builder = {options = [{parts = [{type = "string-part", value = "#####"}]}, {parts = [{type = "string-part", value = "#####-####"}]}]} + default-country = {code = "US", name = "United States"} +} \ No newline at end of file diff --git a/src/main/resources/default/name.conf b/src/main/resources/en/name.conf similarity index 100% rename from src/main/resources/default/name.conf rename to src/main/resources/en/name.conf diff --git a/src/main/resources/en_US.conf b/src/main/resources/en_US.conf new file mode 100644 index 00000000..f4255b92 --- /dev/null +++ b/src/main/resources/en_US.conf @@ -0,0 +1 @@ +include "en_US/address.conf" \ No newline at end of file diff --git a/src/main/resources/en_US/address.conf b/src/main/resources/en_US/address.conf new file mode 100644 index 00000000..f2af965b --- /dev/null +++ b/src/main/resources/en_US/address.conf @@ -0,0 +1,74 @@ +include "../en/address.conf" + +address { + states = [ + {name = Alabama, abbr = AL, min-zip = 99501, max-zip = 99950}, + {name = Alaska, abbr = AK, min-zip = 35004, max-zip = 36925}, + {name = Arizona, abbr = AZ, min-zip = 71601, max-zip = 72959}, + {name = Arkansas, abbr = AR, min-zip = 85001, max-zip = 86556}, + {name = California, abbr = CA, min-zip = 90001, max-zip = 96162}, + {name = Colorado, abbr = CO, min-zip = 80001, max-zip = 81658}, + {name = Connecticut, abbr = CT, min-zip = 6001, max-zip = 6389}, + {name = Washington D.C., abbr = DC, min-zip = 20001, max-zip = 20039}, + {name = Delaware, abbr = DE, min-zip = 19701, max-zip = 19980}, + {name = Florida, abbr = FL, min-zip = 32004, max-zip = 34997}, + {name = Georgia, abbr = GA, min-zip = 30001, max-zip = 31999}, + {name = Hawaii, abbr = HI, min-zip = 96701, max-zip = 96898}, + {name = Idaho, abbr = ID, min-zip = 50001, max-zip = 52809}, + {name = Illinois, abbr = IL, min-zip = 83201, max-zip = 83876}, + {name = Indiana, abbr = IN, min-zip = 60001, max-zip = 62999}, + {name = Iowa, abbr = IA, min-zip = 46001, max-zip = 47997}, + {name = Kansas, abbr = KS, min-zip = 66002, max-zip = 67954}, + {name = Kentucky, abbr = KY, min-zip = 40003, max-zip = 42788}, + {name = Louisiana, abbr = LA, min-zip = 70001, max-zip = 71232}, + {name = Maine, abbr = ME, min-zip = 1001, max-zip = 2791}, + {name = Maryland, abbr = MD, min-zip = 20331, max-zip = 20331}, + {name = Massachusetts, abbr = MA, min-zip = 3901, max-zip = 4992}, + {name = Michigan, abbr = MI, min-zip = 48001, max-zip = 49971}, + {name = Minnesota, abbr = MN, min-zip = 55001, max-zip = 56763}, + {name = Mississippi, abbr = MS, min-zip = 63001, max-zip = 65899}, + {name = Missouri, abbr = MO, min-zip = 38601, max-zip = 39776}, + {name = Montana, abbr = MT, min-zip = 59001, max-zip = 59937}, + {name = Nebraska, abbr = NE, min-zip = 27006, max-zip = 28909}, + {name = Nevada, abbr = NV, min-zip = 58001, max-zip = 58856}, + {name = New Hampshire, abbr = NH, min-zip = 68001, max-zip = 68118}, + {name = New Jersey, abbr = NJ, min-zip = 3031, max-zip = 3897}, + {name = New Mexico, abbr = NM, min-zip = 7001, max-zip = 8989}, + {name = New York, abbr = NY, min-zip = 87001, max-zip = 88441}, + {name = North Carolina, abbr = NC, min-zip = 88901, max-zip = 89883}, + {name = North Dakota, abbr = ND, min-zip = 6390, max-zip = 6390}, + {name = Ohio, abbr = OH, min-zip = 43001, max-zip = 45999}, + {name = Oklahoma, abbr = OK, min-zip = 73001, max-zip = 73199}, + {name = Oregon, abbr = OR, min-zip = 97001, max-zip = 97920}, + {name = Pennsylvania, abbr = PA, min-zip = 15001, max-zip = 19640}, + {name = Rhode Island, abbr = RI, min-zip = 2801, max-zip = 2940}, + {name = South Carolina, abbr = SC, min-zip = 29001, max-zip = 29948}, + {name = South Dakota, abbr = SD, min-zip = 57001, max-zip = 57799}, + {name = Tennessee, abbr = TN, min-zip = 37010, max-zip = 38589}, + {name = Texas, abbr = TX, min-zip = 73301, max-zip = 73301}, + {name = Utah, abbr = UT, min-zip = 84001, max-zip = 84784}, + {name = Vermont, abbr = VT, min-zip = 20040, max-zip = 20041}, + {name = Virginia, abbr = VA, min-zip = 5001, max-zip = 5495}, + {name = Washington, abbr = WA, min-zip = 98001, max-zip = 99403}, + {name = West Virginia, abbr = WV, min-zip = 53001, max-zip = 54990}, + {name = Wisconsin, abbr = WI, min-zip = 24701, max-zip = 26886}, + {name = Wyoming, abbr = WY, min-zip = 82001, max-zip = 83128} + ] + default-country = {code = "US", name = "United States"} + postal-code-builder = {options = [{parts = [{type = "seq-state-zip-part", value = ${address.states}}]}]} + full-address-builder = { + options = [ + {parts = [ + {type = "string-builder-part", value = ${address.street-address-builder}, suffix = ", "}, + {type = "string-builder-part", value = ${address.city-builder}, suffix = ", "}, + {type = "seq-state-abbr-and-zip-part", value = ${address.states}} + ]}, + {parts = [ + {type = "string-builder-part", value = ${address.street-address-builder}, suffix = " "}, + {type = "seq-string-part", value = ${address.secondary-addresses}, suffix = ", "}, + {type = "string-builder-part", value = ${address.city-builder}, suffix = ", "}, + {type = "seq-state-abbr-and-zip-part", value = ${address.states}} + ]} + ] + } +} \ No newline at end of file diff --git a/src/main/scala-2.11/faker/compat/LazyListCompat.scala b/src/main/scala-2.11/faker/compat/LazyListCompat.scala index 976c4e34..a2447765 100644 --- a/src/main/scala-2.11/faker/compat/LazyListCompat.scala +++ b/src/main/scala-2.11/faker/compat/LazyListCompat.scala @@ -3,4 +3,4 @@ package faker.compat trait LazyListCompat { type LazyList[A] = Stream[A] val LazyList = Stream -} \ No newline at end of file +} diff --git a/src/main/scala/faker/Faker.scala b/src/main/scala/faker/Faker.scala index 3dd9a971..0a4bd85f 100644 --- a/src/main/scala/faker/Faker.scala +++ b/src/main/scala/faker/Faker.scala @@ -118,6 +118,44 @@ final class Faker(private val locale: Locale) { def randomZonedDateTime(): ZonedDateTime = Arbitrary.arbitrary[time.RandomZonedDateTime].one.value + // Address + def buildingNumber(): String = + Arbitrary.arbitrary[address.BuildingNumber].one.value + def city(): String = + Arbitrary.arbitrary[address.City].one.value + def cityPrefix(): String = + Arbitrary.arbitrary[address.CityPrefix].one.value + def citySuffix(): String = + Arbitrary.arbitrary[address.CitySuffix].one.value + def country(): address.Country = + Arbitrary.arbitrary[address.Country].one + def countryCode(): String = country().code + def countryName(): String = country().name + def defaultCountry(): address.DefaultCountry = + Arbitrary.arbitrary[address.DefaultCountry].one + def defaultCountryCode(): String = defaultCountry().code + def defaultCountryName(): String = defaultCountry().name + def fullAddress(): String = + Arbitrary.arbitrary[address.FullAddress].one.value + def latitude(): String = + Arbitrary.arbitrary[address.Latitude].one.value + def longitude(): String = + Arbitrary.arbitrary[address.Longitude].one.value + def postalCode(): String = + Arbitrary.arbitrary[address.PostalCode].one.value + def secondaryAddress(): String = + Arbitrary.arbitrary[address.SecondaryAddress].one.value + def state(): address.State = Arbitrary.arbitrary[address.State].one + def stateAbbr(): String = state().abbr + def stateZip(): String = state().zipGen.one + def streetAddress(): String = + Arbitrary.arbitrary[address.StreetAddress].one.value + def streetName(): String = + Arbitrary.arbitrary[address.StreetName].one.value + def streetPrefix(): String = + Arbitrary.arbitrary[address.StreetPrefix].one.value + def streetSuffix(): String = + Arbitrary.arbitrary[address.StreetSuffix].one.value } object Faker { diff --git a/src/main/scala/faker/ResourceLoader.scala b/src/main/scala/faker/ResourceLoader.scala index e93afa37..46e9bba8 100644 --- a/src/main/scala/faker/ResourceLoader.scala +++ b/src/main/scala/faker/ResourceLoader.scala @@ -1,33 +1,33 @@ package faker -import scala.jdk.CollectionConverters._ +import scala.reflect.ClassTag import java.util.Locale -import com.typesafe.config._ +import pureconfig._ final class ResourceLoader(private val locale: Locale) { - private val defaultConfig: Config = - ConfigFactory.parseResources("default.conf") - private val languageConfig: Config = - ConfigFactory.parseResources(s"${locale.getLanguage}.conf") - private val localeConfig: Config = - ConfigFactory.parseResources(s"${locale.toString}.conf") + private val defaultConfig: ConfigObjectSource = + ConfigSource.resources("default.conf") + private val languageConfig: ConfigObjectSource = + ConfigSource.resources(s"${locale.getLanguage}.conf") + private val localeConfig: ConfigObjectSource = + ConfigSource.resources(s"${locale.toString}.conf") // Fallback Pattern: en-US.conf -> en.conf -> default.conf - private val conf: Config = - localeConfig - .withFallback(languageConfig) + private val conf: ConfigSource = + localeConfig.optional + .withFallback(languageConfig.optional) .withFallback(defaultConfig) - .resolve() - def loadStringList(key: String): Seq[String] = - conf.getStringList(key).asScala.toSeq + def loadKey[A: ConfigReader: ClassTag](key: String): A = + conf.at(key).loadOrThrow[A] } object ResourceLoader { val default: ResourceLoader = new ResourceLoader(Locale.getDefault) + val US: ResourceLoader = new ResourceLoader(Locale.US) object Implicits { implicit val defaultResourceLoader: ResourceLoader = default diff --git a/src/main/scala/faker/StringGenBuilder.scala b/src/main/scala/faker/StringGenBuilder.scala new file mode 100644 index 00000000..e9387529 --- /dev/null +++ b/src/main/scala/faker/StringGenBuilder.scala @@ -0,0 +1,118 @@ +package faker + +import scala.jdk.CollectionConverters._ + +import org.scalacheck.Gen +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.syntax.string._ + +private[faker] final case class StringGenBuilder( + options: Seq[StringGenBuilderWeightedOption] +) { + val gen: Gen[String] = + Gen.frequency(options.map(opt => opt.weight -> opt.gen): _*) +} + +object StringGenBuilder { + implicit val stringGenBuilderConfigReader: ConfigReader[StringGenBuilder] = + deriveReader +} + +private[faker] sealed trait StringGenBuilderPart { + def prefix: Option[String] + def suffix: Option[String] + def valueGen: Gen[String] + def gen: Gen[String] = + valueGen.map(x => s"${prefix.getOrElse("")}$x${suffix.getOrElse("")}") +} + +object StringGenBuilderPart { + implicit val stringGenBuilderPartConfigReader + : ConfigReader[StringGenBuilderPart] = deriveReader +} + +private[faker] final case class SeqStringPart( + prefix: Option[String], + suffix: Option[String], + value: Seq[String] +) extends StringGenBuilderPart { + val valueGen: Gen[String] = + Gen + .sequence(value.map(_.interpolatedGen)) + .flatMap(x => Gen.oneOf(x.asScala)) +} + +object SeqStringPart { + implicit val seqStringPartConfigReader: ConfigReader[SeqStringPart] = + deriveReader +} + +private[faker] final case class SeqStateZipPart( + prefix: Option[String], + suffix: Option[String], + value: Seq[address.State] +) extends StringGenBuilderPart { + val valueGen: Gen[String] = + Gen.sequence(value.map(_.zipGen)).flatMap(x => Gen.oneOf(x.asScala)) +} + +object SeqStateZipPart { + implicit val seqStateZipPartConfigReader: ConfigReader[SeqStateZipPart] = + deriveReader +} + +private[faker] final case class SeqStateAbbrAndZipPart( + prefix: Option[String], + suffix: Option[String], + value: Seq[address.State] +) extends StringGenBuilderPart { + val valueGen: Gen[String] = for { + state <- Gen.oneOf(value) + zip <- state.zipGen + } yield s"${state.abbr} $zip" +} + +object SeqStateAbbrAndZipPart { + implicit val seqStateAbbrPartConfigReader + : ConfigReader[SeqStateAbbrAndZipPart] = + deriveReader +} + +private[faker] final case class StringPart( + prefix: Option[String], + suffix: Option[String], + value: String +) extends StringGenBuilderPart { + val valueGen: Gen[String] = value.interpolatedGen +} + +object StringPart { + implicit val stringPartConfigReader: ConfigReader[StringPart] = deriveReader +} + +private[faker] final case class StringBuilderPart( + prefix: Option[String], + suffix: Option[String], + value: StringGenBuilder +) extends StringGenBuilderPart { + val valueGen: Gen[String] = value.gen +} + +object StringBuilderPart { + implicit val stringBuilderPartConfigReader: ConfigReader[StringBuilderPart] = + deriveReader +} + +private[faker] final case class StringGenBuilderWeightedOption( + parts: List[StringGenBuilderPart], + weight: Int = 1 +) { + val gen: Gen[String] = Gen.sequence(parts.map(_.gen)).map(_.asScala.mkString) +} + +object StringGenBuilderWeightedOption { + implicit val stringGenBuilderWeightedOptionConfigReader + : ConfigReader[StringGenBuilderWeightedOption] = deriveReader +} diff --git a/src/main/scala/faker/address/BuildingNumber.scala b/src/main/scala/faker/address/BuildingNumber.scala new file mode 100644 index 00000000..01915d6d --- /dev/null +++ b/src/main/scala/faker/address/BuildingNumber.scala @@ -0,0 +1,25 @@ +package faker.address + +import scala.jdk.CollectionConverters._ + +import org.scalacheck.{Arbitrary, Gen} + +import faker.ResourceLoader +import faker.syntax.string._ + +final case class BuildingNumber private (value: String) extends AnyVal + +object BuildingNumber { + implicit def buildingNumberArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[BuildingNumber] = + Arbitrary( + Gen + .sequence( + loader + .loadKey[Seq[String]]("address.building-numbers") + .map(_.interpolatedGen) + ) + .flatMap(x => Gen.oneOf(x.asScala).map(BuildingNumber.apply)) + ) +} diff --git a/src/main/scala/faker/address/City.scala b/src/main/scala/faker/address/City.scala new file mode 100644 index 00000000..6e4359e1 --- /dev/null +++ b/src/main/scala/faker/address/City.scala @@ -0,0 +1,22 @@ +package faker.address + +import org.scalacheck.Arbitrary +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.{ResourceLoader, StringGenBuilder} + +final case class City private (value: String) extends AnyVal + +object City { + implicit def cityArbitrary(implicit loader: ResourceLoader): Arbitrary[City] = + Arbitrary( + loader + .loadKey[StringGenBuilder]("address.city-builder") + .gen + .map(x => City(x)) + ) + + implicit val cityConfigReader: ConfigReader[City] = deriveReader + +} diff --git a/src/main/scala/faker/address/CityPrefix.scala b/src/main/scala/faker/address/CityPrefix.scala new file mode 100644 index 00000000..e4f9f8f4 --- /dev/null +++ b/src/main/scala/faker/address/CityPrefix.scala @@ -0,0 +1,20 @@ +package faker.address + +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.ResourceLoader + +final case class CityPrefix private (value: String) extends AnyVal + +object CityPrefix { + def cityPrefixes(implicit loader: ResourceLoader): Seq[CityPrefix] = + loader.loadKey[Seq[CityPrefix]]("address.city-prefixes") + implicit def cityPrefixArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[CityPrefix] = + Arbitrary(Gen.oneOf(cityPrefixes)) + + implicit val cityPrefixConfigReader: ConfigReader[CityPrefix] = deriveReader +} diff --git a/src/main/scala/faker/address/CitySuffix.scala b/src/main/scala/faker/address/CitySuffix.scala new file mode 100644 index 00000000..9b2500b3 --- /dev/null +++ b/src/main/scala/faker/address/CitySuffix.scala @@ -0,0 +1,20 @@ +package faker.address + +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.ResourceLoader + +final case class CitySuffix private (value: String) extends AnyVal + +object CitySuffix { + def citySuffixes(implicit loader: ResourceLoader): Seq[CitySuffix] = + loader.loadKey[Seq[CitySuffix]]("address.city-suffixes") + implicit def citySuffixArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[CitySuffix] = + Arbitrary(Gen.oneOf(citySuffixes)) + + implicit val citySuffixConfigReader: ConfigReader[CitySuffix] = deriveReader +} diff --git a/src/main/scala/faker/address/Country.scala b/src/main/scala/faker/address/Country.scala new file mode 100644 index 00000000..c2126be9 --- /dev/null +++ b/src/main/scala/faker/address/Country.scala @@ -0,0 +1,19 @@ +package faker.address + +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.ResourceLoader + +final case class Country private (code: String, name: String) + +object Country { + def countries(implicit loader: ResourceLoader): Seq[Country] = + loader.loadKey[Seq[Country]]("address.countries") + implicit def countryArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[Country] = Arbitrary(Gen.oneOf(countries)) + + implicit val countryConfigReader: ConfigReader[Country] = deriveReader +} diff --git a/src/main/scala/faker/address/DefaultCountry.scala b/src/main/scala/faker/address/DefaultCountry.scala new file mode 100644 index 00000000..c82e7e93 --- /dev/null +++ b/src/main/scala/faker/address/DefaultCountry.scala @@ -0,0 +1,20 @@ +package faker.address + +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.ResourceLoader + +final case class DefaultCountry private (code: String, name: String) + +object DefaultCountry { + def defaultCountry(implicit loader: ResourceLoader): DefaultCountry = + loader.loadKey[DefaultCountry]("address.default-country") + implicit def defaultCountryArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[DefaultCountry] = Arbitrary(Gen.const(defaultCountry)) + + implicit val defaultCountryConfigReader: ConfigReader[DefaultCountry] = + deriveReader +} diff --git a/src/main/scala/faker/address/FullAddress.scala b/src/main/scala/faker/address/FullAddress.scala new file mode 100644 index 00000000..d8f9cdaf --- /dev/null +++ b/src/main/scala/faker/address/FullAddress.scala @@ -0,0 +1,19 @@ +package faker.address + +import org.scalacheck.Arbitrary + +import faker.{ResourceLoader, StringGenBuilder} + +final case class FullAddress private (value: String) extends AnyVal + +object FullAddress { + implicit def fullAddressArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[FullAddress] = + Arbitrary( + loader + .loadKey[StringGenBuilder]("address.full-address-builder") + .gen + .map(FullAddress.apply) + ) +} diff --git a/src/main/scala/faker/address/Latitude.scala b/src/main/scala/faker/address/Latitude.scala new file mode 100644 index 00000000..6e021ea4 --- /dev/null +++ b/src/main/scala/faker/address/Latitude.scala @@ -0,0 +1,14 @@ +package faker.address + +import scala.util.Random + +import org.scalacheck.{Arbitrary, Gen} + +final case class Latitude private (value: String) extends AnyVal + +object Latitude { + def latitude: String = "%.8g".format((Random.nextDouble() * 180) - 90) + implicit val latitudeArbitrary: Arbitrary[Latitude] = Arbitrary( + Gen.delay(latitude).map(Latitude.apply) + ) +} diff --git a/src/main/scala/faker/address/Longitude.scala b/src/main/scala/faker/address/Longitude.scala new file mode 100644 index 00000000..6533d7ce --- /dev/null +++ b/src/main/scala/faker/address/Longitude.scala @@ -0,0 +1,14 @@ +package faker.address + +import scala.util.Random + +import org.scalacheck.{Arbitrary, Gen} + +final case class Longitude private (value: String) extends AnyVal + +object Longitude { + def longitude: String = "%.8g".format((Random.nextDouble() * 360) - 180) + implicit val longitudeArbitrary: Arbitrary[Longitude] = Arbitrary( + Gen.delay(longitude).map(Longitude.apply) + ) +} diff --git a/src/main/scala/faker/address/PostalCode.scala b/src/main/scala/faker/address/PostalCode.scala new file mode 100644 index 00000000..f1d3d757 --- /dev/null +++ b/src/main/scala/faker/address/PostalCode.scala @@ -0,0 +1,19 @@ +package faker.address + +import org.scalacheck.Arbitrary + +import faker.{ResourceLoader, StringGenBuilder} + +final case class PostalCode private (value: String) extends AnyVal + +object PostalCode { + implicit def postalCodeArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[PostalCode] = + Arbitrary( + loader + .loadKey[StringGenBuilder]("address.postal-code-builder") + .gen + .map(PostalCode.apply) + ) +} diff --git a/src/main/scala/faker/address/SecondaryAddress.scala b/src/main/scala/faker/address/SecondaryAddress.scala new file mode 100644 index 00000000..fb0be068 --- /dev/null +++ b/src/main/scala/faker/address/SecondaryAddress.scala @@ -0,0 +1,25 @@ +package faker.address + +import scala.jdk.CollectionConverters._ + +import org.scalacheck.{Arbitrary, Gen} + +import faker.ResourceLoader +import faker.syntax.string._ + +final case class SecondaryAddress private (value: String) extends AnyVal + +object SecondaryAddress { + implicit def secondaryAddressArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[SecondaryAddress] = + Arbitrary( + Gen + .sequence( + loader + .loadKey[Seq[String]]("address.secondary-addresses") + .map(_.interpolatedGen) + ) + .flatMap(x => Gen.oneOf(x.asScala).map(SecondaryAddress.apply)) + ) +} diff --git a/src/main/scala/faker/address/State.scala b/src/main/scala/faker/address/State.scala new file mode 100644 index 00000000..de886a09 --- /dev/null +++ b/src/main/scala/faker/address/State.scala @@ -0,0 +1,32 @@ +package faker.address + +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.ResourceLoader +import faker.syntax.string._ + +/** Faker class for US States. Since this is specific for the US locale, that locale is + * always used to load the data for this class. + */ +final case class State private ( + abbr: String, + name: String, + minZip: Int, + maxZip: Int +) { + val zipCodes: Seq[String] = + minZip.until(maxZip + 1).map(x => "%05d".format(x)) + private val zip5Gen: Gen[String] = Gen.oneOf(zipCodes) + private val zip9Gen: Gen[String] = + zip5Gen.flatMap(zip5 => "####".interpolatedGen.map(zip4 => s"$zip5-$zip4")) + val zipGen: Gen[String] = Gen.frequency(1 -> zip5Gen, 1 -> zip9Gen) +} + +object State { + implicit val stateConfigReader: ConfigReader[State] = deriveReader + val states: Seq[State] = + ResourceLoader.US.loadKey[Seq[State]]("address.states") + implicit val stateArbitrary: Arbitrary[State] = Arbitrary(Gen.oneOf(states)) +} diff --git a/src/main/scala/faker/address/StreetAddress.scala b/src/main/scala/faker/address/StreetAddress.scala new file mode 100644 index 00000000..2f2ab01a --- /dev/null +++ b/src/main/scala/faker/address/StreetAddress.scala @@ -0,0 +1,19 @@ +package faker.address + +import org.scalacheck.Arbitrary + +import faker.{ResourceLoader, StringGenBuilder} + +final case class StreetAddress private (value: String) extends AnyVal + +object StreetAddress { + implicit def streetAddressArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[StreetAddress] = + Arbitrary( + loader + .loadKey[StringGenBuilder]("address.street-address-builder") + .gen + .map(StreetAddress.apply) + ) +} diff --git a/src/main/scala/faker/address/StreetName.scala b/src/main/scala/faker/address/StreetName.scala new file mode 100644 index 00000000..0aa16d4b --- /dev/null +++ b/src/main/scala/faker/address/StreetName.scala @@ -0,0 +1,19 @@ +package faker.address + +import org.scalacheck.Arbitrary + +import faker.{ResourceLoader, StringGenBuilder} + +final case class StreetName private (value: String) extends AnyVal + +object StreetName { + implicit def streetNameArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[StreetName] = + Arbitrary( + loader + .loadKey[StringGenBuilder]("address.street-name-builder") + .gen + .map(StreetName.apply) + ) +} diff --git a/src/main/scala/faker/address/StreetPrefix.scala b/src/main/scala/faker/address/StreetPrefix.scala new file mode 100644 index 00000000..af7689e6 --- /dev/null +++ b/src/main/scala/faker/address/StreetPrefix.scala @@ -0,0 +1,20 @@ +package faker.address + +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.ResourceLoader + +final case class StreetPrefix private (value: String) extends AnyVal + +object StreetPrefix { + def streetPrefixes(implicit loader: ResourceLoader): Seq[StreetPrefix] = + loader.loadKey[Seq[StreetPrefix]]("address.street-prefixes") + implicit def streetPrefixArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[StreetPrefix] = + Arbitrary(Gen.oneOf(streetPrefixes)) + implicit val streetPrefixConfigReader: ConfigReader[StreetPrefix] = + deriveReader +} diff --git a/src/main/scala/faker/address/StreetSuffix.scala b/src/main/scala/faker/address/StreetSuffix.scala new file mode 100644 index 00000000..f86d19ca --- /dev/null +++ b/src/main/scala/faker/address/StreetSuffix.scala @@ -0,0 +1,20 @@ +package faker.address + +import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ + +import faker.ResourceLoader + +final case class StreetSuffix private (value: String) extends AnyVal + +object StreetSuffix { + def streetSuffixes(implicit loader: ResourceLoader): Seq[StreetSuffix] = + loader.loadKey[Seq[StreetSuffix]]("address.street-suffixes") + implicit def streetSuffixArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[StreetSuffix] = + Arbitrary(Gen.oneOf(streetSuffixes)) + implicit val streetSuffixConfigReader: ConfigReader[StreetSuffix] = + deriveReader +} diff --git a/src/main/scala/faker/internet/Avatar.scala b/src/main/scala/faker/internet/Avatar.scala index 6f374e55..51f5614a 100644 --- a/src/main/scala/faker/internet/Avatar.scala +++ b/src/main/scala/faker/internet/Avatar.scala @@ -15,7 +15,7 @@ final case class Avatar private (value: String) extends AnyVal object Avatar { def avatars(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("internet.avatars") + loader.loadKey[Seq[String]]("internet.avatars") implicit def avatarArbitrary(implicit loader: ResourceLoader diff --git a/src/main/scala/faker/internet/DomainSuffix.scala b/src/main/scala/faker/internet/DomainSuffix.scala index d297b115..7e051d68 100644 --- a/src/main/scala/faker/internet/DomainSuffix.scala +++ b/src/main/scala/faker/internet/DomainSuffix.scala @@ -1,17 +1,22 @@ package faker.internet import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ import faker.ResourceLoader final case class DomainSuffix private (value: String) extends AnyVal object DomainSuffix { - def domainSuffixes(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("internet.domain-suffixes") + def domainSuffixes(implicit loader: ResourceLoader): Seq[DomainSuffix] = + loader.loadKey[Seq[DomainSuffix]]("internet.domain-suffixes") implicit def domainSuffixArbitrary(implicit loader: ResourceLoader ): Arbitrary[DomainSuffix] = - Arbitrary(Gen.oneOf(domainSuffixes.map(DomainSuffix.apply))) + Arbitrary(Gen.oneOf(domainSuffixes)) + + implicit val domainSuffixConfigReader: ConfigReader[DomainSuffix] = + deriveReader } diff --git a/src/main/scala/faker/internet/EmailAddress.scala b/src/main/scala/faker/internet/EmailAddress.scala index 0d15de00..a44fc4c0 100644 --- a/src/main/scala/faker/internet/EmailAddress.scala +++ b/src/main/scala/faker/internet/EmailAddress.scala @@ -10,7 +10,7 @@ final case class EmailAddress private (value: String) extends AnyVal object EmailAddress { def freeEmailDomains(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("internet.free-email-domains") + loader.loadKey[Seq[String]]("internet.free-email-domains") implicit def emailAddressArbitrary(implicit loader: ResourceLoader ): Arbitrary[EmailAddress] = diff --git a/src/main/scala/faker/internet/Image.scala b/src/main/scala/faker/internet/Image.scala index 95863e77..57394e1f 100644 --- a/src/main/scala/faker/internet/Image.scala +++ b/src/main/scala/faker/internet/Image.scala @@ -14,9 +14,9 @@ final case class Image private (value: String) extends AnyVal object Image { def imageDimensions(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("internet.image.dimensions") + loader.loadKey[Seq[String]]("internet.image.dimensions") def imageCategories(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("internet.image.categories") + loader.loadKey[Seq[String]]("internet.image.categories") implicit def imageArbitrary(implicit loader: ResourceLoader ): Arbitrary[Image] = diff --git a/src/main/scala/faker/internet/Password.scala b/src/main/scala/faker/internet/Password.scala index b101662f..b59f4c9b 100644 --- a/src/main/scala/faker/internet/Password.scala +++ b/src/main/scala/faker/internet/Password.scala @@ -11,7 +11,7 @@ final case class Password private (value: String) extends AnyVal object Password { def passwordSpecialCharacters(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("internet.password.special-characters") + loader.loadKey[Seq[String]]("internet.password.special-characters") implicit def passwordArbitrary(implicit loader: ResourceLoader ): Arbitrary[Password] = diff --git a/src/main/scala/faker/internet/SafeEmailAddress.scala b/src/main/scala/faker/internet/SafeEmailAddress.scala index 35e4eb94..3a24664d 100644 --- a/src/main/scala/faker/internet/SafeEmailAddress.scala +++ b/src/main/scala/faker/internet/SafeEmailAddress.scala @@ -10,7 +10,7 @@ final case class SafeEmailAddress private (value: String) extends AnyVal object SafeEmailAddress { def safeEmailDomains(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("internet.safe-email-domains") + loader.loadKey[Seq[String]]("internet.safe-email-domains") implicit def safeEmailAddressArbitrary(implicit loader: ResourceLoader ): Arbitrary[SafeEmailAddress] = diff --git a/src/main/scala/faker/internet/UserAgent.scala b/src/main/scala/faker/internet/UserAgent.scala index bae0f0cd..1deaeaca 100644 --- a/src/main/scala/faker/internet/UserAgent.scala +++ b/src/main/scala/faker/internet/UserAgent.scala @@ -8,22 +8,9 @@ final case class UserAgent private (value: String) extends AnyVal object UserAgent { def userAgents(loader: ResourceLoader): Map[String, Seq[String]] = - Seq( - "aol", - "chrome", - "firefox", - "internet-explorer", - "netscape", - "opera", - "safari" - ).map(x => x -> loader.loadStringList(s"internet.user-agents.$x")).toMap + loader.loadKey[Map[String, Seq[String]]](s"internet.user-agents") implicit def userAgentArbitrary(implicit loader: ResourceLoader ): Arbitrary[UserAgent] = - Arbitrary( - for { - tpe <- Gen.oneOf(userAgents(loader).keys) - agent <- Gen.oneOf(userAgents(loader)(tpe)) - } yield UserAgent(agent) - ) + Arbitrary(Gen.oneOf(userAgents(loader).values.flatten).map(UserAgent.apply)) } diff --git a/src/main/scala/faker/lorem/LoremWord.scala b/src/main/scala/faker/lorem/LoremWord.scala index efe76069..ff53bcd2 100644 --- a/src/main/scala/faker/lorem/LoremWord.scala +++ b/src/main/scala/faker/lorem/LoremWord.scala @@ -1,18 +1,20 @@ package faker.lorem import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ import faker.ResourceLoader final case class LoremWord private (value: String) extends AnyVal object LoremWord { - def loremWords(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("lorem.words") + def loremWords(implicit loader: ResourceLoader): Seq[LoremWord] = + loader.loadKey[Seq[LoremWord]]("lorem.words") implicit def loremWordArbitrary(implicit loader: ResourceLoader ): Arbitrary[LoremWord] = - Arbitrary( - Gen.oneOf(loremWords).map(LoremWord.apply) - ) + Arbitrary(Gen.oneOf(loremWords)) + + implicit val loremWordConfigReader: ConfigReader[LoremWord] = deriveReader } diff --git a/src/main/scala/faker/name/FirstName.scala b/src/main/scala/faker/name/FirstName.scala index ec67d70c..385b8d8a 100644 --- a/src/main/scala/faker/name/FirstName.scala +++ b/src/main/scala/faker/name/FirstName.scala @@ -1,19 +1,21 @@ package faker.name import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ import faker.ResourceLoader final case class FirstName private (value: String) extends AnyVal object FirstName { - def firstNames(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("name.first.names") + def firstNames(implicit loader: ResourceLoader): Seq[FirstName] = + loader.loadKey[Seq[FirstName]]("name.first.names") implicit def firstNameArbitrary(implicit loader: ResourceLoader ): Arbitrary[FirstName] = - Arbitrary( - Gen.oneOf(firstNames).map(FirstName.apply) - ) + Arbitrary(Gen.oneOf(firstNames)) + + implicit val firstNameConfigReader: ConfigReader[FirstName] = deriveReader } diff --git a/src/main/scala/faker/name/LastName.scala b/src/main/scala/faker/name/LastName.scala index f924ac0b..91bd6b13 100644 --- a/src/main/scala/faker/name/LastName.scala +++ b/src/main/scala/faker/name/LastName.scala @@ -1,18 +1,20 @@ package faker.name import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ import faker.ResourceLoader final case class LastName private (value: String) extends AnyVal object LastName { - def lastNames(implicit loader: ResourceLoader): Seq[String] = - loader.loadStringList("name.last.names") + def lastNames(implicit loader: ResourceLoader): Seq[LastName] = + loader.loadKey[Seq[LastName]]("name.last.names") implicit def lastNameArbitrary(implicit loader: ResourceLoader ): Arbitrary[LastName] = - Arbitrary( - Gen.oneOf(lastNames).map(LastName.apply) - ) + Arbitrary(Gen.oneOf(lastNames)) + + implicit val lastNameConfigReader: ConfigReader[LastName] = deriveReader } diff --git a/src/main/scala/faker/name/Prefix.scala b/src/main/scala/faker/name/Prefix.scala index 9d493f9c..a9ac35a6 100644 --- a/src/main/scala/faker/name/Prefix.scala +++ b/src/main/scala/faker/name/Prefix.scala @@ -1,15 +1,20 @@ package faker.name import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ import faker.ResourceLoader final case class Prefix private (value: String) extends AnyVal object Prefix { - private val prefixes: Seq[String] = - ResourceLoader.default.loadStringList("name.prefixes") - implicit val prefixArbitrary: Arbitrary[Prefix] = Arbitrary( - Gen.oneOf(prefixes).map(Prefix.apply) - ) + def prefixes(implicit loader: ResourceLoader): Seq[Prefix] = + loader.loadKey[Seq[Prefix]]("name.prefixes") + implicit def prefixArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[Prefix] = + Arbitrary(Gen.oneOf(prefixes)) + + implicit val prefixConfigReader: ConfigReader[Prefix] = deriveReader } diff --git a/src/main/scala/faker/name/Suffix.scala b/src/main/scala/faker/name/Suffix.scala index 55b35fb5..08630194 100644 --- a/src/main/scala/faker/name/Suffix.scala +++ b/src/main/scala/faker/name/Suffix.scala @@ -1,15 +1,19 @@ package faker.name import org.scalacheck.{Arbitrary, Gen} +import pureconfig.ConfigReader +import pureconfig.generic.semiauto._ import faker.ResourceLoader final case class Suffix private (value: String) extends AnyVal object Suffix { - private val suffixes: Seq[String] = - ResourceLoader.default.loadStringList("name.suffixes") - implicit val suffixArbitrary: Arbitrary[Suffix] = Arbitrary( - Gen.oneOf(suffixes).map(Suffix.apply) - ) + def suffixes(implicit loader: ResourceLoader): Seq[Suffix] = + loader.loadKey[Seq[Suffix]]("name.suffixes") + implicit def suffixArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[Suffix] = Arbitrary(Gen.oneOf(suffixes)) + + implicit val suffixConfigReader: ConfigReader[Suffix] = deriveReader } diff --git a/src/main/scala/faker/name/Title.scala b/src/main/scala/faker/name/Title.scala index 869f03f2..9c781730 100644 --- a/src/main/scala/faker/name/Title.scala +++ b/src/main/scala/faker/name/Title.scala @@ -7,17 +7,20 @@ import faker.ResourceLoader final case class Title private (value: String) extends AnyVal object Title { - private val titleDescriptors: Seq[String] = - ResourceLoader.default.loadStringList("name.title.descriptors") - private val titleLevels: Seq[String] = - ResourceLoader.default.loadStringList("name.title.levels") - private val titleJobs: Seq[String] = - ResourceLoader.default.loadStringList("name.title.jobs") - implicit val titleArbitrary: Arbitrary[Title] = Arbitrary { - for { - descriptor <- Gen.oneOf(titleDescriptors) - level <- Gen.oneOf(titleLevels) - job <- Gen.oneOf(titleJobs) - } yield Title(s"$descriptor $level $job") - } + def titleDescriptors(implicit loader: ResourceLoader): Seq[String] = + loader.loadKey[Seq[String]]("name.title.descriptors") + def titleLevels(implicit loader: ResourceLoader): Seq[String] = + loader.loadKey[Seq[String]]("name.title.levels") + def titleJobs(implicit loader: ResourceLoader): Seq[String] = + loader.loadKey[Seq[String]]("name.title.jobs") + implicit def titleArbitrary(implicit + loader: ResourceLoader + ): Arbitrary[Title] = + Arbitrary { + for { + descriptor <- Gen.oneOf(titleDescriptors) + level <- Gen.oneOf(titleLevels) + job <- Gen.oneOf(titleJobs) + } yield Title(s"$descriptor $level $job") + } } diff --git a/src/main/scala/faker/syntax/string.scala b/src/main/scala/faker/syntax/string.scala new file mode 100644 index 00000000..bf20f077 --- /dev/null +++ b/src/main/scala/faker/syntax/string.scala @@ -0,0 +1,20 @@ +package faker.syntax + +import org.scalacheck.Gen + +object string extends FakerStringSyntax + +trait FakerStringSyntax { + implicit def toFakerStringOps(str: String): FakerStringSyntax.FakerStringOps = + new FakerStringSyntax.FakerStringOps(str) +} + +object FakerStringSyntax { + final class FakerStringOps(private val str: String) extends AnyVal { + def interpolatedGen: Gen[String] = + str.foldLeft(Gen.const("")) { (gen, char) => + if (char == '#') gen.flatMap(x => Gen.choose(0, 9).map(y => s"$x$y")) + else gen.map(y => y + char.toString) + } + } +} diff --git a/src/test/scala/faker/FakerSpec.scala b/src/test/scala/faker/FakerSpec.scala index 77a3d48e..b6f18079 100644 --- a/src/test/scala/faker/FakerSpec.scala +++ b/src/test/scala/faker/FakerSpec.scala @@ -258,5 +258,91 @@ class FakerSpec extends AnyFreeSpecLike { assert(Option(res).isDefined, res) } } + "Address" - { + "buildingNumber should return successfully" in { + val res = Faker.default.buildingNumber() + assert(res.nonEmpty, res) + } + "city should return successfully" in { + val res = Faker.default.city() + assert(res.nonEmpty, res) + } + "cityPrefix should return successfully" in { + val res = Faker.default.cityPrefix() + assert(res.nonEmpty, res) + } + "citySuffix should return successfully" in { + val res = Faker.default.citySuffix() + assert(res.nonEmpty, res) + } + "country should return successfully" in { + val res = Faker.default.country() + assert(res.code.nonEmpty && res.name.nonEmpty, res) + } + "countryCode should return successfully" in { + val res = Faker.default.countryCode() + assert(res.nonEmpty, res) + } + "countryName should return successfully" in { + val res = Faker.default.countryName() + assert(res.nonEmpty, res) + } + "defaultCountry should return successfully" in { + val res = Faker.default.defaultCountry() + assert(res.code.nonEmpty && res.name.nonEmpty, res) + } + "defaultCountryCode should return successfully" in { + val res = Faker.default.defaultCountryCode() + assert(res.nonEmpty, res) + } + "defaultCountryName should return successfully" in { + val res = Faker.default.defaultCountryName() + assert(res.nonEmpty, res) + } + "fullAddress should return successfully" in { + val res = Faker.default.fullAddress() + assert(res.nonEmpty, res) + } + "latitude should return successfully" in { + val res = Faker.default.latitude() + assert(res.nonEmpty, res) + } + "longitude should return successfully" in { + val res = Faker.default.longitude() + assert(res.nonEmpty, res) + } + "secondaryAddress should return successfully" in { + val res = Faker.default.secondaryAddress() + assert(res.nonEmpty, res) + } + "state should return successfully" in { + val res = Faker.default.state() + assert(res.abbr.nonEmpty && res.name.nonEmpty, res) + } + "stateAbbr should return successfully" in { + val res = Faker.default.stateAbbr() + assert(res.nonEmpty, res) + } + "stateZip should return successfully" in { + val res = Faker.default.stateZip() + assert(res.nonEmpty, res) + } + "streetAddress should return successfully" in { + val res = Faker.default.streetAddress() + assert(res.nonEmpty, res) + } + "streetName should return successfully" in { + val res = Faker.default.streetName() + assert(res.nonEmpty, res) + } + "streetPrefix should return successfully" in { + val res = Faker.default.streetPrefix() + assert(res.nonEmpty, res) + } + "streetSuffix should return successfully" in { + val res = Faker.default.streetSuffix() + assert(res.nonEmpty, res) + } + } } } diff --git a/src/test/scala/faker/address/AddressArbitrarySpec.scala b/src/test/scala/faker/address/AddressArbitrarySpec.scala new file mode 100644 index 00000000..ef36c11b --- /dev/null +++ b/src/test/scala/faker/address/AddressArbitrarySpec.scala @@ -0,0 +1,25 @@ +package faker.address + +import faker.FakerArbitrarySpec +import faker.ResourceLoader.Implicits._ + +class AddressArbitrarySpec extends FakerArbitrarySpec { + "Internet" - { + testCanGen[BuildingNumber] + testCanGen[City] + testCanGen[CityPrefix] + testCanGen[CitySuffix] + testCanGen[Country] + testCanGen[DefaultCountry] + testCanGen[FullAddress] + testCanGen[Latitude] + testCanGen[Longitude] + testCanGen[PostalCode] + testCanGen[SecondaryAddress] + testCanGen[State] + testCanGen[StreetAddress] + testCanGen[StreetName] + testCanGen[StreetPrefix] + testCanGen[StreetSuffix] + } +}