Use system tzinfo instead of compiling it into the binary

This commit is contained in:
Joscha 2021-11-22 00:42:55 +01:00
parent 36aa6abcb9
commit d7df47eba4
6 changed files with 46 additions and 207 deletions

175
Cargo.lock generated
View file

@ -73,12 +73,6 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.19" version = "0.4.19"
@ -92,28 +86,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "chrono-tz"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c01c1c607d25c71bbaa67c113d6c6b36c434744b4fd66691d711b5b1bc0c8b"
dependencies = [
"chrono",
"chrono-tz-build",
"phf",
]
[[package]]
name = "chrono-tz-build"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069"
dependencies = [
"parse-zoneinfo",
"phf",
"phf_codegen",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.3" version = "2.33.3"
@ -153,17 +125,6 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.3" version = "0.3.3"
@ -225,15 +186,6 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "parse-zoneinfo"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
dependencies = [
"regex",
]
[[package]] [[package]]
name = "pest" name = "pest"
version = "2.1.3" version = "2.1.3"
@ -277,51 +229,6 @@ dependencies = [
"sha-1", "sha-1",
] ]
[[package]]
name = "phf"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_shared"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher",
"uncased",
]
[[package]]
name = "ppv-lite86"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.4" version = "1.0.4"
@ -364,61 +271,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]] [[package]]
name = "sha-1" name = "sha-1"
version = "0.8.2" version = "0.8.2"
@ -431,12 +283,6 @@ dependencies = [
"opaque-debug", "opaque-debug",
] ]
[[package]]
name = "siphasher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.8.0" version = "0.8.0"
@ -524,11 +370,11 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
"chrono-tz",
"pest", "pest",
"pest_derive", "pest_derive",
"structopt", "structopt",
"thiserror", "thiserror",
"tzfile",
] ]
[[package]] [[package]]
@ -537,21 +383,22 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
[[package]]
name = "tzfile"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f59c22c42a2537e4c7ad21a4007273bbc5bebed7f36bc93730a5780e22a4592e"
dependencies = [
"byteorder",
"chrono",
]
[[package]] [[package]]
name = "ucd-trie" name = "ucd-trie"
version = "0.1.3" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "uncased"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
dependencies = [
"version_check",
]
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.8.0" version = "1.8.0"

View file

@ -6,8 +6,8 @@ edition = "2018"
[dependencies] [dependencies]
anyhow = "1.0.45" anyhow = "1.0.45"
chrono = "0.4.19" chrono = "0.4.19"
chrono-tz = "0.6.0"
pest = "2.1.3" pest = "2.1.3"
pest_derive = "2.1.0" pest_derive = "2.1.0"
structopt = "0.3.25" structopt = "0.3.25"
thiserror = "1.0.30" thiserror = "1.0.30"
tzfile = "0.1.3"

View file

