ct_tracker_lib/projects/
persistent.rs

1use super::super::ct_fs;
2use super::errors;
3use super::has as proj_exists;
4use super::ProjectFrame;
5use std::{
6    fs::{File, OpenOptions},
7    io::{Read, Write},
8    path::PathBuf,
9};
10
11const PERSISTENT_FILE_NAME: &str = "./stored.ctstore";
12
13// TODO separate stores -> v0.2.0 """maybe"""
14//      -> One for the running project
15//      -> One for the last used project
16
17fn store_path() -> errors::CtResult<PathBuf> {
18    Ok(ct_fs::default_path::conf_path()?.join(PERSISTENT_FILE_NAME))
19}
20
21fn clear_store_and_get_handle() -> errors::CtResult<File> {
22    OpenOptions::new()
23        .write(true)
24        .truncate(true)
25        .create(true)
26        .open(store_path()?)
27        .map_err(|e| e.into())
28}
29
30pub(crate) fn clear_store() -> errors::CtResult<()> {
31    clear_store_and_get_handle()?;
32    Ok(())
33}
34
35/// Is there a project running currently?
36/// Meaning, is it stored as a reference in the persistent store?
37/// If one exists, the project name is returned
38/// This already checks, if the project stored in the persistent store exists
39pub fn has_project() -> errors::CtResult<Option<String>> {
40    let path = store_path()?;
41    let mut file = if !path.exists() {
42        OpenOptions::new()
43            .write(true)
44            .read(true)
45            .create_new(true)
46            .open(path)?;
47        // File has been created, so we don't need to check if something is in it
48        return Ok(None);
49    } else {
50        OpenOptions::new().read(true).open(path)?
51    };
52    let mut contents = String::new();
53    file.read_to_string(&mut contents)?;
54
55    Ok(if let Some(line) = contents.lines().next() {
56        // We only care about the first line
57        if line.is_empty() {
58            // File is empty
59            None
60        } else if proj_exists(line)? {
61            Some(line.to_owned())
62        } else {
63            // Just clear the store file, as the project in it doesn't exist.
64            clear_store()?;
65            None
66        }
67    } else {
68        None
69    })
70}
71
72pub(crate) fn register(name: &str) -> errors::CtResult<()> {
73    if let Some(stored) = has_project()? {
74        if name == stored {
75            return Ok(());
76        }
77    }
78
79    let mut file = clear_store_and_get_handle()?;
80    file.write_all(name.as_bytes())?;
81    Ok(())
82}
83
84/// Deregister a project from the persistent store
85/// If you pass Some(name), it will only deregister that project,
86/// otherwise it will deregister any project
87pub(crate) fn deregister(name: Option<&str>) -> errors::CtResult<()> {
88    if let Some(name) = name {
89        if let Some(stored) = has_project()? {
90            if stored != name {
91                return Ok(());
92            }
93        } else {
94            return Ok(());
95        }
96    }
97    clear_store()?;
98    Ok(())
99}
100
101/// Validate the persistent store -> Check if files have been deleted, if project exists.
102/// Check if project stored as running is actually running.
103pub(crate) fn validate() -> errors::CtResult<()> {
104    if let Some(name) = has_project()? {
105        let p = ProjectFrame::load_from_name(name.as_str())?;
106        if !p.is_open() {
107            // The project in the persistent store is not running, so we
108            // clear the store to make it accurate again
109            clear_store()?;
110        }
111    };
112    Ok(())
113}
114
115pub(crate) fn delete_stored_if(name: &str) -> errors::CtResult<bool> {
116    if let Some(proj) = has_project()? {
117        if proj == name {
118            clear_store()?;
119            return Ok(true);
120        }
121    }
122    Ok(false)
123}