Something went wrong. Try again.
My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193use std::{ env::current_dir, fs::{File, create_dir_all}, io::Write,};
use color_eyre::eyre::{Context, Result, eyre};use fil_core::Kasten;use tower_lsp::{LspService, Server};
use crate::{ cli::{Commands, TodoSubcommand, TuiArgs, ZettelSubcommand}, client::Client, config::{Config, get_config_dir}, elm::Elmui, lsp::Backend, server::FilServer,};
impl Commands { pub async fn process(self, port: Option<u16>) -> Result<()> { match self { Self::Init { name } => { let dir = current_dir() .context("Failed to get current directory")? .join(&name);
Kasten::initialize(dir.clone()).await?;
// Write config that sets the filaments directory to current Dir! let config_str = dbg! {Config::generate(&dir)}?;
// create the config Dir let config_dir = get_config_dir();
create_dir_all(config_dir).expect("creating the config dir should not error");
let mut config_file = File::create(get_config_dir().join("config.ron")) .context("Failed to create config file")?;
write!(config_file, "{config_str}")?;
println!("wrote config to {config_file:#?}");
println!("Initialized successfully!"); }
// the daemon builds the archive, so this works from any client // and never opens the vault alongside a running daemon Self::Export { path } => { let conf = config(port)?; let fil = Client::new(&conf.daemon_url);
let path = path.unwrap_or_else(|| Kasten::default_export_name().into()); let files = fil .export(&path) .await .with_context(|| daemon_hint(&conf)) .with_context(|| format!("Failed to export to {}", path.display()))?;
println!("Exported {files} files to {}", path.display()); }
Self::Import { archive, dir, keep_config, } => { let kt = Kasten::import(&archive, &dir) .await .with_context(|| format!("Failed to import {}", archive.display()))?;
println!("Imported {} into {}", archive.display(), kt.root.display());
if !keep_config { let config = Config::install(&kt.root)?; println!("wrote config to {}", config.display()); } }
// these go through the daemon so its state, and every client // following it, sees the change Self::Zettel(command) => zettel(command, port).await?, Self::Todo(command) => todo(command, port).await?,
Self::Lsp => { // everything comes from the daemon let conf = config(port).with_context(|| "Failed to parse the config!!")?; let fil = Client::new(&conf.daemon_url);
// get the in and out let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout();
// start the LSP let (service, socket) = LspService::new(|client| Backend::new(client, fil)); Server::new(stdin, stdout, socket).serve(service).await; }
Self::Serve => { let server = FilServer::new(config(port)?).await?; server.run().await?; } }
Ok(()) }}
/// Runs the TUI, which talks to `fil serve` over HTTP: what `fil` does when/// given no command.pub async fn run_tui(TuiArgs { tick_rate }: TuiArgs, port: Option<u16>) -> Result<()> { let conf = config(port)?; Elmui::new(tick_rate, &conf.daemon_url, conf.keymap)? .run() .await}
/// The config, with `--port` applied.fn config(port: Option<u16>) -> Result<Config> { Config::parse()?.with_port(port)}
/// Context for a failed daemon request, which also covers `fil serve` not/// running at all.fn daemon_hint(conf: &Config) -> String { format!("Request to `fil serve` at {} failed", conf.daemon_url)}
async fn zettel(command: ZettelSubcommand, port: Option<u16>) -> Result<()> { let conf = config(port)?; let fil = Client::new(&conf.daemon_url);
match command { ZettelSubcommand::New { title } => { let zettel = fil .create_zettel(title) .await .with_context(|| daemon_hint(&conf))?; println!("created zettel {} ({})", zettel.title, zettel.id); } ZettelSubcommand::List { by_tag } => { let mut zettels = fil .fetch_zettels() .await .with_context(|| daemon_hint(&conf))?; // zettels name their tags by id let tag_id = match by_tag { Some(name) => { let tags = fil.fetch_tags().await.with_context(|| daemon_hint(&conf))?; let Some(tag) = tags.into_iter().find(|tag| tag.name == name) else { return Err(eyre!("no tag named {name}")); }; Some(tag.id) } None => None, }; zettels.sort_by_key(|z| std::cmp::Reverse(z.modified_at)); for zettel in zettels .iter() .filter(|z| tag_id.as_ref().is_none_or(|id| z.tag_ids.contains(id))) { println!("{} {}", zettel.id, zettel.title); } } } Ok(())}
async fn todo(command: TodoSubcommand, port: Option<u16>) -> Result<()> { let conf = config(port)?; let fil = Client::new(&conf.daemon_url);
match command { TodoSubcommand::Group { name, parent_id } => { let group = fil .create_group(name, parent_id) .await .with_context(|| daemon_hint(&conf))?; println!("created group {} ({})", group.name, group.id); } TodoSubcommand::Task { name, parent_id } => { //TODO: need to expose the due date and priority through the CLI let task = fil .create_task(name, parent_id) .await .with_context(|| daemon_hint(&conf))?; println!("created task {} ({})", task.name, task.id); } } Ok(())}