@ -2,7 +2,8 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{fs, io, result}; use std::{fs, io, result};
use chrono_tz::Tz; use chrono::{DateTime, Utc};
use tzfile::Tz;
use self::commands::File; use self::commands::File;
@ -13,7 +14,7 @@ mod parse;
#[derive(Debug)] #[derive(Debug)]
pub struct Files { pub struct Files {
files: HashMap<PathBuf, File>, files: HashMap<PathBuf, File>,
timezone: Option<Tz>, timezone: Tz,
} }
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
@ -25,9 +26,9 @@ pub enum Error {
#[error("{file1} has time zone {tz1} but {file2} has time zone {tz2}")] #[error("{file1} has time zone {tz1} but {file2} has time zone {tz2}")]
TzConflict { TzConflict {
file1: PathBuf, file1: PathBuf,
tz1: Tz, tz1: String,
file2: PathBuf, file2: PathBuf,
tz2: Tz, tz2: String,
}, },
} }
@ -35,20 +36,15 @@ pub type Result<T> = result::Result<T, Error>;
impl Files { impl Files {
pub fn load(path: &Path) -> Result<Self> { pub fn load(path: &Path) -> Result<Self> {
let mut new = Self { let mut files = HashMap::new();
files: HashMap::new(), Self::load_file(&mut files, path)?;
timezone: None, let timezone = Self::determine_timezone(&files)?;
}; Ok(Self { files, timezone })
new.load_file(path)?;
new.determine_timezone()?;
Ok(new)
} }
fn load_file(&mut self, path: &Path) -> Result<()> { fn load_file(files: &mut HashMap<PathBuf, File>, path: &Path) -> Result<()> {
let canon_path = path.canonicalize()?; let canon_path = path.canonicalize()?;
if self.files.contains_key(&canon_path) { if files.contains_key(&canon_path) {
// We've already loaded this exact file. // We've already loaded this exact file.
return Ok(()); return Ok(());
} }
@ -57,39 +53,43 @@ impl Files {
let file = parse::parse(path, &content)?; let file = parse::parse(path, &content)?;
let includes = file.includes.clone(); let includes = file.includes.clone();
self.files.insert(canon_path, file); files.insert(canon_path, file);
for include in includes { for include in includes {
self.load_file(&include)?; Self::load_file(files, &include)?;
} }
Ok(()) Ok(())
} }
fn determine_timezone(&mut self) -> Result<()> { fn determine_timezone(files: &HashMap<PathBuf, File>) -> Result<Tz> {
let mut found: Option<(PathBuf, Tz)> = None; let mut found: Option<(PathBuf, String)> = None;
for file in self.files.values() { for file in files.values() {
if let Some(file_tz) = file.timezone { if let Some(file_tz) = &file.timezone {
if let Some((found_name, found_tz)) = &found { if let Some((found_name, found_tz)) = &found {
if *found_tz != file_tz { if found_tz != file_tz {
return Err(Error::TzConflict { return Err(Error::TzConflict {
file1: found_name.clone(), file1: found_name.clone(),
tz1: *found_tz, tz1: found_tz.clone(),
file2: file.name.clone(), file2: file.name.clone(),
tz2: file_tz, tz2: file_tz.clone(),
}); });
} }
} else { } else {
found = Some((file.name.clone(), file_tz)); found = Some((file.name.clone(), file_tz.clone()));
} }
} }
} }
if let Some((_, tz)) = found { Ok(if let Some((_, tz)) = found {
self.timezone = Some(tz); Tz::named(&tz)?
} } else {
Tz::local()?
})
}
Ok(()) pub fn now(&self) -> DateTime<&Tz> {
Utc::now().with_timezone(&&self.timezone)
} }
} }

View file

@ -1,7 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use chrono::NaiveDate; use chrono::NaiveDate;
use chrono_tz::Tz;
#[derive(Debug)] #[derive(Debug)]
pub struct Time { pub struct Time {
@ -331,6 +330,6 @@ pub enum Command {
pub struct File { pub struct File {
pub name: PathBuf, pub name: PathBuf,
pub includes: Vec<PathBuf>, pub includes: Vec<PathBuf>,
pub timezone: Option<Tz>, pub timezone: Option<String>,
pub commands: Vec<Command>, pub commands: Vec<Command>,
} }

View file

@ -2,7 +2,6 @@ use std::path::{Path, PathBuf};
use std::result; use std::result;
use chrono::NaiveDate; use chrono::NaiveDate;
use chrono_tz::Tz;
use pest::error::ErrorVariant; use pest::error::ErrorVariant;
use pest::iterators::Pair; use pest::iterators::Pair;
use pest::prec_climber::{Assoc, Operator, PrecClimber}; use pest::prec_climber::{Assoc, Operator, PrecClimber};
@ -38,15 +37,9 @@ fn parse_include(p: Pair<Rule>) -> String {
p.into_inner().next().unwrap().as_str().to_string() p.into_inner().next().unwrap().as_str().to_string()
} }
fn parse_timezone(p: Pair<Rule>) -> Result<Tz> { fn parse_timezone(p: Pair<Rule>) -> String {
assert_eq!(p.as_rule(), Rule::timezone); assert_eq!(p.as_rule(), Rule::timezone);
let span = p.as_span(); p.into_inner().next().unwrap().as_str().trim().to_string()
p.into_inner()
.next()
.unwrap()
.as_str()
.parse()
.map_err(|_| error(span, "invalid timezone"))
} }
fn parse_number(p: Pair<Rule>) -> i32 { fn parse_number(p: Pair<Rule>) -> i32 {
@ -720,7 +713,7 @@ fn parse_command(p: Pair<Rule>, file: &mut File) -> Result<()> {
file.includes.push(parent.join(parse_include(p))); file.includes.push(parent.join(parse_include(p)));
} }
Rule::timezone => match file.timezone { Rule::timezone => match file.timezone {
None => file.timezone = Some(parse_timezone(p)?), None => file.timezone = Some(parse_timezone(p)),
Some(_) => fail(p.as_span(), "cannot set timezone multiple times")?, Some(_) => fail(p.as_span(), "cannot set timezone multiple times")?,
}, },
Rule::task => file.commands.push(Command::Task(parse_task(p)?)), Rule::task => file.commands.push(Command::Task(parse_task(p)?)),

View file

@ -16,6 +16,6 @@ pub struct Opt {
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let opt = Opt::from_args(); let opt = Opt::from_args();
let files = Files::load(&opt.file)?; let files = Files::load(&opt.file)?;
println!("{:#?}", files); println!("{}", files.now().format("%F %T %Z"));
Ok(()) Ok(())
} }