unwrap() purge

This commit is contained in:
thatscringebro
2026-04-06 13:32:42 -04:00
parent ba1b5349db
commit 1c65375044
7 changed files with 159 additions and 172 deletions

View File

@@ -33,8 +33,15 @@ impl App {
} }
}; };
let mut error = String::new();
data_layer::setup(&con); data_layer::setup(&con);
let tr_types = data_layer::get_transaction_types(&con); let tr_types = match data_layer::get_transaction_types(&con) {
Ok(tt) => tt,
Err(e) => {
error = e.to_string();
Vec::new()
}
};
return App { return App {
current_screen: CurrentScreen::Main, current_screen: CurrentScreen::Main,
@@ -55,7 +62,11 @@ impl App {
"Chart 3".to_string(), "Chart 3".to_string(),
] ]
.to_vec(), .to_vec(),
error: String::new(), error: if error.is_empty() {
String::new()
} else {
error
},
error_showing: false, error_showing: false,
selected_tr_type_chart: 1, selected_tr_type_chart: 1,
selected_tr_type_chart_desc: tr_types selected_tr_type_chart_desc: tr_types
@@ -86,10 +97,16 @@ impl App {
} }
pub fn get_list_trx(&mut self) -> Vec<Transaction> { pub fn get_list_trx(&mut self) -> Vec<Transaction> {
let accounts = self return match self
.trx_table .trx_table
.get_trx(self.acc_list.get_selected_id(), &self.connection); .get_trx(self.acc_list.get_selected_id(), &self.connection)
return accounts.to_vec(); {
Ok(t) => t,
Err(e) => {
self.error = e.to_string();
Vec::new()
}
};
} }
pub fn next_tr(&mut self) { pub fn next_tr(&mut self) {
@@ -120,7 +137,14 @@ impl App {
pub fn save_new_tr(&mut self) { pub fn save_new_tr(&mut self) {
let new = self.new_transaction.get_id() == 0; let new = self.new_transaction.get_id() == 0;
self.next_tr_input(); self.next_tr_input();
let tr = data_layer::upsert_transaction(&self.connection, self.new_transaction.clone()); let tr =
match data_layer::upsert_transaction(&self.connection, self.new_transaction.clone()) {
Ok(tr) => tr,
Err(e) => {
self.error = e.to_string();
return;
}
};
if new { if new {
self.trx_table.add_tr(tr); self.trx_table.add_tr(tr);
} }
@@ -305,19 +329,31 @@ impl App {
} }
} }
pub fn get_cumulative_total(&self) -> Vec<(f64, f64)> { pub fn get_cumulative_total(&mut self) -> Vec<(f64, f64)> {
return data_layer::get_cumulative_account_sum( return match data_layer::get_cumulative_account_sum(
&self.connection, &self.connection,
self.acc_list.get_selected_id(), self.acc_list.get_selected_id(),
); ) {
Ok(d) => d,
Err(e) => {
self.error = e.to_string();
Vec::new()
}
};
} }
pub fn get_trtype_data(&self) -> Vec<(f64, f64)> { pub fn get_trtype_data(&mut self) -> Vec<(f64, f64)> {
return data_layer::get_tr_type_price_over_time( return match data_layer::get_tr_type_price_over_time(
&self.connection, &self.connection,
self.acc_list.get_selected_id(), self.acc_list.get_selected_id(),
self.selected_tr_type_chart, self.selected_tr_type_chart,
); ) {
Ok(d) => d,
Err(e) => {
self.error = e.to_string();
Vec::new()
}
};
} }
pub fn select_tr_type_chart(&mut self, id: i64) { pub fn select_tr_type_chart(&mut self, id: i64) {

View File

@@ -1,3 +1,5 @@
use std::error::Error;
use crate::entities::*; use crate::entities::*;
use chrono::DateTime; use chrono::DateTime;
use sqlite::{Connection, State}; use sqlite::{Connection, State};
@@ -82,28 +84,7 @@ pub fn upsert_account(
)); ));
} }
// pub fn get_account(con: &Connection, id: i64) -> Account { pub fn get_account_total(id: i64, con: &Connection) -> Result<f64, Box<dyn Error>> {
// let query = "SELECT * FROM Accounts WHERE id = ?";
// let mut statement = con.prepare(query).unwrap();
// statement.bind((1, id)).unwrap();
// if let Ok(State::Row) = statement.next() {
// return Account::new(
// statement.read::<i64, _>("id").unwrap(),
// statement.read::<String, _>("name").unwrap(),
// statement.read::<f64, _>("asset_price").unwrap(),
// statement
// .read::<i64, _>("type")
// .unwrap()
// .try_into()
// .unwrap(),
// );
// } else {
// return Account::new(0, "".to_string(), 0.0, AccountType::Cash);
// }
// }
pub fn get_account_total(id: i64, con: &Connection) -> f64 {
let mut query = " let mut query = "
SELECT SELECT
SUM( SUM(
@@ -119,16 +100,16 @@ JOIN Accounts a ON t.account_id = a.id"
query.push_str(" WHERE account_id = :id") query.push_str(" WHERE account_id = :id")
} }
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query)?;
if id != 0 { if id != 0 {
statement.bind((":id", id)).unwrap(); statement.bind((":id", id))?;
} }
if let Ok(State::Row) = statement.next() { if let Ok(State::Row) = statement.next() {
return statement.read::<f64, _>("total").unwrap(); return Ok(statement.read::<f64, _>("total")?);
} else { } else {
return 0.0; return Ok(0.0);
} }
} }
@@ -156,62 +137,40 @@ pub fn get_accounts(con: &Connection) -> Result<Vec<Account>, Box<dyn std::error
return Ok(vec); return Ok(vec);
} }
// pub fn upsert_transaction_type(con: &Connection, tt: TransactionType) -> TransactionType { pub fn get_transaction_type(con: &Connection, id: i64) -> Result<TransactionType, Box<dyn Error>> {
// let query;
// if tt.get_id() == 0 {
// query = "INSERT INTO TransactionTypes (description) VALUES (?) RETURNING id;";
// } else {
// query = "UPDATE TransactionTypes SET description = ? WHERE id = ? RETURNING id;";
// }
// let mut statement = con.prepare(query).unwrap();
// statement.bind((1, &tt.get_description() as &str)).unwrap();
// if tt.get_id() != 0 {
// statement.bind((2, tt.get_id())).unwrap();
// }
// let id;
// if let Ok(State::Row) = statement.next() {
// id = statement.read::<i64, _>("id").unwrap();
// } else {
// id = 0;
// }
// return TransactionType::new(id, tt.get_description());
// }
pub fn get_transaction_type(con: &Connection, id: i64) -> TransactionType {
let query = "SELECT * FROM TransactionTypes WHERE id = ?"; let query = "SELECT * FROM TransactionTypes WHERE id = ?";
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query)?;
statement.bind((1, id)).unwrap(); statement.bind((1, id))?;
if let Ok(State::Row) = statement.next() { if let Ok(State::Row) = statement.next() {
return TransactionType::new( return Ok(TransactionType::new(
statement.read::<i64, _>("id").unwrap(), statement.read::<i64, _>("id")?,
statement.read::<String, _>("description").unwrap(), statement.read::<String, _>("description")?,
); ));
} else { } else {
return TransactionType::new(0, "".to_string()); return Ok(TransactionType::new(0, "".to_string()));
} }
} }
pub fn get_transaction_types(con: &Connection) -> Vec<TransactionType> { pub fn get_transaction_types(con: &Connection) -> Result<Vec<TransactionType>, Box<dyn Error>> {
let query = "SELECT * FROM TransactionTypes"; let query = "SELECT * FROM TransactionTypes";
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query)?;
let mut vec = Vec::<TransactionType>::new(); let mut vec = Vec::<TransactionType>::new();
while let Ok(State::Row) = statement.next() { while let Ok(State::Row) = statement.next() {
vec.push(TransactionType::new( vec.push(TransactionType::new(
statement.read::<i64, _>("id").unwrap(), statement.read::<i64, _>("id")?,
statement.read::<String, _>("description").unwrap(), statement.read::<String, _>("description")?,
)); ));
} }
return vec; return Ok(vec);
} }
pub fn upsert_transaction(con: &Connection, tr: Transaction) -> Transaction { pub fn upsert_transaction(
con: &Connection,
tr: Transaction,
) -> Result<Transaction, Box<dyn Error>> {
let query; let query;
if tr.get_id() == 0 { if tr.get_id() == 0 {
query = "INSERT INTO Transactions query = "INSERT INTO Transactions
@@ -224,32 +183,26 @@ pub fn upsert_transaction(con: &Connection, tr: Transaction) -> Transaction {
WHERE id = :id RETURNING id;"; WHERE id = :id RETURNING id;";
} }
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query)?;
statement.bind((":ac_id", tr.get_account_id())).unwrap(); statement.bind((":ac_id", tr.get_account_id()))?;
statement statement.bind((":type_id", tr.get_type().get_id()))?;
.bind((":type_id", tr.get_type().get_id())) statement.bind((":amnt", tr.get_amount()))?;
.unwrap(); statement.bind((":date", tr.get_date().timestamp()))?;
statement.bind((":amnt", tr.get_amount())).unwrap(); statement.bind((":desc", &tr.get_desc() as &str))?;
statement statement.bind((":asset_amnt", tr.get_asset_amnt()))?;
.bind((":date", tr.get_date().timestamp()))
.unwrap();
statement.bind((":desc", &tr.get_desc() as &str)).unwrap();
statement
.bind((":asset_amnt", tr.get_asset_amnt()))
.unwrap();
if tr.get_id() != 0 { if tr.get_id() != 0 {
statement.bind((":id", tr.get_id())).unwrap(); statement.bind((":id", tr.get_id()))?;
} }
let id; let id;
if let Ok(State::Row) = statement.next() { if let Ok(State::Row) = statement.next() {
id = statement.read::<i64, _>("id").unwrap(); id = statement.read::<i64, _>("id")?;
} else { } else {
id = 0; id = 0;
} }
return Transaction::new( return Ok(Transaction::new(
id, id,
tr.get_account_id(), tr.get_account_id(),
tr.get_amount(), tr.get_amount(),
@@ -258,82 +211,64 @@ pub fn upsert_transaction(con: &Connection, tr: Transaction) -> Transaction {
tr.get_type().get_id(), tr.get_type().get_id(),
tr.get_asset_amnt(), tr.get_asset_amnt(),
con, con,
); ));
} }
// pub fn get_transaction(con: &Connection, id: i64) -> Transaction { pub fn get_account_transactions(
// let query = "SELECT * FROM Transactions WHERE id = :id"; con: &Connection,
// let mut statement = con.prepare(query).unwrap(); ac_id: i64,
// statement.bind((":id", id)).unwrap(); ) -> Result<Vec<Transaction>, Box<dyn Error>> {
// if let Ok(State::Row) = statement.next() {
// return Transaction::new(
// statement.read::<i64, _>("id").unwrap(),
// statement.read::<i64, _>("account_id").unwrap(),
// statement.read::<f64, _>("amount").unwrap(),
// DateTime::from_timestamp(statement.read::<i64, _>("date").unwrap(), 0).unwrap(),
// statement.read::<String, _>("description").unwrap(),
// statement.read::<i64, _>("type_id").unwrap(),
// statement.read::<f64, _>("asset_amount").unwrap(),
// con,
// );
// } else {
// return Transaction::new_empty();
// }
// }
pub fn get_account_transactions(con: &Connection, ac_id: i64) -> Vec<Transaction> {
if ac_id == 0 { if ac_id == 0 {
return get_transactions(con); return get_transactions(con);
} }
let query = "SELECT * FROM Transactions WHERE account_id = :id ORDER BY Date DESC"; let query = "SELECT * FROM Transactions WHERE account_id = :id ORDER BY Date DESC";
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query)?;
statement.bind((":id", ac_id)).unwrap(); statement.bind((":id", ac_id))?;
let mut vec = Vec::<Transaction>::new(); let mut vec = Vec::<Transaction>::new();
while let Ok(State::Row) = statement.next() { while let Ok(State::Row) = statement.next() {
vec.push(Transaction::new( vec.push(Transaction::new(
statement.read::<i64, _>("id").unwrap(), statement.read::<i64, _>("id")?,
statement.read::<i64, _>("account_id").unwrap(), statement.read::<i64, _>("account_id")?,
statement.read::<f64, _>("amount").unwrap(), statement.read::<f64, _>("amount")?,
DateTime::from_timestamp(statement.read::<i64, _>("date").unwrap(), 0).unwrap(), DateTime::from_timestamp(statement.read::<i64, _>("date")?, 0).unwrap_or_default(),
statement.read::<String, _>("description").unwrap(), statement.read::<String, _>("description")?,
statement.read::<i64, _>("type_id").unwrap(), statement.read::<i64, _>("type_id")?,
statement.read::<f64, _>("asset_amount").unwrap(), statement.read::<f64, _>("asset_amount")?,
con, con,
)); ));
} }
return vec; return Ok(vec);
} }
pub fn get_transactions(con: &Connection) -> Vec<Transaction> { pub fn get_transactions(con: &Connection) -> Result<Vec<Transaction>, Box<dyn Error>> {
let query = "SELECT * FROM Transactions ORDER BY Date DESC"; let query = "SELECT * FROM Transactions ORDER BY Date DESC";
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query)?;
let mut vec = Vec::<Transaction>::new(); let mut vec = Vec::<Transaction>::new();
while let Ok(State::Row) = statement.next() { while let Ok(State::Row) = statement.next() {
vec.push(Transaction::new( vec.push(Transaction::new(
statement.read::<i64, _>("id").unwrap(), statement.read::<i64, _>("id")?,
statement.read::<i64, _>("account_id").unwrap(), statement.read::<i64, _>("account_id")?,
statement.read::<f64, _>("amount").unwrap(), statement.read::<f64, _>("amount")?,
DateTime::from_timestamp(statement.read::<i64, _>("date").unwrap(), 0).unwrap(), DateTime::from_timestamp(statement.read::<i64, _>("date")?, 0).unwrap_or_default(),
statement.read::<String, _>("description").unwrap(), statement.read::<String, _>("description")?,
statement.read::<i64, _>("type_id").unwrap(), statement.read::<i64, _>("type_id")?,
statement.read::<f64, _>("asset_amount").unwrap(), statement.read::<f64, _>("asset_amount")?,
con, con,
)); ));
} }
return vec; return Ok(vec);
} }
pub fn get_tr_type_price_over_time( pub fn get_tr_type_price_over_time(
con: &Connection, con: &Connection,
ac_id: i64, ac_id: i64,
tr_type_id: i64, tr_type_id: i64,
) -> Vec<(f64, f64)> { ) -> Result<Vec<(f64, f64)>, Box<dyn Error>> {
let mut query = " let mut query = "
SELECT strftime('%Y%m', datetime(date, 'unixepoch')) as z, SUM(amount) as y SELECT strftime('%Y%m', datetime(date, 'unixepoch')) as z, SUM(amount) as y
FROM Transactions FROM Transactions
@@ -345,24 +280,27 @@ WHERE type_id = :tr_type
} }
query.push_str(" GROUP BY z;"); query.push_str(" GROUP BY z;");
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query)?;
statement.bind((":tr_type", tr_type_id)).unwrap(); statement.bind((":tr_type", tr_type_id))?;
if ac_id != 0 { if ac_id != 0 {
statement.bind((":ac_id", ac_id)).unwrap(); statement.bind((":ac_id", ac_id))?;
} }
let mut data = Vec::<(f64, f64)>::new(); let mut data = Vec::<(f64, f64)>::new();
let mut x = 0.0; let mut x = 0.0;
while let Ok(State::Row) = statement.next() { while let Ok(State::Row) = statement.next() {
let y = statement.read::<f64, _>("y").unwrap() * -1.0; // Pour passer de dépense dans le négatif à un graphique qui monte let y = statement.read::<f64, _>("y")? * -1.0; // Pour passer de dépense dans le négatif à un graphique qui monte
data.push((x, y)); data.push((x, y));
x += 1.0; x += 1.0;
} }
return data; return Ok(data);
} }
pub fn get_cumulative_account_sum(con: &Connection, ac_id: i64) -> Vec<(f64, f64)> { pub fn get_cumulative_account_sum(
con: &Connection,
ac_id: i64,
) -> Result<Vec<(f64, f64)>, Box<dyn Error>> {
let mut query = " let mut query = "
SELECT SELECT
SUM(amount) OVER ( SUM(amount) OVER (
@@ -375,18 +313,18 @@ FROM
query.push_str(" WHERE account_id = :ac_id"); query.push_str(" WHERE account_id = :ac_id");
} }
let mut statement = con.prepare(query).unwrap(); let mut statement = con.prepare(query)?;
if ac_id != 0 { if ac_id != 0 {
statement.bind((":ac_id", ac_id)).unwrap(); statement.bind((":ac_id", ac_id))?;
} }
let mut data = Vec::<(f64, f64)>::new(); let mut data = Vec::<(f64, f64)>::new();
let mut x = 0.0; let mut x = 0.0;
while let Ok(State::Row) = statement.next() { while let Ok(State::Row) = statement.next() {
let y = statement.read::<f64, _>("y").unwrap(); let y = statement.read::<f64, _>("y")?;
data.push((x, y)); data.push((x, y));
x += 1.0; x += 1.0;
} }
return data; return Ok(data);
} }

