1use crate::cache::FileEntry;
2use crate::fs_impl::Drive;
3use crate::mount::{mount, umount};
4use cli_rs::cli_error::{CliError, CliResult};
5use lb_rs::model::core_config::Config;
6use lb_rs::model::errors::Unexpected;
7use lb_rs::service::events::{Actor, Event};
8use lb_rs::{Lb, Uuid};
9use nfs3_server::tcp::{NFSTcp, NFSTcpListener};
10use std::io;
11use std::io::IsTerminal;
12use std::process::exit;
13use std::sync::Arc;
14use std::time::Duration;
15use tokio::time;
16use tracing::{debug, error, info};
17
18pub mod cache;
19pub(crate) mod file_handle;
20pub mod fs_impl;
21pub mod logger;
22pub mod mount;
23pub mod utils;
24
25impl Drive {
26 pub async fn init() -> Self {
27 let lb = Lb::init(Config {
28 writeable_path: Config::writeable_path("drive"),
29 background_work: false,
30 logs: false,
31 stdout_logs: false,
32 colored_logs: false,
33 })
34 .await
35 .unwrap();
36
37 logger::init();
38
39 let root = lb.root().await.map(|file| file.id).unwrap_or(Uuid::nil());
40
41 let data = Arc::default();
42
43 let fs = Self { lb, root, data };
44 fs.clone().monitor_lb().await;
45
46 fs
47 }
48
49 pub async fn import() -> CliResult<()> {
50 let drive = Self::init().await;
51
52 if io::stdin().is_terminal() {
53 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()));
54 }
55
56 let mut account_string = String::new();
57 io::stdin()
58 .read_line(&mut account_string)
59 .expect("failed to read from stdin");
60 account_string.retain(|c| !c.is_whitespace());
61
62 println!("importing account...");
63 drive
64 .lb
65 .import_account(&account_string, None)
66 .await
67 .unwrap();
68
69 drive.lb.sync().await.unwrap();
70
71 Ok(())
72 }
73
74 pub async fn mount() -> CliResult<()> {
75 let drive = Self::init().await;
76 drive.lb.sync().await.unwrap();
77 drive.fill_cache().await;
78 info!("registering sig handler");
79
80 tokio::spawn(async move {
82 tokio::signal::ctrl_c().await.unwrap();
83 let mut unmount_success = umount().await;
84 while !unmount_success {
85 error!("unmount failed, please close any apps using lb-fs! Retrying in 1s.");
86 time::sleep(Duration::from_secs(1)).await;
87 unmount_success = umount().await;
88 }
89 info!("cleaned up, goodbye!");
90 exit(0);
91 });
92
93 let syncer = drive.clone();
95 tokio::spawn(async move {
96 loop {
97 info!("will sync in 30 seconds");
98 tokio::time::sleep(Duration::from_secs(30)).await;
99 info!("syncing");
100 syncer.lb.sync().await.map_unexpected().log_and_ignore();
101 }
102 });
103
104 let event_handler = drive.clone();
106 tokio::spawn(async move {
107 let mut events = event_handler.lb.subscribe();
108 loop {
109 let event = events.recv().await.unwrap();
110
111 match event {
123 Event::MetadataChanged(Actor::Sync) => event_handler.fill_cache().await,
124 Event::DocumentWritten(dirty_id, Actor::Sync) => {
125 let file = event_handler.lb.get_file_by_id(dirty_id).await.unwrap();
126 let size = if file.is_document() {
127 event_handler
128 .lb
129 .read_document(dirty_id, false)
130 .await
131 .unwrap()
132 .len()
133 } else {
134 0
135 };
136
137 let mut entry = FileEntry::from_file(file, size as u64);
138
139 let now = FileEntry::now();
140
141 entry.fattr.mtime = now;
142 entry.fattr.ctime = now;
143
144 event_handler
145 .data
146 .lock()
147 .await
148 .insert(entry.file.id.into(), entry);
149 }
150 _ => {}
151 }
152 }
153 });
154
155 info!("creating server");
157 let listener = NFSTcpListener::bind("127.0.0.1:11111", drive)
158 .await
159 .unwrap();
160
161 info!("mounting");
162 mount();
163
164 info!("ready");
165 listener.handle_forever().await.unwrap();
166 Ok(())
167 }
168
169 async fn monitor_lb(self) {
170 tokio::spawn(async move {
171 let mut sub = self.lb.subscribe();
172 loop {
173 if let Event::Sync(sync_increment) = sub.recv().await.unwrap() {
174 debug!("syncing: {sync_increment:?}")
175 }
176 }
177 });
178 }
179}