Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented subdivisions for US & DE #16

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,10 @@ VE = []
VN = []
ZM = []
ZW = []

[lints.rust]
unsafe_code = "forbid"

[lints.clippy]
enum_glob_use = "forbid"
pedantic = { level = "deny", priority = -1 }
227 changes: 172 additions & 55 deletions gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
class Country:
code: str
name: str
subdivision_code: str = None
subdivision_name: str = None


countries = [
Expand Down Expand Up @@ -45,6 +47,23 @@ class Country:
Country("FR", "France"),
Country("GE", "Georgia"),
Country("DE", "Germany"),
Country("DE", "Germany", "BB", "Brandenburg"),
Country("DE", "Germany", "BE", "Berlin"),
Country("DE", "Germany", "BW", "Baden-Württemberg"),
Country("DE", "Germany", "BY", "Bavaria (Bayern)"),
Country("DE", "Germany", "BYP", "Bavaria (Bayern) with more protestants"),
Country("DE", "Germany", "HB", "Bremen"),
Country("DE", "Germany", "HE", "Hesse (Hessen)"),
Country("DE", "Germany", "HH", "Hamburg"),
Country("DE", "Germany", "MV", "Mecklenburg-Vorpommern"),
Country("DE", "Germany", "NI", "Lower Saxony (Niedersachsen)"),
Country("DE", "Germany", "NW", "North Rhine-Westphalia (Nordrhein-Westfalen)"),
Country("DE", "Germany", "RP", "Rhineland-Palatinate (Rheinland-Pfalz)"),
Country("DE", "Germany", "SH", "Schleswig-Holstein"),
Country("DE", "Germany", "SL", "Saarland"),
Country("DE", "Germany", "SN", "Saxony (Sachsen)"),
Country("DE", "Germany", "ST", "Saxony-Anhalt (Sachsen-Anhalt)"),
Country("DE", "Germany", "TH", "Thuringia (Thüringen)"),
Country("GR", "Greece"),
Country("HN", "Honduras"),
Country("HK", "Hong Kong"),
Expand Down Expand Up @@ -105,6 +124,63 @@ class Country:
Country("AE", "United Arab Emirates"),
Country("GB", "United Kingdom"),
Country("US", "United States"),
Country("US", "United States", "AK", "Alaska"),
Country("US", "United States", "AL", "Alabama"),
Country("US", "United States", "AR", "Arkansas"),
Country("US", "United States", "AS", "American Samoa"),
Country("US", "United States", "AZ", "Arizona"),
Country("US", "United States", "CA", "California"),
Country("US", "United States", "CO", "Colorado"),
Country("US", "United States", "CT", "Connecticut"),
Country("US", "United States", "DC", "District of Columbia"),
Country("US", "United States", "DE", "Delaware"),
Country("US", "United States", "FL", "Florida"),
Country("US", "United States", "GA", "Georgia"),
Country("US", "United States", "GU", "Guam"),
Country("US", "United States", "HI", "Hawaii"),
Country("US", "United States", "IA", "Iowa"),
Country("US", "United States", "ID", "Idaho"),
Country("US", "United States", "IL", "Illinois"),
Country("US", "United States", "IN", "Indiana"),
Country("US", "United States", "KS", "Kansas"),
Country("US", "United States", "KY", "Kentucky"),
Country("US", "United States", "LA", "Louisiana"),
Country("US", "United States", "MA", "Massachusetts"),
Country("US", "United States", "MD", "Maryland"),
Country("US", "United States", "ME", "Maine"),
Country("US", "United States", "MI", "Michigan"),
Country("US", "United States", "MN", "Minnesota"),
Country("US", "United States", "MO", "Missouri"),
Country("US", "United States", "MP", "Northern Mariana Islands"),
Country("US", "United States", "MS", "Mississippi"),
Country("US", "United States", "MT", "Montana"),
Country("US", "United States", "NC", "North Carolina"),
Country("US", "United States", "ND", "North Dakota"),
Country("US", "United States", "NE", "Nebraska"),
Country("US", "United States", "NH", "New Hampshire"),
Country("US", "United States", "NJ", "New Jersey"),
Country("US", "United States", "NM", "New Mexico"),
Country("US", "United States", "NV", "Nevada"),
Country("US", "United States", "NY", "New York"),
Country("US", "United States", "OH", "Ohio"),
Country("US", "United States", "OK", "Oklahoma"),
Country("US", "United States", "OR", "Oregon"),
Country("US", "United States", "PA", "Pennsylvania"),
Country("US", "United States", "PR", "Puerto Rico"),
Country("US", "United States", "RI", "Rhode Island"),
Country("US", "United States", "SC", "South Carolina"),
Country("US", "United States", "SD", "South Dakota"),
Country("US", "United States", "TN", "Tennessee"),
Country("US", "United States", "TX", "Texas"),
Country("US", "United States", "UM", "United States Minor Outlying Islands"),
Country("US", "United States", "UT", "Utah"),
Country("US", "United States", "VA", "Virginia"),
Country("US", "United States", "VI", "Virgin Islands, U.S.."),
Country("US", "United States", "VT", "Vermont"),
Country("US", "United States", "WA", "Washington"),
Country("US", "United States", "WI", "Wisconsin"),
Country("US", "United States", "WV", "West Virginia"),
Country("US", "United States", "WY", "Wyoming"),
Country("UY", "Uruguay"),
Country("UZ", "Uzbekistan"),
Country("VE", "Venezuela"),
Expand All @@ -120,12 +196,13 @@ class Country:

/// Two-letter country codes defined in ISO 3166-1 alpha-2 .
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub enum Country {
{%- for country in countries %}
#[cfg(feature = "{{country.code}}")]
/// {{country.name}}
{{country.code}},
#[cfg(feature = "{{country.code}}")]
/// {{country|display_name}}
{{country|enum_name}},
{%- endfor %}
}

Expand All @@ -136,102 +213,129 @@ class Country:
}

