lb_fs/
lib.rs

1use crate::fs_impl::Drive;
2use crate::mount::{mount, umount};
3use cli_rs::cli_error::{CliError, CliResult};
4use lb_rs::model::core_config::Config;
5use lb_rs::service::sync::SyncProgress;
6use lb_rs::{Lb, Uuid};
7use nfs3_server::tcp::{NFSTcp, NFSTcpListener};
8use std::io;
9use std::io::IsTerminal;
10use std::process::exit;
11use std::sync::Arc;
12use std::time::Duration;
13use tokio::time;
14use tracing::{error, info};
15
16pub mod cache;
17pub(crate) mod file_handle;
18pub mod fs_impl;
19pub mod logger;
20pub mod mount;
21pub mod utils;
22
23impl Drive {
24    pub async fn init() -> Self {
25        let lb = Lb::init(Config {
26            writeable_path: Config::writeable_path("drive"),
27            background_work: false,
28            logs: false,
29            stdout_logs: false,
30            colored_logs: false,
31        })
32        .await
33        .unwrap();
34
35        logger::init();
36
37        let root = lb.root().await.map(|file| file.id).unwrap_or(Uuid::nil());
38
39        let data = Arc::default();
40
41        Self { lb, root, data }
42    }
43
44    pub async fn import() -> CliResult<()> {
45        let drive = Self::init().await;
46
47        if io::stdin().is_terminal() {
48            return Err(CliError::from("to import an existing lockbook account, pipe your account string into this command, e.g.:\npbpaste | lb-fs import".to_string()));
49        }
50
51        let mut account_string = String::new();
52        io::stdin()
53            .read_line(&mut account_string)
54            .expect("failed to read from stdin");
55        account_string.retain(|c| !c.is_whitespace());
56
57        println!("importing account...");
58        drive
59            .lb
60            .import_account(&account_string, None)
61            .await
62            .unwrap();
63
64        drive.lb.sync(Self::progress()).await.unwrap();
65
66        Ok(())
67    }
68
69    pub async fn mount() -> CliResult<()> {
70        let drive = Self::init().await;
71        drive.prepare_caches().await;
72        info!("registering sig handler");
73
74        // capture ctrl_c and try to cleanup
75        tokio::spawn(async move {
76            tokio::signal::ctrl_c().await.unwrap();
77            let mut unmount_success = umount().await;
78            while !unmount_success {
79                error!("unmount failed, please close any apps using lb-fs! Retrying in 1s.");
80                time::sleep(Duration::from_secs(1)).await;
81                unmount_success = umount().await;
82            }
83            info!("cleaned up, goodbye!");
84            exit(0);
85        });
86
87        // sync periodically in the background
88        let syncer = drive.clone();
89        tokio::spawn(async move {
90            loop {
91                info!("will sync in 30 seconds");
92                tokio::time::sleep(Duration::from_secs(30)).await;
93                info!("syncing");
94                syncer.sync().await;
95            }
96        });
97
98        // todo have a better port selection strategy
99        info!("creating server");
100        let listener = NFSTcpListener::bind("127.0.0.1:11111", drive)
101            .await
102            .unwrap();
103
104        info!("mounting");
105        mount();
106
107        info!("ready");
108        listener.handle_forever().await.unwrap();
109        Ok(())
110    }
111
112    pub fn progress() -> Option<Box<dyn Fn(SyncProgress) + Send>> {
113        Some(Box::new(|status| println!("{status}")))
114    }
115}