refactor: restructure canvas file folder structure (#1368)

* refactor: group together some canvas files

* rename file

* more shuffling around

* fmt

* more shuffling

Going to flatten widgets in another PR.

* some docs

* naming

* fmt
This commit is contained in:
Clement Tsang 2024-01-01 10:19:52 +00:00 committed by GitHub
parent 74792f0ddf
commit ecb6faa089
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 197 additions and 157 deletions

View file

@ -7,6 +7,7 @@ tab_spaces = 4
max_width = 100
# Unstable options, disabled by default.
# imports_granularity = "Crate"
# group_imports = "StdExternalCrate"
# wrap_comments = true
# format_code_in_doc_comments = true

View file

@ -15,12 +15,12 @@ use crate::{
constants,
data_collection::temperature,
data_conversion::ConvertedData,
utils::error::{BottomError, Result},
Pid,
};
use crate::{
utils::data_units::DataUnit,
utils::{
data_units::DataUnit,
error::{BottomError, Result},
},
widgets::{ProcWidgetColumn, ProcWidgetMode},
Pid,
};
pub mod data_farmer;

View file

@ -13,7 +13,6 @@
//! memory usage and higher CPU usage - you will be trying to process more and
//! more points as this is used!
use crate::data_collection::processes::ProcessHarvest;
use std::{collections::BTreeMap, time::Instant, vec::Vec};
use hashbrown::HashMap;
@ -21,9 +20,8 @@ use hashbrown::HashMap;
#[cfg(feature = "battery")]
use crate::data_collection::batteries;
use crate::{
data_collection::{cpu, disks, memory, network, temperature, Data},
utils::data_prefixes::*,
utils::gen_util::get_decimal_bytes,
data_collection::{cpu, disks, memory, network, processes::ProcessHarvest, temperature, Data},
utils::{data_prefixes::*, gen_util::get_decimal_bytes},
Pid,
};
@ -370,9 +368,10 @@ impl DataCollection {
let io_device = {
#[cfg(target_os = "macos")]
{
use regex::Regex;
use std::sync::OnceLock;
use regex::Regex;
// Must trim one level further for macOS!
static DISK_REGEX: OnceLock<Regex> = OnceLock::new();
if let Some(new_name) = DISK_REGEX

View file

@ -1,7 +1,9 @@
use std::collections::BTreeMap;
use crate::constants::DEFAULT_WIDGET_ID;
use crate::error::{BottomError, Result};
use crate::{
constants::DEFAULT_WIDGET_ID,
error::{BottomError, Result},
};
/// Represents a more usable representation of the layout, derived from the
/// config.

View file

@ -1,16 +1,23 @@
use std::fmt::{Debug, Formatter};
use std::time::Duration;
use std::{borrow::Cow, collections::VecDeque};
use std::{
borrow::Cow,
collections::VecDeque,
fmt::{Debug, Formatter},
time::Duration,
};
use humantime::parse_duration;
use regex::Regex;
use crate::data_collection::processes::ProcessHarvest;
use crate::multi_eq_ignore_ascii_case;
use crate::utils::data_prefixes::*;
use crate::utils::error::{
use crate::{
data_collection::processes::ProcessHarvest,
multi_eq_ignore_ascii_case,
utils::{
data_prefixes::*,
error::{
BottomError::{self, QueryError},
Result,
},
},
};
const DELIMITER_LIST: [char; 6] = ['=', '>', '<', '(', ')', '\"'];

View file

@ -1,5 +1,4 @@
use clap::builder::PossibleValuesParser;
use clap::*;
use clap::{builder::PossibleValuesParser, *};
const TEMPLATE: &str = "\
{name} {version}

View file

@ -13,16 +13,12 @@ use std::{
};
use anyhow::{Context, Result};
use crossterm::{
event::{EnableBracketedPaste, EnableMouseCapture},
execute,
terminal::{enable_raw_mode, EnterAlternateScreen},
};
use tui::{backend::CrosstermBackend, Terminal};
use bottom::{
args,
canvas::{self, canvas_styling::CanvasStyling},
canvas::{
styling::CanvasStyling,
{self},
},
check_if_terminal, cleanup_terminal, create_collection_thread, create_input_thread,
create_or_get_config,
data_conversion::*,
@ -30,6 +26,12 @@ use bottom::{
options::*,
panic_hook, read_config, try_drawing, update_data, BottomEvent,
};
use crossterm::{
event::{EnableBracketedPaste, EnableMouseCapture},
execute,
terminal::{enable_raw_mode, EnterAlternateScreen},
};
use tui::{backend::CrosstermBackend, Terminal};
// Used for heap allocation debugging purposes.
// #[global_allocator]
@ -151,9 +153,10 @@ fn main() -> Result<()> {
let _stderr_fd = {
// A really ugly band-aid to suppress stderr warnings on FreeBSD due to sysinfo.
// For more information, see https://github.com/ClementTsang/bottom/issues/798.
use filedescriptor::{FileDescriptor, StdioDescriptor};
use std::fs::OpenOptions;
use filedescriptor::{FileDescriptor, StdioDescriptor};
let path = OpenOptions::new().write(true).open("/dev/null")?;
FileDescriptor::redirect_stdio(&path, StdioDescriptor::Stderr)?
};

View file

@ -1,7 +1,7 @@
use std::str::FromStr;
use canvas_styling::*;
use itertools::izip;
use styling::*;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
@ -16,13 +16,13 @@ use crate::{
App,
},
constants::*,
utils::error,
utils::error::BottomError,
utils::{error, error::BottomError},
};
pub mod canvas_styling;
mod dialogs;
mod drawing_utils;
pub mod styling;
pub mod tui_widgets;
mod widgets;
#[derive(Debug)]

View file

@ -3,8 +3,7 @@ use std::cmp::{max, min};
use tui::{
layout::{Alignment, Rect},
terminal::Frame,
text::Line,
text::Span,
text::{Line, Span},
widgets::{Block, Borders, Paragraph, Wrap},
};
use unicode_width::UnicodeWidthStr;

View file

@ -235,9 +235,10 @@ impl CanvasStyling {
#[cfg(test)]
mod test {
use tui::style::{Color, Style};
use super::{CanvasStyling, ColourScheme};
use crate::Config;
use tui::style::{Color, Style};
#[test]
fn default_selected_colour_works() {

View file

@ -0,0 +1,6 @@
//! Cstom ratatui widgets used by the rest of bottom.
pub mod data_table;
pub mod pipe_gauge;
pub mod time_chart;
pub mod time_graph;

View file

@ -1,6 +1,6 @@
use tui::style::Style;
use crate::canvas::canvas_styling::CanvasStyling;
use crate::canvas::styling::CanvasStyling;
#[derive(Default)]
pub struct DataTableStyling {

View file

@ -11,7 +11,7 @@ use tui::{
};
use unicode_segmentation::UnicodeSegmentation;
use super::tui_widget::time_chart::{Axis, Dataset, Point, TimeChart, DEFAULT_LEGEND_CONSTRAINTS};
use super::time_chart::{Axis, Dataset, Point, TimeChart, DEFAULT_LEGEND_CONSTRAINTS};
/// Represents the data required by the [`TimeGraph`].
pub struct GraphData<'a> {
@ -183,7 +183,7 @@ mod test {
};
use super::TimeGraph;
use crate::components::tui_widget::time_chart::Axis;
use crate::canvas::tui_widgets::time_chart::Axis;
const Y_LABELS: [Cow<'static, str>; 3] = [
Cow::Borrowed("0%"),

View file

@ -1,8 +1,7 @@
use tui::{
layout::{Alignment, Constraint, Direction, Layout, Rect},
terminal::Frame,
text::Line,
text::Span,
text::{Line, Span},
widgets::{Block, Paragraph},
};

View file

@ -8,8 +8,10 @@ use tui::{
use crate::{
app::App,
canvas::Painter,
components::tui_widget::pipe_gauge::{LabelLimit, PipeGauge},
canvas::{
tui_widgets::pipe_gauge::{LabelLimit, PipeGauge},
Painter,
},
constants::*,
data_collection::cpu::CpuDataType,
data_conversion::CpuWidgetData,

View file

@ -8,11 +8,14 @@ use tui::{
use crate::{
app::{layout_manager::WidgetDirection, App},
canvas::{drawing_utils::should_hide_x_label, Painter},
components::{
canvas::{
drawing_utils::should_hide_x_label,
tui_widgets::{
data_table::{DrawInfo, SelectionState},
time_graph::{GraphData, TimeGraph},
},
Painter,
},
data_conversion::CpuWidgetData,
widgets::CpuWidgetState,
};

View file

@ -2,8 +2,10 @@ use tui::{layout::Rect, terminal::Frame};
use crate::{
app::{self},
canvas::Painter,
components::data_table::{DrawInfo, SelectionState},
canvas::{
tui_widgets::data_table::{DrawInfo, SelectionState},
Painter,
},
};
impl Painter {

View file

@ -5,7 +5,9 @@ use tui::{
};
use crate::{
app::App, canvas::Painter, components::tui_widget::pipe_gauge::PipeGauge, constants::*,
app::App,
canvas::{tui_widgets::pipe_gauge::PipeGauge, Painter},
constants::*,
};
impl Painter {

View file

@ -8,8 +8,11 @@ use tui::{
use crate::{
app::App,
canvas::{drawing_utils::should_hide_x_label, Painter},
components::time_graph::{GraphData, TimeGraph},
canvas::{
drawing_utils::should_hide_x_label,
tui_widgets::time_graph::{GraphData, TimeGraph},
Painter,
},
};
impl Painter {

View file

@ -8,10 +8,13 @@ use tui::{
use crate::{
app::{App, AxisScaling},
canvas::{drawing_utils::should_hide_x_label, Painter},
components::{
canvas::{
drawing_utils::should_hide_x_label,
tui_widgets::{
time_chart::Point,
time_graph::{GraphData, TimeGraph},
tui_widget::time_chart::Point,
},
Painter,
},
utils::{data_prefixes::*, data_units::DataUnit, gen_util::partial_ordering},
};

View file

@ -9,8 +9,10 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::{
app::{App, AppSearchState},
canvas::Painter,
components::data_table::{DrawInfo, SelectionState},
canvas::{
tui_widgets::data_table::{DrawInfo, SelectionState},
Painter,
},
constants::*,
};

View file

@ -2,8 +2,10 @@ use tui::{layout::Rect, terminal::Frame};
use crate::{
app,
canvas::Painter,
components::data_table::{DrawInfo, SelectionState},
canvas::{
tui_widgets::data_table::{DrawInfo, SelectionState},
Painter,
},
};
impl Painter {

View file

@ -1,3 +0,0 @@
pub mod data_table;
pub mod time_graph;
pub mod tui_widget;

View file

@ -1,2 +0,0 @@
pub mod pipe_gauge;
pub mod time_chart;

View file

@ -2,15 +2,14 @@
use std::io;
use hashbrown::HashMap;
use serde::Deserialize;
use super::{keep_disk_entry, DiskHarvest, IoHarvest};
use crate::{
data_collection::deserialize_xo, data_collection::disks::IoData,
data_collection::DataCollector, utils::error,
data_collection::{deserialize_xo, disks::IoData, DataCollector},
utils::error,
};
use hashbrown::HashMap;
#[derive(Deserialize, Debug, Default)]
#[serde(rename_all = "kebab-case")]

View file

@ -2,9 +2,8 @@
use sysinfo::{DiskExt, SystemExt};
use crate::data_collection::DataCollector;
use super::{keep_disk_entry, DiskHarvest};
use crate::data_collection::DataCollector;
pub(crate) fn get_disk_usage(collector: &DataCollector) -> anyhow::Result<Vec<DiskHarvest>> {
let disks = collector.sys.disks();

View file

@ -166,9 +166,10 @@ impl FromStr for FileSystem {
#[cfg(test)]
mod test {
use super::FileSystem;
use std::str::FromStr;
use super::FileSystem;
#[test]
fn file_system_from_str() {
// Something supported

View file

@ -5,11 +5,12 @@
//!
//! Ideally, we can remove this if sysinfo ever gains disk I/O capabilities.
use core_foundation::base::{mach_port_t, CFAllocatorRef};
use core_foundation::dictionary::CFMutableDictionaryRef;
use core_foundation::{
base::{mach_port_t, CFAllocatorRef},
dictionary::CFMutableDictionaryRef,
};
use libc::c_char;
use mach2::kern_return::kern_return_t;
use mach2::port::MACH_PORT_NULL;
use mach2::{kern_return::kern_return_t, port::MACH_PORT_NULL};
#[allow(non_camel_case_types)]
pub type io_object_t = mach_port_t;

View file

@ -4,13 +4,15 @@
use std::mem;
use anyhow::{anyhow, bail};
use core_foundation::base::{kCFAllocatorDefault, CFType, TCFType, ToVoid};
use core_foundation::dictionary::{
use core_foundation::{
base::{kCFAllocatorDefault, CFType, TCFType, ToVoid},
dictionary::{
CFDictionary, CFDictionaryGetTypeID, CFDictionaryRef, CFMutableDictionary,
CFMutableDictionaryRef,
},
number::{CFNumber, CFNumberGetTypeID},
string::{CFString, CFStringGetTypeID},
};
use core_foundation::number::{CFNumber, CFNumberGetTypeID};
use core_foundation::string::{CFString, CFStringGetTypeID};
use mach2::kern_return;
use super::bindings::*;

View file

@ -4,7 +4,6 @@ use itertools::Itertools;
use sysinfo::{DiskExt, SystemExt};
use super::{keep_disk_entry, DiskHarvest};
use crate::data_collection::{disks::IoCounters, DataCollector};
mod bindings;

View file

@ -1,15 +1,17 @@
use std::sync::OnceLock;
use hashbrown::HashMap;
use nvml_wrapper::enum_wrappers::device::TemperatureSensor;
use nvml_wrapper::enums::device::UsedGpuMemory;
use nvml_wrapper::{error::NvmlError, Nvml};
use nvml_wrapper::{
enum_wrappers::device::TemperatureSensor, enums::device::UsedGpuMemory, error::NvmlError, Nvml,
};
use crate::app::filter::Filter;
use crate::app::layout_manager::UsedWidgets;
use crate::data_collection::memory::MemHarvest;
use crate::data_collection::temperature::{is_temp_filtered, TempHarvest, TemperatureType};
use crate::{
app::{filter::Filter, layout_manager::UsedWidgets},
data_collection::{
memory::MemHarvest,
temperature::{is_temp_filtered, TempHarvest, TemperatureType},
},
};
pub static NVML_DATA: OnceLock<Result<Nvml, NvmlError>> = OnceLock::new();

View file

@ -3,11 +3,11 @@
//! For Linux, this is handled by a custom set of functions.
//! For Windows, macOS, FreeBSD, Android, and Linux, this is handled by sysinfo.
use cfg_if::cfg_if;
use std::{borrow::Cow, time::Duration};
use super::DataCollector;
use cfg_if::cfg_if;
use super::DataCollector;
use crate::{utils::error, Pid};
cfg_if! {

View file

@ -1,13 +1,14 @@
//! Process data collection for FreeBSD. Uses sysinfo.
use std::io;
use std::process::Command;
use std::{io, process::Command};
use hashbrown::HashMap;
use serde::{Deserialize, Deserializer};
use crate::data_collection::{deserialize_xo, processes::UnixProcessExt};
use crate::Pid;
use crate::{
data_collection::{deserialize_xo, processes::UnixProcessExt},
Pid,
};
#[derive(Deserialize, Debug, Default)]
#[serde(rename_all = "kebab-case")]

View file

@ -1,19 +1,22 @@
//! Process data collection for Linux.
mod process;
use process::*;
use std::fs::{self, File};
use std::io::{BufRead, BufReader};
use std::time::Duration;
use std::{
fs::{self, File},
io::{BufRead, BufReader},
time::Duration,
};
use hashbrown::HashSet;
use process::*;
use sysinfo::ProcessStatus;
use super::{ProcessHarvest, UserTable};
use crate::data_collection::DataCollector;
use crate::utils::error::{self, BottomError};
use crate::Pid;
use crate::{
data_collection::DataCollector,
utils::error::{self, BottomError},
Pid,
};
/// Maximum character length of a /proc/<PID>/stat process name.
/// If it's equal or greater, then we instead refer to the command for the name.

View file

@ -1,14 +1,12 @@
//! Process data collection for macOS. Uses sysinfo and custom bindings.
use std::io;
use std::process::Command;
use std::{io, process::Command};
use hashbrown::HashMap;
use itertools::Itertools;
use sysinfo::{PidExt, ProcessExt};
use super::UnixProcessExt;
use crate::Pid;
mod sysctl_bindings;

View file

@ -1,7 +1,6 @@
//! Shared process data harvesting code from macOS and FreeBSD via sysinfo.
use std::io;
use std::time::Duration;
use std::{io, time::Duration};
use hashbrown::HashMap;
use sysinfo::{CpuExt, PidExt, ProcessExt, ProcessStatus, System, SystemExt};

View file

@ -5,7 +5,6 @@ use std::time::Duration;
use sysinfo::{CpuExt, PidExt, ProcessExt, SystemExt, UserExt};
use super::ProcessHarvest;
use crate::data_collection::DataCollector;
pub fn sysinfo_process_data(

View file

@ -3,13 +3,13 @@
use kstring::KString;
use crate::app::{data_farmer::DataCollection, AxisScaling};
use crate::components::tui_widget::time_chart::Point;
use crate::data_collection::{cpu::CpuDataType, memory::MemHarvest, temperature::TemperatureType};
use crate::utils::data_prefixes::*;
use crate::utils::data_units::DataUnit;
use crate::utils::gen_util::*;
use crate::widgets::{DiskWidgetData, TempWidgetData};
use crate::{
app::{data_farmer::DataCollection, AxisScaling},
canvas::tui_widgets::time_chart::Point,
data_collection::{cpu::CpuDataType, memory::MemHarvest, temperature::TemperatureType},
utils::{data_prefixes::*, data_units::DataUnit, gen_util::*},
widgets::{DiskWidgetData, TempWidgetData},
};
#[derive(Debug, Default)]
pub enum BatteryDuration {

View file

@ -16,10 +16,9 @@ use std::{
io::{stderr, stdout, Write},
panic::PanicInfo,
path::PathBuf,
sync::Mutex,
sync::{
mpsc::{Receiver, Sender},
Arc, Condvar,
Arc, Condvar, Mutex,
},
thread::{self, JoinHandle},
time::{Duration, Instant},
@ -54,7 +53,6 @@ pub mod utils {
}
pub mod args;
pub mod canvas;
pub mod components;
pub mod constants;
pub mod data_collection;
pub mod data_conversion;

View file

@ -11,13 +11,12 @@ use indexmap::IndexSet;
use layout_options::*;
use regex::Regex;
use serde::{Deserialize, Serialize};
#[cfg(feature = "battery")]
use starship_battery::Manager;
use crate::{
app::{filter::Filter, layout_manager::*, *},
canvas::{canvas_styling::CanvasStyling, ColourScheme},
canvas::{styling::CanvasStyling, ColourScheme},
constants::*,
data_collection::temperature::TemperatureType,
utils::{
@ -33,9 +32,8 @@ mod process_columns;
pub use process_columns::ProcessConfig;
mod cpu;
pub use cpu::{CpuConfig, CpuDefault};
use anyhow::{Context, Result};
pub use cpu::{CpuConfig, CpuDefault};
#[derive(Clone, Debug, Default, Deserialize)]
pub struct Config {
@ -918,7 +916,7 @@ mod test {
use super::{get_color_scheme, get_time_interval, get_widget_layout, Config};
use crate::{
app::App,
canvas::canvas_styling::CanvasStyling,
canvas::styling::CanvasStyling,
options::{
get_default_time_value, get_retention, get_update_rate, try_parse_ms, ConfigFlags,
},

View file

@ -1,7 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::app::layout_manager::*;
use crate::error::Result;
use crate::{app::layout_manager::*, error::Result};
/// Represents a row. This has a length of some sort (optional) and a vector
/// of children.

View file

@ -10,9 +10,8 @@ pub struct ProcessConfig {
#[cfg(test)]
mod test {
use crate::widgets::ProcWidgetColumn;
use super::ProcessConfig;
use crate::widgets::ProcWidgetColumn;
#[test]
fn empty_column_setting() {

View file

@ -5,11 +5,14 @@ use tui::{style::Style, text::Text, widgets::Row};
use crate::{
app::AppConfigFields,
canvas::{canvas_styling::CanvasStyling, Painter},
components::data_table::{
canvas::{
styling::CanvasStyling,
tui_widgets::data_table::{
Column, ColumnHeader, DataTable, DataTableColumn, DataTableProps, DataTableStyling,
DataToCell,
},
Painter,
},
data_collection::cpu::CpuDataType,
data_conversion::CpuWidgetData,
options::CpuDefault,

View file

@ -5,10 +5,12 @@ use tui::text::Text;
use crate::{
app::AppConfigFields,
canvas::canvas_styling::CanvasStyling,
components::data_table::{
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell, SortColumn,
SortDataTable, SortDataTableProps, SortOrder, SortsRow,
canvas::{
styling::CanvasStyling,
tui_widgets::data_table::{
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell,
SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
},
},
utils::gen_util::{get_decimal_bytes, sort_partial_fn, truncate_to_text},
};

View file

@ -11,11 +11,13 @@ use crate::{
query::*,
AppConfigFields, AppSearchState,
},
canvas::canvas_styling::CanvasStyling,
components::data_table::{
canvas::{
styling::CanvasStyling,
tui_widgets::data_table::{
Column, ColumnHeader, ColumnWidthBounds, DataTable, DataTableColumn, DataTableProps,
DataTableStyling, SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
},
},
data_collection::processes::ProcessHarvest,
Pid,
};

View file

@ -4,7 +4,7 @@ use serde::{de::Error, Deserialize, Serialize};
use super::ProcWidgetData;
use crate::{
components::data_table::{ColumnHeader, SortsRow},
canvas::tui_widgets::data_table::{ColumnHeader, SortsRow},
utils::gen_util::sort_partial_fn,
};

View file

@ -9,8 +9,10 @@ use tui::{text::Text, widgets::Row};
use super::proc_widget_column::ProcColumn;
use crate::{
canvas::Painter,
components::data_table::{DataTableColumn, DataToCell},
canvas::{
tui_widgets::data_table::{DataTableColumn, DataToCell},
Painter,
},
data_collection::processes::ProcessHarvest,
data_conversion::{binary_byte_string, dec_bytes_per_second_string, dec_bytes_string},
utils::gen_util::truncate_to_text,

View file

@ -3,7 +3,7 @@ use std::borrow::Cow;
use tui::text::Text;
use crate::{
components::data_table::{ColumnHeader, DataTableColumn, DataToCell},
canvas::tui_widgets::data_table::{ColumnHeader, DataTableColumn, DataToCell},
utils::gen_util::truncate_to_text,
};

View file

@ -6,10 +6,12 @@ use tui::text::Text;
use crate::{
app::AppConfigFields,
canvas::canvas_styling::CanvasStyling,
components::data_table::{
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell, SortColumn,
SortDataTable, SortDataTableProps, SortOrder, SortsRow,
canvas::{
styling::CanvasStyling,
tui_widgets::data_table::{
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell,
SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
},
},
data_collection::temperature::TemperatureType,
utils::gen_util::{sort_partial_fn, truncate_to_text},

View file

@ -1,11 +1,13 @@
//! Mocks layout management, so we can check if we broke anything.
use bottom::app::layout_manager::{BottomLayout, BottomWidgetType};
#[cfg(feature = "battery")]
use bottom::constants::DEFAULT_BATTERY_LAYOUT;
use bottom::constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID};
use bottom::options::{layout_options::Row, Config};
use bottom::utils::error;
use bottom::{
app::layout_manager::{BottomLayout, BottomWidgetType},
constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID},
options::{layout_options::Row, Config},
utils::error,
};
use toml_edit::de::from_str;
// TODO: Could move these into the library files rather than external tbh.