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::{ClientType, 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 client_type: ClientType::Cli,
34 })
35 .await
36 .unwrap();
37
38 logger::init();
39
40 let root = lb.root().await.map(|file| file.id).unwrap_or(Uuid::nil());
41
42 let data = Arc::default();
43
44 let fs = Self { lb, root, data };
45 fs.clone().monitor_lb().await;
46
47 fs
48 }
49
50 pub async fn import() -> CliResult<()> {
51 let drive = Self::init().await;
52
53 if io::stdin().is_terminal() {
54 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()));
55 }
56
57 let mut account_string = String::new();
58 io::stdin()
59 .read_line(&mut account_string)
60 .expect("failed to read from stdin");
61 account_string.retain(|c| !c.is_whitespace());
62
63 println!("importing account...");
64 drive
65 .lb
66 .import_account(&account_string, None)
67 .await
68 .unwrap();
69
70 drive.lb.sync().await.unwrap();
71
72 Ok(())
73 }
74
75 pub async fn mount() -> CliResult<()> {
76 let drive = Self::init().await;
77 drive.lb.sync().await.unwrap();
78 drive.fill_cache().await;
79 info!("registering sig handler");
80
81 tokio::spawn(async move {
83 tokio::signal::ctrl_c().await.unwrap();
84 let mut unmount_success = umount().await;
85 while !unmount_success {
86 error!("unmount failed, please close any apps using lb-fs! Retrying in 1s.");
87 time::sleep(Duration::from_secs(1)).await;
88 unmount_success = umount().await;
89 }
90 info!("cleaned up, goodbye!");
91 exit(0);
92 });
93
94 let syncer = drive.clone();
96 tokio::spawn(async move {
97 loop {
98 info!("will sync in 30 seconds");
99 tokio::time::sleep(Duration::from_secs(30)).await;
100 info!("syncing");
101 syncer.lb.sync().await.map_unexpected().log_and_ignore();
102 }
103 });
104
105 let event_handler = drive.clone();
107 tokio::spawn(async move {
108 let mut events = event_handler.lb.subscribe();
109 loop {
110 let event = events.recv().await.unwrap();
111
112 match event {
124 Event::MetadataChanged(Actor::Sync) => event_handler.fill_cache().await,
125 Event::DocumentWritten(dirty_id, Actor::Sync) => {
126 let file = event_handler.lb.get_file_by_id(dirty_id).await.unwrap();
127 let size = if file.is_document() {
128 event_handler
129 .lb
130 .read_document(dirty_id, false)
131 .await
132 .unwrap()
133 .len()
134 } else {
135 0
136 };
137
138 let mut entry = FileEntry::from_file(file, size as u64);
139
140 let now = FileEntry::now();
141
142 entry.fattr.mtime = now;
143 entry.fattr.ctime = now;
144
145 event_handler
146 .data
147 .lock()
148 .await
149 .insert(entry.file.id.into(), entry);
150 }
151 _ => {}
152 }
153 }
154 });
155
156 info!("creating server");
158 let listener = NFSTcpListener::bind("127.0.0.1:11111", drive)
159 .await
160 .unwrap();
161
162 info!("mounting");
163 mount();
164
165 info!("ready");
166 listener.handle_forever().await.unwrap();
167 Ok(())
168 }
169
170 async fn monitor_lb(self) {
171 tokio::spawn(async move {
172 let mut sub = self.lb.subscribe();
173 loop {
174 if let Event::Sync(sync_increment) = sub.recv().await.unwrap() {
175 debug!("syncing: {sync_increment:?}")
176 }
177 }
178 });
179 }
180}