impl AsRef<str> for Country {
fn as_ref(&self) -> &str {
match self {
#[allow(clippy::too_many_lines)]
fn as_ref(&self) -> &str {
match self {
{%- for country in countries %}
#[cfg(feature = "{{country.code}}")]
Country::{{country.code}} => "{{country.code}}",
#[cfg(feature = "{{country.code}}")]
Country::{{country|enum_name}} => "{{country|enum_name}}",
{%- endfor %}
}
}
}
}

impl std::str::FromStr for Country {
type Err = Error;
type Err = Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
#[allow(clippy::too_many_lines)]
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s {
{%- for country in countries %}
#[cfg(feature = "{{country.code}}")]
"{{country.code}}" => Country::{{country.code}},
#[cfg(feature = "{{country.code}}")]
"{{country|enum_name}}" => Country::{{country|enum_name}},
{%- endfor %}
_ => return Err(Error::CountryNotAvailable),
})
}
_ => return Err(Error::CountryNotAvailable),
})
}
}

"""

build = """
use std::collections::HashSet;

use crate::{data::*, prelude::*, HolidayMap, Result, Year};
use crate::{data, prelude::*, HolidayMap, Result, Year};

fn should_build(countries: Option<&HashSet<Country>>, country: Country) -> bool {
match countries {
Some(c) => c.contains(&country),
None => true,
}
}

/// Generate holiday map for the specified countries and years.
#[allow(clippy::too_many_lines)]
pub fn build(countries: Option<&HashSet<Country>>, years: Option<&std::ops::Range<Year>>) -> Result<HolidayMap> {
let mut map = HolidayMap::new();
let mut map = HolidayMap::new();

{% for country in countries %}
#[cfg(feature = "{{country.code}}")]
if countries.is_none() || countries.unwrap().contains(&Country::{{country.code}}) {
map.insert(Country::{{country.code}}, {{country.code|escape}}::build(&years)?);
}
#[cfg(feature = "{{country.code}}")]
if should_build(countries, Country::{{country|enum_name}}) {
map.insert(Country::{{country|enum_name}}, data::{{country|mod_name|escape}}::build(years)?);
}
{% endfor %}
Ok(map)

Ok(map)
}

"""

country_mod = """
mod helper;

use crate::{prelude::*, Holiday, NaiveDateExt, Result, Year};
use helper::build_year;
use crate::{prelude::*, HolidayPerCountryMap, NaiveDateExt, Result, Year};
use helper::{build_subdivision_year, build_year};

