1use crate::{aim::{WriterLike, Writer}, literals::{parse_date, parse_unsigned}};
7use nom::{
8 IResult, Parser,
9 character::complete::{char, multispace0},
10 combinator::opt,
11 sequence::delimited,
12};
13use jiff::{Timestamp, civil::DateTime};
14use std::fmt;
15
16pub fn now() -> DateTime {
17 match Timestamp::now().in_tz("UTC") {
18 Ok(zdt) => zdt.datetime(),
19 _ => unimplemented!(),
20 }
21}
22
23
24#[derive(Debug, PartialEq, Clone)]
28pub struct Version {
29 epoch: u32,
30 partial: u32,
31 date: DateTime,
32}
33
34impl Default for Version {
35 fn default() -> Self {
36 Version {
37 epoch: 0,
38 partial: 0,
39 date: now(),
40 }
41 }
42}
43
44impl Version {
45 pub fn new(epoch: u32, partial: u32, date: DateTime) -> Self {
47 Self { epoch, partial, date, }
48 }
49
50 pub fn epoch(&self) -> u32 {
52 self.epoch
53 }
54
55 pub fn set_epoch(&mut self, epoch: u32) {
57 self.epoch = epoch;
58 self.partial = 0;
59 }
60
61 pub fn increment_epoch(&mut self) {
63 self.epoch += 1;
64 self.partial = 0;
65 self.date = now();
66 }
67
68 pub fn partial(&self) -> u32 {
70 self.partial
71 }
72
73 pub fn set_partial(&mut self, partial: u32) {
75 self.partial = partial;
76 }
77
78 pub fn increment_partial(&mut self) {
80 self.partial += 1;
81 self.date = now();
82 }
83
84 pub fn date(&self) -> DateTime {
85 self.date
86 }
87
88 pub fn set_date(&mut self, date: DateTime) {
89 self.date = date;
90 }
91
92 pub fn print(&self, writer: &mut Writer) {
93 writer.write_char('[');
94 writer.write_unsigned(self.epoch);
95 if self.partial > 0 {
96 writer.write_char(':');
97 writer.write_unsigned(self.partial);
98 }
99 writer.write_str(" @");
100 writer.write_date(&self.date);
101 writer.write_char(']');
102 writer.write_eol();
103 }
104
105 pub fn to_formula(&self) -> String {
107 let mut writer = Writer::formulizer();
108 self.print(&mut writer);
109 writer.finish()
110 }
111}
112
113impl WriterLike for Version {
114 fn write(&self, writer: &mut Writer) {
115 self.print(writer);
116 }
117}
118
119impl fmt::Display for Version {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 write!(f, "{}", self.to_stringized())
122 }
123}
124
125pub fn parse_version(input: &str) -> IResult<&str, Version> {
129 delimited(
130 multispace0,
131 delimited(
132 char('['),
133 delimited(
134 multispace0,
135 parse_epoc,
136 multispace0
137 ),
138 char(']')
139 ),
140 multispace0
141 )
142 .parse(input)
143}
144
145fn parse_epoc(input: &str) -> IResult<&str, Version> {
149 let (input, (epoch, partial_opt, date_opt)) = (
150 parse_unsigned,
151 opt((
152 delimited(multispace0, char(':'), multispace0),
153 parse_unsigned,
154 )),
155 opt((
156 delimited(multispace0, char('@'), multispace0),
157 parse_date,
158 )),
159 )
160 .parse(input)?;
161 let partial = partial_opt.map(|(_, p)| p).unwrap_or(0);
162 let date = date_opt.map(|(_, d)| d).unwrap_or(now());
163
164 Ok((input, Version { epoch, partial, date }))
165}