about summary refs log tree commit diff stats
path: root/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/ts/tskm/src/interface/open/handle.rs')
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/handle.rs232
1 files changed, 232 insertions, 0 deletions
diff --git a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
new file mode 100644
index 00000000..4d7341b2
--- /dev/null
+++ b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
@@ -0,0 +1,232 @@
+use std::{
+    fs,
+    net::{IpAddr, Ipv4Addr},
+    path::PathBuf,
+    process,
+};
+
+use anyhow::{bail, Context, Result};
+use log::{error, info, warn};
+use url::Url;
+
+use crate::{cli::OpenCommand, rofi, state::State, task};
+
+pub fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
+    match command {
+        OpenCommand::Review => {
+            for project in task::Project::all().context("Failed to get all project files")? {
+                if project.is_touched() {
+                    info!("Reviewing project: '{}'", project.to_project_display());
+                    open_in_browser(project, state, None).with_context(|| {
+                        format!(
+                            "Failed to open project ('{}') in Firefox",
+                            project.to_project_display()
+                        )
+                    })?;
+                    project.untouch().with_context(|| {
+                        format!(
+                            "Failed to untouch project ('{}')",
+                            project.to_project_display()
+                        )
+                    })?;
+                }
+            }
+        }
+        OpenCommand::Project { project, url } => {
+            project.touch().context("Failed to touch project")?;
+            open_in_browser(&project, state, url).with_context(|| {
+                format!("Failed to open project: {}", project.to_project_display())
+            })?;
+        }
+        OpenCommand::Select { url } => {
+            let selected_project: task::Project = task::Project::from_project_string(
+                &rofi::select(
+                    task::Project::all()
+                        .context("Failed to get all registered projects")?
+                        .iter()
+                        .map(task::Project::to_project_display)
+                        .collect::<Vec<_>>()
+                        .as_slice(),
+                )
+                .context("Failed to get selected project")?,
+            )
+            .expect("This should work, as we send only projects in");
+
+            selected_project
+                .touch()
+                .context("Failed to touch project")?;
+
+            open_in_browser(&selected_project, state, url).context("Failed to open project")?;
+        }
+        OpenCommand::ListTabs { project } => {
+            let project = if let Some(p) = project {
+                p
+            } else if let Some(p) =
+                task::Project::get_current().context("Failed to get currently focused project")?
+            {
+                p
+            } else {
+                bail!("You need to either supply a project or have a project active!");
+            };
+
+            let session_store = project.get_sessionstore().with_context(|| {
+                format!(
+                    "Failed to get session store for project: '{}'",
+                    project.to_project_display()
+                )
+            })?;
+
+            let selected = session_store
+                .windows
+                .iter()
+                .map(|w| w.selected)
+                .collect::<Vec<_>>();
+
+            let tabs = session_store
+                .windows
+                .iter()
+                .flat_map(|window| window.tabs.iter())
+                .map(|tab| tab.entries.get(tab.index - 1).expect("This should be Some"))
+                .collect::<Vec<_>>();
+
+            for (index, entry) in tabs.iter().enumerate() {
+                let index = index + 1;
+                let is_selected = {
+                    if selected.contains(&index) {
+                        "🔻 "
+                    } else {
+                        "   "
+                    }
+                };
+                println!("{}{}", is_selected, entry.url);
+            }
+        }
+    }
+    Ok(())
+}
+
+fn open_in_browser(
+    selected_project: &task::Project,
+    state: &mut State,
+    url: Option<Url>,
+) -> Result<()> {
+    let old_project: Option<task::Project> =
+        task::Project::get_current().context("Failed to get currently active project")?;
+    let old_task: Option<task::Task> =
+        task::Task::get_current(state).context("Failed to get currently active task")?;
+
+    selected_project.activate().with_context(|| {
+        format!(
+            "Failed to active project: '{}'",
+            selected_project.to_project_display()
+        )
+    })?;
+
+    let tracking_task = {
+        let all_tasks = selected_project.get_tasks(state).with_context(|| {
+            format!(
+                "Failed to get assoctiated tasks for project: '{}'",
+                selected_project.to_project_display()
+            )
+        })?;
+
+        let tracking_task = all_tasks.into_iter().find(|t| {
+            let maybe_desc = t.description(state);
+            if let Ok(desc) = maybe_desc {
+                desc == "tracking"
+            } else {
+                error!(
+                    "Getting task description returned error: {}",
+                    maybe_desc.expect_err("We already check for Ok")
+                );
+                false
+            }
+        });
+
+        if let Some(task) = tracking_task {
+            info!(
+                "Starting task {} -> tracking",
+                selected_project.to_project_display()
+            );
+            task.start(state)
+                .with_context(|| format!("Failed to start task {task}"))?;
+        }
+        tracking_task
+    };
+
+    let status = {
+        let mut args = vec!["-P".to_owned(), selected_project.to_project_display()];
+        if let Some(url) = url {
+            args.push(url.to_string());
+        } else {
+            let lock_file = dirs::home_dir()
+                .expect("Exists")
+                .join(".mozilla/firefox")
+                .join(selected_project.to_project_display())
+                .join("lock");
+
+            if lock_file.exists() {
+                let (ip, pid): (IpAddr, u32) = {
+                    let link = fs::read_link(&lock_file).with_context(|| {
+                        format!("Failed to readlink lock at '{}'", lock_file.display())
+                    })?;
+
+                    let (ip, pid) = link
+                        .to_str()
+                        .expect("Should work")
+                        .split_once(':')
+                        .expect("The split works");
+
+                    (
+                        ip.parse().expect("Should be a valid ip address"),
+                        pid.parse().expect("Should be a valid pid"),
+                    )
+                };
+
+                if ip != Ipv4Addr::new(127, 0, 0, 2) {
+                    warn!("Your ip is weird..");
+                }
+
+                if PathBuf::from("/proc").join(pid.to_string()).exists() {
+                    // Another Firefox instance has already been started for this project
+                    // Add a buffer URL to force Firefox to open it in the already open instance
+                    args.push("about:newtab".to_owned());
+                } else {
+                    // This project does not yet have another Firefox instance
+                    // We do not need to add anything to the arguments, Firefox will open a new
+                    // instance.
+                }
+            } else {
+                // There is no lock file and thus no instance already open.
+            }
+        };
+
+        process::Command::new("firefox")
+            .args(args)
+            .status()
+            .context("Failed to start firefox")?
+    };
+
+    if !status.success() {
+        error!("Firefox run exited with error.");
+    }
+
+    if let Some(task) = tracking_task {
+        task.stop(state)
+            .with_context(|| format!("Failed to stop task {task}"))?;
+    }
+    if let Some(task) = old_task {
+        task.start(state)
+            .with_context(|| format!("Failed to start task {task}"))?;
+    }
+
+    if let Some(project) = old_project {
+        project.activate().with_context(|| {
+            format!("Failed to active project {}", project.to_project_display())
+        })?;
+    } else {
+        task::Project::clear().context("Failed to clear currently focused project")?;
+    }
+
+    Ok(())
+}