Store earnings
This commit is contained in:
parent
c2bb26b8d5
commit
68bace9004
3 changed files with 224 additions and 2 deletions
40
Cargo.lock
generated
40
Cargo.lock
generated
|
|
@ -714,6 +714,7 @@ dependencies = [
|
||||||
"peg",
|
"peg",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1804,7 +1805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"toml_edit",
|
"toml_edit 0.19.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2047,6 +2048,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
|
@ -2310,11 +2320,26 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit 0.21.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
|
|
@ -2327,6 +2352,19 @@ dependencies = [
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 2.1.0",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ttf-parser"
|
name = "ttf-parser"
|
||||||
version = "0.19.2"
|
version = "0.19.2"
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,4 @@ itertools = "0.11.0"
|
||||||
peg = "0.8.2"
|
peg = "0.8.2"
|
||||||
serde = { version = "1.0.192", features = ["derive"] }
|
serde = { version = "1.0.192", features = ["derive"] }
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.108"
|
||||||
|
toml = "0.8.8"
|
||||||
|
|
|
||||||
185
src/main.rs
185
src/main.rs
|
|
@ -420,6 +420,105 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ErrorEdit<F, G> {
|
||||||
|
v: String,
|
||||||
|
parse: F,
|
||||||
|
on_submit: G,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ErrorEditState {
|
||||||
|
error: bool,
|
||||||
|
value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum ErrorEditMsg {
|
||||||
|
Edit(String),
|
||||||
|
Submit,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ErrorEditStyleSheet {
|
||||||
|
error: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl text_input::StyleSheet for ErrorEditStyleSheet {
|
||||||
|
type Style = iced::Theme;
|
||||||
|
|
||||||
|
fn active(&self, style: &Self::Style) -> text_input::Appearance {
|
||||||
|
style.active(&theme::TextInput::Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focused(&self, style: &Self::Style) -> text_input::Appearance {
|
||||||
|
let def = style.focused(&theme::TextInput::Default);
|
||||||
|
|
||||||
|
text_input::Appearance {
|
||||||
|
border_color: match self.error {
|
||||||
|
true => iced::Color::from_rgb8(240, 14, 27),
|
||||||
|
false => def.border_color,
|
||||||
|
},
|
||||||
|
..def
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn placeholder_color(&self, style: &Self::Style) -> iced::Color {
|
||||||
|
style.placeholder_color(&theme::TextInput::Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_color(&self, style: &Self::Style) -> iced::Color {
|
||||||
|
style.value_color(&theme::TextInput::Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disabled_color(&self, style: &Self::Style) -> iced::Color {
|
||||||
|
style.disabled_color(&theme::TextInput::Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selection_color(&self, style: &Self::Style) -> iced::Color {
|
||||||
|
style.selection_color(&theme::TextInput::Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disabled(&self, style: &Self::Style) -> text_input::Appearance {
|
||||||
|
style.disabled(&theme::TextInput::Default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M, F, G, T, E> iced::widget::Component<M, Renderer> for ErrorEdit<F, G>
|
||||||
|
where
|
||||||
|
F: FnMut(&str) -> Result<T, E>,
|
||||||
|
G: FnMut(T) -> M,
|
||||||
|
{
|
||||||
|
type State = ErrorEditState;
|
||||||
|
type Event = ErrorEditMsg;
|
||||||
|
|
||||||
|
fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<M> {
|
||||||
|
match event {
|
||||||
|
ErrorEditMsg::Edit(e) => {
|
||||||
|
state.error = (self.parse)(&e).is_err();
|
||||||
|
state.value = Some(e);
|
||||||
|
}
|
||||||
|
ErrorEditMsg::Submit => {
|
||||||
|
if let Some(v) = &state.value {
|
||||||
|
if let Ok(v) = (self.parse)(v) {
|
||||||
|
return Some((self.on_submit)(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self, state: &Self::State) -> iced_aw::Element<'_, Self::Event, Renderer> {
|
||||||
|
text_input("value", state.value.as_ref().unwrap_or(&self.v))
|
||||||
|
.on_input(ErrorEditMsg::Edit)
|
||||||
|
.on_submit(ErrorEditMsg::Submit)
|
||||||
|
.style(theme::TextInput::Custom(Box::new(ErrorEditStyleSheet {
|
||||||
|
error: state.error,
|
||||||
|
})))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Message {
|
enum Message {
|
||||||
Event(Event),
|
Event(Event),
|
||||||
|
|
@ -427,12 +526,17 @@ enum Message {
|
||||||
FontLoaded(Result<(), font::Error>),
|
FontLoaded(Result<(), font::Error>),
|
||||||
AddVariable(String),
|
AddVariable(String),
|
||||||
EditVariable(String, String),
|
EditVariable(String, String),
|
||||||
|
EditEarings1(f64),
|
||||||
|
EditEarings2(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Glaurung {
|
struct Glaurung {
|
||||||
|
config: Config,
|
||||||
recurring: BTreeMap<String, f64>,
|
recurring: BTreeMap<String, f64>,
|
||||||
variable: BTreeMap<String, (String, Option<f64>)>,
|
variable: BTreeMap<String, (String, Option<f64>)>,
|
||||||
save_file: PathBuf,
|
save_file: PathBuf,
|
||||||
|
earnings_1: f64,
|
||||||
|
earnings_2: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
|
|
@ -441,12 +545,17 @@ struct SaveFile {
|
||||||
recurring: BTreeMap<String, f64>,
|
recurring: BTreeMap<String, f64>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
variable: HashMap<String, String>,
|
variable: HashMap<String, String>,
|
||||||
|
#[serde(default)]
|
||||||
|
earnings_1: f64,
|
||||||
|
#[serde(default)]
|
||||||
|
earnings_2: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct AppConfig {
|
struct AppConfig {
|
||||||
save: SaveFile,
|
save: SaveFile,
|
||||||
save_file: PathBuf,
|
save_file: PathBuf,
|
||||||
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Glaurung {
|
impl Application for Glaurung {
|
||||||
|
|
@ -458,6 +567,7 @@ impl Application for Glaurung {
|
||||||
fn new(config: Self::Flags) -> (Self, Command<Message>) {
|
fn new(config: Self::Flags) -> (Self, Command<Message>) {
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
|
config: config.config,
|
||||||
recurring: config.save.recurring,
|
recurring: config.save.recurring,
|
||||||
variable: config
|
variable: config
|
||||||
.save
|
.save
|
||||||
|
|
@ -468,6 +578,8 @@ impl Application for Glaurung {
|
||||||
(k, (e, f.ok()))
|
(k, (e, f.ok()))
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
earnings_1: config.save.earnings_1,
|
||||||
|
earnings_2: config.save.earnings_2,
|
||||||
save_file: config.save_file,
|
save_file: config.save_file,
|
||||||
},
|
},
|
||||||
Command::batch(vec![
|
Command::batch(vec![
|
||||||
|
|
@ -506,6 +618,8 @@ impl Application for Glaurung {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, (e, _))| (k, e))
|
.map(|(k, (e, _))| (k, e))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
earnings_1: self.earnings_1,
|
||||||
|
earnings_2: self.earnings_2,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("could not write save file");
|
.expect("could not write save file");
|
||||||
|
|
@ -522,6 +636,12 @@ impl Application for Glaurung {
|
||||||
entry.0 = expr;
|
entry.0 = expr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::EditEarings1(v) => {
|
||||||
|
self.earnings_1 = v;
|
||||||
|
}
|
||||||
|
Message::EditEarings2(v) => {
|
||||||
|
self.earnings_2 = v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::none()
|
Command::none()
|
||||||
|
|
@ -555,6 +675,30 @@ impl Application for Glaurung {
|
||||||
.sum::<f64>()
|
.sum::<f64>()
|
||||||
))
|
))
|
||||||
.size(TEXT_EMPH2),
|
.size(TEXT_EMPH2),
|
||||||
|
text("Earnings").size(TEXT_H1),
|
||||||
|
row![
|
||||||
|
text(&self.config.person_1).size(TEXT_EMPH1),
|
||||||
|
component(ErrorEdit {
|
||||||
|
v: self.earnings_1.to_string(),
|
||||||
|
parse: |s: &str| -> Result<f64, _> { s.parse() },
|
||||||
|
on_submit: Message::EditEarings1,
|
||||||
|
})
|
||||||
|
]
|
||||||
|
.align_items(iced::Alignment::Center),
|
||||||
|
row![
|
||||||
|
text(&self.config.person_2).size(TEXT_EMPH1),
|
||||||
|
component(ErrorEdit {
|
||||||
|
v: self.earnings_2.to_string(),
|
||||||
|
parse: |s: &str| -> Result<f64, _> { s.parse() },
|
||||||
|
on_submit: Message::EditEarings2,
|
||||||
|
})
|
||||||
|
]
|
||||||
|
.align_items(iced::Alignment::Center),
|
||||||
|
text(&format!(
|
||||||
|
"Total earnings: {} €",
|
||||||
|
self.earnings_1 + self.earnings_2,
|
||||||
|
))
|
||||||
|
.size(TEXT_EMPH2),
|
||||||
]
|
]
|
||||||
.max_width(500)
|
.max_width(500)
|
||||||
.padding(5)
|
.padding(5)
|
||||||
|
|
@ -566,6 +710,32 @@ impl Application for Glaurung {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn person_1() -> String {
|
||||||
|
"Person 1".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn person_2() -> String {
|
||||||
|
"Person 2".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
struct Config {
|
||||||
|
#[serde(default = "person_1")]
|
||||||
|
person_1: String,
|
||||||
|
|
||||||
|
#[serde(default = "person_2")]
|
||||||
|
person_2: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
person_1: person_1(),
|
||||||
|
person_2: person_2(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let project_dir = ProjectDirs::from("net", "traxys", "glaurung").ok_or(anyhow!(""))?;
|
let project_dir = ProjectDirs::from("net", "traxys", "glaurung").ok_or(anyhow!(""))?;
|
||||||
let state_dir = project_dir
|
let state_dir = project_dir
|
||||||
|
|
@ -580,7 +750,20 @@ fn main() -> anyhow::Result<()> {
|
||||||
true => serde_json::from_reader(BufReader::new(File::open(&save_file)?))?,
|
true => serde_json::from_reader(BufReader::new(File::open(&save_file)?))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut settings = Settings::with_flags(AppConfig { save, save_file });
|
let config_file = project_dir.config_dir().join("config.toml");
|
||||||
|
let config: Config = match config_file.exists() {
|
||||||
|
true => {
|
||||||
|
let config = std::fs::read_to_string(config_file)?;
|
||||||
|
toml::from_str(&config)?
|
||||||
|
}
|
||||||
|
false => Config::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut settings = Settings::with_flags(AppConfig {
|
||||||
|
save,
|
||||||
|
save_file,
|
||||||
|
config,
|
||||||
|
});
|
||||||
settings.exit_on_close_request = false;
|
settings.exit_on_close_request = false;
|
||||||
|
|
||||||
Glaurung::run(settings)?;
|
Glaurung::run(settings)?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue