Skip to content

Commit 9709ddb

Browse files
committed
pr: switched to ron for state persistance
1 parent 599dd13 commit 9709ddb

File tree

1 file changed

+58
-44
lines changed

1 file changed

+58
-44
lines changed

anyrun/src/main.rs

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::{
22
cell::RefCell,
33
env, fs,
4-
io::{self, BufRead, BufReader, Read, Write},
4+
io::{self, Write},
55
mem,
66
path::PathBuf,
77
rc::Rc,
@@ -46,14 +46,14 @@ struct Config {
4646
close_on_click: bool,
4747
#[serde(default)]
4848
show_results_immediately: bool,
49-
#[serde(default)]
50-
max_entries: Option<usize>,
5149
#[serde(default = "Config::default_layer")]
5250
layer: Layer,
5351
#[serde(default)]
5452
persist_state: bool,
5553
#[serde(default)]
5654
state_ttl_secs: Option<u64>,
55+
#[serde(default)]
56+
max_entries: Option<usize>,
5757
}
5858

5959
impl Config {
@@ -186,6 +186,12 @@ struct RuntimeData {
186186
state_dir: Option<String>,
187187
}
188188

189+
#[derive(Deserialize, Serialize)]
190+
struct PersistentState {
191+
timestamp: u64,
192+
text: String,
193+
}
194+
189195
impl RuntimeData {
190196
fn new(config_dir_path: Option<String>, cli_config: ConfigArgs) -> Self {
191197
// Setup config directory
@@ -252,53 +258,50 @@ impl RuntimeData {
252258

253259
fn state_file(&self) -> String {
254260
let state_dir = self.state_dir.as_ref().expect("state operations called when persistence is disabled");
255-
PathBuf::from(state_dir).join("state.txt").to_str().unwrap().to_string()
261+
PathBuf::from(state_dir).join("state.ron").to_str().unwrap().to_string()
256262
}
257263

258264
fn save_state(&self, text: &str) -> io::Result<()> {
259265
if !self.config.persist_state {
260266
return Ok(());
261267
}
262-
let timestamp = SystemTime::now()
263-
.duration_since(UNIX_EPOCH)
264-
.unwrap()
265-
.as_millis();
266-
267-
let mut file = fs::File::create(self.state_file())?;
268-
writeln!(file, "{}", timestamp)?;
269-
write!(file, "{}", text)
268+
269+
let state = PersistentState {
270+
timestamp: SystemTime::now()
271+
.duration_since(UNIX_EPOCH)
272+
.unwrap()
273+
.as_secs(),
274+
text: text.to_string(),
275+
};
276+
277+
fs::write(self.state_file(), ron::ser::to_string_pretty(&state, ron::ser::PrettyConfig::default())
278+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?)
270279
}
271280

272-
fn load_state(&self) -> io::Result<String> {
281+
fn load_state(&self) -> io::Result<Option<String>> {
273282
if !self.config.persist_state {
274-
return Ok(String::new());
283+
return Ok(None);
275284
}
276-
match fs::File::open(self.state_file()) {
277-
Ok(file) => {
278-
let mut reader = BufReader::new(file);
279-
280-
// Read timestamp from first line
281-
let mut timestamp_str = String::new();
282-
reader.read_line(&mut timestamp_str)?;
283-
let timestamp = timestamp_str.trim().parse::<u128>().unwrap_or(0);
284-
285+
286+
match fs::read_to_string(self.state_file()) {
287+
Ok(content) => {
288+
let state: PersistentState = ron::from_str(&content)
289+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
290+
285291
// Check if state has expired
286292
if let Some(expiry_secs) = self.config.state_ttl_secs {
287293
let now = SystemTime::now()
288294
.duration_since(UNIX_EPOCH)
289295
.unwrap()
290-
.as_millis();
291-
if now - timestamp > u128::from(expiry_secs) * 1000 {
292-
return Ok(String::new());
296+
.as_secs();
297+
if now - state.timestamp > u64::from(expiry_secs) {
298+
return Ok(None);
293299
}
294300
}
295-
296-
// Read text from second line to end
297-
let mut text = String::new();
298-
reader.read_to_string(&mut text)?;
299-
Ok(text)
301+
302+
Ok(Some(state.text))
300303
}
301-
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(String::new()),
304+
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
302305
Err(e) => Err(e),
303306
}
304307
}
@@ -307,7 +310,12 @@ impl RuntimeData {
307310
if !self.config.persist_state {
308311
return Ok(());
309312
}
310-
fs::write(self.state_file(), "0\n")
313+
314+
match fs::remove_file(self.state_file()) {
315+
Ok(()) => Ok(()),
316+
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()), // File doesn't exist = already cleared
317+
Err(e) => Err(e),
318+
}
311319
}
312320
}
313321

@@ -557,13 +565,6 @@ fn activate(app: &gtk::Application, runtime_data: Rc<RefCell<RuntimeData>>) {
557565
.name(style_names::ENTRY)
558566
.build();
559567

560-
// Set initial text from loaded state
561-
if let Ok(initial_text) = runtime_data.borrow().load_state() {
562-
entry.set_text(&initial_text);
563-
} else {
564-
eprintln!("Failed to load state");
565-
}
566-
567568
// Update last_input, save state and refresh matches when text changes
568569
let runtime_data_clone = runtime_data.clone();
569570
entry.connect_changed(move |entry| {
@@ -743,11 +744,19 @@ fn activate(app: &gtk::Application, runtime_data: Rc<RefCell<RuntimeData>>) {
743744
// Only create the widgets once to avoid issues
744745
let configure_once = Once::new();
745746

746-
// Create widgets here for proper positioning
747+
// Load initial state before configuring
748+
let initial_text = if runtime_data.borrow().config.persist_state {
749+
runtime_data.borrow().load_state().ok().flatten()
750+
} else {
751+
None
752+
};
753+
let initial_text_clone = initial_text.clone();
754+
747755
window.connect_configure_event(move |window, event| {
748756
let runtime_data = runtime_data.clone();
749757
let entry = entry.clone();
750758
let main_list = main_list.clone();
759+
let initial_text_inner = initial_text_clone.clone();
751760

752761
configure_once.call_once(move || {
753762
{
@@ -791,13 +800,18 @@ fn activate(app: &gtk::Application, runtime_data: Rc<RefCell<RuntimeData>>) {
791800
main_vbox.add(&main_list);
792801
main_list.show();
793802
entry.grab_focus(); // Grab the focus so typing is immediately accepted by the entry box
794-
entry.set_position(-1); // -1 moves cursor to end of text in case some text was restored
803+
804+
// Set initial text if we loaded state
805+
if let Some(text) = &initial_text_inner {
806+
entry.set_text(text);
807+
entry.set_position(-1); // -1 moves cursor to end of text
808+
}
795809
}
796810

797-
// Show initial results if state restoration is enabled or immediate results are configured
811+
// Show initial results if state was loaded or immediate results are configured
798812
let should_show_results = {
799813
let data = runtime_data.borrow();
800-
data.config.persist_state || data.config.show_results_immediately
814+
initial_text_inner.is_some() || data.config.show_results_immediately
801815
};
802816

803817
if should_show_results {

0 commit comments

Comments
 (0)