工作日倒数 v0.1.0
说明
学了应该快2个月的Rust,只看不动手,那肯定是不行的。所以写个小东西来实践一下。
同时发现坑也是真的多,Rust是真的难,代码还没有完成,也还有很多错误(又不是不能运行),还在持续学习。没有注释,有空再来说一下每个函数的作用。
还有雾化提醒没有加(开玩笑
成品放打赏区。可能会报毒,但放心使用!
上代码!
#![windows_subsystem = "windows"]
use chrono::{Datelike, Local, TimeZone};
use iced::theme::Theme;
use iced::widget::{column, container, pick_list, scrollable, text, vertical_space};
use iced::window::Position;
use iced::{Alignment, Application, Command, Element, Length, Settings};
use serde::{Deserialize, Serialize};
use std::fs;
const SETTINGS_FILE: &str = "settings.json";
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Eq)]
pub enum WeekName {
SmallWeek,
BigWeek,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Eq)]
pub enum WorkingMode {
Weekends,
SingleDay,
AutoSwitch(WeekName),
}
impl WorkingMode {
const ALL: [WorkingMode; 4] = [
WorkingMode::Weekends,
WorkingMode::SingleDay,
WorkingMode::AutoSwitch(WeekName::BigWeek),
WorkingMode::AutoSwitch(WeekName::SmallWeek),
];
}
impl std::fmt::Display for WorkingMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
WorkingMode::Weekends => "双休",
WorkingMode::SingleDay => "单休",
WorkingMode::AutoSwitch(week_name) => {
if *week_name == WeekName::BigWeek {
"大小周(双休)"
} else {
"大小周(单休)"
}
}
}
)
}
}
impl Default for WorkingMode {
fn default() -> Self {
WorkingMode::Weekends
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
struct SettingsData {
first_time: bool,
mode: WorkingMode,
current_week: WorkingMode,
last_update_date: chrono::DateTime<Local>,
}
struct Holiday {
holiday_name: String,
result_day: i32,
need_day: u32,
working_week: String,
mode_picklist_state: Option<WorkingMode>,
settings: SettingsData,
}
#[derive(Debug, Clone)]
enum Message {
ModeSelected(WorkingMode),
}
impl Default for SettingsData {
fn default() -> Self {
let today = Local::now();
SettingsData {
mode: WorkingMode::default(),
current_week: WorkingMode::default(),
first_time: true,
last_update_date: today,
}
}
}
impl SettingsData {
fn load() -> Self {
match fs::read_to_string(SETTINGS_FILE) {
Ok(contents) => {
if let Ok(data) = serde_json::from_str(&contents) {
data
} else {
SettingsData::default()
}
}
Err(_) => SettingsData::default(),
}
}
fn save(&self) {
if let Ok(data) = serde_json::to_string(self) {
if let Err(err) = fs::write(SETTINGS_FILE, data) {
eprintln!("保存失败: {}", err);
}
} else {
eprintln!("序列化失败");
}
}
}
impl Holiday {
fn is_monday(&self) -> bool {
let today = Local::now();
today.weekday() == chrono::Weekday::Mon
}
fn is_same_week(
&self,
date1: &chrono::DateTime<Local>,
date2: &chrono::DateTime<Local>,
) -> bool {
let duration = date1.signed_duration_since(*date2);
duration.num_weeks() == 0
}
fn update_settings(&mut self, mode: WorkingMode) {
let current_date = Local::now();
let last_update_date = self.settings.last_update_date;
self.settings.current_week = mode;
self.settings.first_time = false;
if !self.is_monday() && self.is_same_week(¤t_date, &last_update_date) {
self.settings.mode = mode;
self.settings.save();
} else {
self.settings.mode = match mode {
WorkingMode::AutoSwitch(WeekName::BigWeek) => {
self.settings.current_week = WorkingMode::AutoSwitch(WeekName::SmallWeek);
WorkingMode::AutoSwitch(WeekName::SmallWeek)
}
WorkingMode::AutoSwitch(WeekName::SmallWeek) => {
self.settings.current_week = WorkingMode::AutoSwitch(WeekName::BigWeek);
WorkingMode::AutoSwitch(WeekName::BigWeek)
}
_ => mode,
};
self.mode_picklist_state = Some(self.settings.mode);
self.settings.last_update_date = Local::now();
self.update_values();
// self.settings.first_time = false;
self.settings.save();
}
}
fn update_values(&mut self) {
let weekday = Local::now().weekday();
self.working_week = match self.settings.mode {
WorkingMode::Weekends => "双休".to_string(),
WorkingMode::SingleDay => "单休".to_string(),
WorkingMode::AutoSwitch(WeekName::BigWeek) => "大小周(双休)".to_string(),
WorkingMode::AutoSwitch(WeekName::SmallWeek) => "大小周(单休)".to_string(),
};
self.need_day = match self.working_week.as_str() {
"双休" | "大小周(双休)" => {
if weekday == chrono::Weekday::Mon {
5 - weekday.number_from_monday()
} else {
6 - weekday.number_from_monday()
}
}
"单休" | "大小周(单休)" => {
if weekday == chrono::Weekday::Mon {
6 - weekday.number_from_monday()
} else {
7 - weekday.number_from_monday()
}
}
_ => panic!("工作模式错误"),
};
let fes = [
("元旦", 2023, 1, 1),
("春节", 2023, 1, 22),
("清明节", 2023, 4, 5),
("劳动节", 2023, 5, 1),
("端午节", 2023, 6, 22),
("中秋节", 2023, 9, 29),
("国庆节", 2023, 10, 1),
];
let mut name = Vec::new();
let mut result = Vec::new();
for (_i, day) in fes.iter().enumerate() {
let x: (&str, i32, u32, u32) = *day;
let holiday_name = x.0;
let year = x.1;
let month = x.2;
let day = x.3;
let festival_date = Local.with_ymd_and_hms(year, month, day, 0, 0, 0).unwrap();
let today = Local::now();
let day_until = festival_date.signed_duration_since(today).num_days() as i32;
if day_until > 0 {
result.push(day_until);
name.push(holiday_name.to_string());
}
}
let smallest_day = *result.iter().min().unwrap();
let smallest_day_index = result.iter().position(|&x| x == smallest_day).unwrap();
self.holiday_name = name[smallest_day_index].clone();
self.result_day = smallest_day;
}
}
impl Application for Holiday {
type Message = Message;
type Executor = iced::executor::Default;
type Flags = ();
type Theme = Theme;
fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
let settings = SettingsData::load();
let mode_picklist_state = Some(settings.mode);
let mut holiday = Self {
holiday_name: String::new(),
result_day: 0,
mode_picklist_state,
settings,
need_day: 0,
working_week: String::new(),
};
holiday.update_values();
let command = Command::perform(async {}, move |_| Message::ModeSelected(settings.mode));
(holiday, command)
}
fn title(&self) -> String {
String::from("Holiday")
}
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
match message {
Message::ModeSelected(mode) => {
self.update_settings(mode);
self.settings.save();
self.mode_picklist_state = Some(self.settings.mode);
self.update_values();
Command::none()
}
}
}
fn view(&self) -> Element<Message> {
let pick_list = pick_list(
&WorkingMode::ALL[..],
self.mode_picklist_state,
Message::ModeSelected,
)
.placeholder("选择模式")
.text_size(16);
let content = if self.need_day == 0 {
format!(
"{}\n周末啦!\n距离{}还有{}天!",
self.working_week, self.holiday_name, self.result_day
)
} else {
format!(
"{}\n还需【{}】天就到周末啦!\n距离【{}】还有【{}】天!",
self.working_week, self.need_day, self.holiday_name, self.result_day
)
};
let content = column![
vertical_space(0),
"",
pick_list,
vertical_space(20),
text(content)
]
.align_items(Alignment::Center)
.spacing(10);
container(scrollable(content))
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.into()
}
}
pub fn main() -> iced::Result {
SettingsData::load();
Holiday::run(Settings {
// 窗口属性
window: iced::window::Settings {
size: (270, 200),
resizable: false,
decorations: true,
transparent: false,
always_on_top: false,
position: Position::Centered,
..iced::window::Settings::default()
},
// 字体路径
default_font: Some(include_bytes!(
r#"..\HarmonyOS_Sans_SC_Regular.ttf"#
)),
..Settings::default()
})
}
这个iced有个坑,那就是中文会乱码,所以得设置一个字体,打包的时候会把字体也包进去(猜测。
一个字体8MB,总共应用才14MB。
上效果!
就是这样,非常简陋,但是有靓点(应该算)!
就是这个大小周,他是会自动切换的,运行首次设置一下本周是大周还是小周,如果这周设置为[大小周(单休)]
,那下周就会自动切换为[大小周(双休)]
。单休、双休
,顾名思义,就是一直单休
或者一直双休
。
问题
现在的问题是,没有实现自动获取节假日,只在代码里写死了今年有假放的节日
因为我还不会别的,只会一捏捏基础。打算今年给他完善一下(flag
因为明年就不是那个时间点的节日了😂
好家伙,鱼排技术学院啊
好家伙,鱼排技术学院啊