use chrono::NaiveDate;
use std::collections::BTreeMap;
use std::collections::HashMap;

{% for country in countries %}
#[cfg(feature = "{{country.code}}")]
pub mod {{country.code|escape}};
pub mod {{country|mod_name|escape}};
{% endfor %}
"""

build_country = """
//! {{country}}
//! {{country|display_name}}
#[allow(clippy::wildcard_imports)]
use super::*;

/// Generate holiday map for {{country}}.
#[allow(unused_mut, unused_variables)]
pub fn build(years: &Option<&std::ops::Range<Year>>) -> Result<HashMap<Year, BTreeMap<NaiveDate, Holiday>>> {
let mut map = HashMap::new();
const COUNTY_NAME: &str = "{{country|display_name}}";
const COUNTY_CODE: Country = Country::{{country|enum_name}};

/// Generate holiday map for {{country|display_name}}.
#[allow(unused_mut, unused_variables, clippy::too_many_lines, clippy::missing_errors_doc)]
pub fn build(years: Option<&std::ops::Range<Year>>) -> Result<HolidayPerCountryMap> {
let mut map = HashMap::new();
{% if country.subdivision_code != None %}
let mut national_holidays = de::build(years)?;
{%- endif %}

{%- for year in years %}
{% if holiday(years=year) %}
build_year(
years,
{{year}},
vec![
{% for date, name in holiday(years=year).items() %}
(NaiveDate::from_ymd_res({{date|year}}, {{date|month}}, {{date|day}})?, "{{name}}"),
{% if holiday(years=year, subdiv=country.subdivision_code) %}

{%- if country.subdivision_code != None %}
build_subdivision_year(
years,
{{year}},
&mut national_holidays,
{%- else %}
build_year(
years,
{{year}},
{%- endif %}
[
{%- for date, name in holiday(years=year, subdiv=country.subdivision_code).items() %}
{%- if country.subdivision_code == None or date not in holiday(years=year) %}
(NaiveDate::from_ymd_res({{date|year}}, {{date|month}}, {{date|day}})?, "{{name}}"),
{%- endif %}
{%- endfor %}
],
&mut map,
Country::{{code}},
"{{country}}",
);
],
&mut map,
COUNTY_CODE,
COUNTY_NAME,
);
{%- endif %}
{%- endfor %}

Ok(map)
Ok(map)
}
"""


def lower(code: str) -> str:
return code.lower()


def escape(code: str) -> str:
rust_keywords = ["as", "in", "do"]
lower = code.lower()
Expand All @@ -240,18 +344,31 @@ def escape(code: str) -> str:
else:
return lower

def enum_name(country: Country) -> str:
if country.subdivision_code == None:
return country.code
else:
return country.code + "_" + country.subdivision_code

def empty_holiday(**kwargs):
return {}
def mod_name(country: Country) -> str:
return enum_name(country).lower()

def display_name(country: Country) -> str:
if country.subdivision_name == None:
return country.name
else:
return country.name + " (" + country.subdivision_name + ")"

if __name__ == "__main__":
env = Environment()
env.filters["year"] = lambda d: d.year
env.filters["month"] = lambda d: d.month
env.filters["day"] = lambda d: d.day
env.filters["escape"] = escape
env.filters["lower"] = lower
env.filters["enum_name"] = enum_name
env.filters["mod_name"] = mod_name
env.filters["display_name"] = display_name

with open("src/country.rs", "w") as f:
rendered = env.from_string(country).render(countries=countries)
f.write(rendered)
Expand All @@ -263,13 +380,13 @@ def empty_holiday(**kwargs):
with open("src/data/mod.rs", "w") as f:
rendered = env.from_string(country_mod).render(countries=countries)
f.write(rendered)

for country in countries:
with open("src/data/{}.rs".format(country.code.lower()), "w") as f:
holiday = getattr(holidays, country.code, None)
with open("src/data/{}.rs".format(mod_name(country)), "w") as f:
# Could use `getattr(holidays, country.code, {}).subdivisions` but this only has the codes and not the names.
holiday = getattr(holidays, country.code, {})
rendered = env.from_string(build_country).render(
code=country.code,
country=country.name,
country=country,
years=years,
holiday=holiday or empty_holiday)
holiday=holiday)
f.write(rendered)
Loading
Loading