View File

@@ -1,4 +1,4 @@
use std::convert::TryFrom; use std::{convert::TryFrom, error::Error};
use sqlite::Connection; use sqlite::Connection;
@@ -53,7 +53,7 @@ impl Account {
pub fn set_type(&mut self, ac_type: AccountType) { pub fn set_type(&mut self, ac_type: AccountType) {
self.ac_type = ac_type; self.ac_type = ac_type;
} }
pub fn get_total(&self, con: &Connection) -> f64 { pub fn get_total(&self, con: &Connection) -> Result<f64, Box<dyn Error>> {
return data_layer::get_account_total(self.id, con); return data_layer::get_account_total(self.id, con);
} }
} }

View File

@@ -33,7 +33,10 @@ impl Transaction {
date: date, date: date,
description: desc, description: desc,
type_id: type_id, type_id: type_id,
tr_type: data_layer::get_transaction_type(con, type_id), tr_type: data_layer::get_transaction_type(con, type_id).unwrap_or(TransactionType {
id: 0,
description: String::new(),
}),
asset_amount: asset_amount, asset_amount: asset_amount,
} }
} }
@@ -91,7 +94,10 @@ impl Transaction {
} }
pub fn set_type(&mut self, tr_id: i64, con: &Connection) { pub fn set_type(&mut self, tr_id: i64, con: &Connection) {
self.type_id = tr_id; self.type_id = tr_id;
self.tr_type = data_layer::get_transaction_type(con, tr_id); self.tr_type = data_layer::get_transaction_type(con, tr_id).unwrap_or(TransactionType {
id: 0,
description: String::new(),
});
} }
pub fn get_asset_amnt(&self) -> f64 { pub fn get_asset_amnt(&self) -> f64 {

View File

@@ -1,3 +1,5 @@
use std::error::Error;
use ratatui::widgets::TableState; use ratatui::widgets::TableState;
use sqlite::Connection; use sqlite::Connection;
@@ -19,15 +21,19 @@ impl TrxTable {
}; };
} }
pub fn get_trx(&mut self, ac_id: i64, con: &Connection) -> Vec<Transaction> { pub fn get_trx(
&mut self,
ac_id: i64,
con: &Connection,
) -> Result<Vec<Transaction>, Box<dyn Error>> {
if ac_id == -1 { if ac_id == -1 {
return Vec::new(); return Ok(Vec::new());
} }
if self.trx.iter().count() == 0 || self.ac_id != ac_id { if self.trx.iter().count() == 0 || self.ac_id != ac_id {
self.ac_id = ac_id; self.ac_id = ac_id;
self.trx = data_layer::get_account_transactions(con, ac_id); self.trx = data_layer::get_account_transactions(con, ac_id)?;
} }
return self.trx.clone(); return Ok(self.trx.clone());
} }
pub fn selected_tr(&self) -> Transaction { pub fn selected_tr(&self) -> Transaction {

View File

@@ -5,7 +5,7 @@ mod enums;
mod ui; mod ui;
use crate::{app::App, enums::*, ui::ui}; use crate::{app::App, enums::*, ui::ui};
use chrono::Local; use chrono::Local;
use entities::{Account, AccountType, Transaction}; use entities::{Account, Transaction};
use ratatui::{ use ratatui::{
Terminal, Terminal,
crossterm::{ crossterm::{

View File

@@ -63,6 +63,7 @@ pub fn ui(frame: &mut Frame, app: &mut App) {
"Total: {}", "Total: {}",
app.get_list_accounts()[i] app.get_list_accounts()[i]
.get_total(&app.connection) .get_total(&app.connection)
.unwrap_or_default()
.to_string() .to_string()
) )
} else { } else {
@@ -116,14 +117,14 @@ pub fn ui(frame: &mut Frame, app: &mut App) {
let min_x = slice let min_x = slice
.into_iter() .into_iter()
.map(|&(x, _)| x) .map(|&(x, _)| x)
.min_by(|a, b| a.partial_cmp(b).unwrap()) .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less))
.unwrap() .unwrap_or_default()
.round(); .round();
let max_x = slice let max_x = slice
.into_iter() .into_iter()
.map(|&(x, _)| x) .map(|&(x, _)| x)
.max_by(|a, b| a.partial_cmp(b).unwrap()) .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater))
.unwrap() .unwrap_or_default()
.round() .round()
+ 1.0; + 1.0;
let x_axis = Axis::default() let x_axis = Axis::default()
@@ -134,15 +135,15 @@ pub fn ui(frame: &mut Frame, app: &mut App) {
let min_y = slice let min_y = slice
.into_iter() .into_iter()
.map(|&(_, y)| y) .map(|&(_, y)| y)
.min_by(|a, b| a.partial_cmp(b).unwrap()) .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less))
.unwrap() .unwrap_or_default()
.round() .round()
- 1.0; - 1.0;
let max_y = slice let max_y = slice
.into_iter() .into_iter()
.map(|&(_, y)| y) .map(|&(_, y)| y)
.max_by(|a, b| a.partial_cmp(b).unwrap()) .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater))
.unwrap() .unwrap_or_default()
.round() .round()
+ 1.0; + 1.0;
let y_axis = Axis::default() let y_axis = Axis::default()
@@ -190,14 +191,14 @@ pub fn ui(frame: &mut Frame, app: &mut App) {
let min_x = slice let min_x = slice
.into_iter() .into_iter()
.map(|&(x, _)| x) .map(|&(x, _)| x)
.min_by(|a, b| a.partial_cmp(b).unwrap()) .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less))
.unwrap() .unwrap_or_default()
.round(); .round();
let max_x = slice let max_x = slice
.into_iter() .into_iter()
.map(|&(x, _)| x) .map(|&(x, _)| x)
.max_by(|a, b| a.partial_cmp(b).unwrap()) .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater))
.unwrap() .unwrap_or_default()
.round() .round()
+ 1.0; + 1.0;
let x_axis = Axis::default() let x_axis = Axis::default()
@@ -208,15 +209,15 @@ pub fn ui(frame: &mut Frame, app: &mut App) {
let min_y = slice let min_y = slice
.into_iter() .into_iter()
.map(|&(_, y)| y) .map(|&(_, y)| y)
.min_by(|a, b| a.partial_cmp(b).unwrap()) .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Less))
.unwrap() .unwrap_or_default()
.round() .round()
- 1.0; - 1.0;
let max_y = slice let max_y = slice
.into_iter() .into_iter()
.map(|&(_, y)| y) .map(|&(_, y)| y)
.max_by(|a, b| a.partial_cmp(b).unwrap()) .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Greater))
.unwrap() .unwrap_or_default()
.round() .round()
+ 1.0; + 1.0;
let y_axis = Axis::default() let y_axis = Axis::default()