Compare commits
No commits in common. 'master' and 'arg-parsing-better' have entirely different histories.
master
...
arg-parsin
@ -1,124 +0,0 @@
|
||||
use super::*;
|
||||
use treemap::{
|
||||
Rect,
|
||||
Mappable,
|
||||
TreemapLayout
|
||||
};
|
||||
use data::{FsInfo, INodeInfoGraph, INodeInfoGraphEntry};
|
||||
|
||||
/// A treemap of all **files** in the graph.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Treemap
|
||||
{
|
||||
//layout: TreemapLayout,
|
||||
nodes: Vec<MapNode>,
|
||||
}
|
||||
|
||||
impl Treemap
|
||||
{
|
||||
/// All nodes of the map.
|
||||
#[inline] pub fn nodes(&self) -> &[MapNode]
|
||||
{
|
||||
&self.nodes[..]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MapNode
|
||||
{
|
||||
name: String,
|
||||
|
||||
vw_size: f64, // Should be halved each iteration
|
||||
vw_bounds: Rect, // should be Rect::new() before aligntment
|
||||
}
|
||||
|
||||
impl MapNode
|
||||
{
|
||||
/// The calculated bounds of the node
|
||||
#[inline] pub fn bounds(&self) -> &Rect
|
||||
{
|
||||
&self.vw_bounds
|
||||
}
|
||||
/// The name of the node
|
||||
#[inline] pub fn name(&self) -> &str
|
||||
{
|
||||
&self.name[..]
|
||||
}
|
||||
|
||||
#[inline] fn size(&self) -> f64 //Is this useful for consumer?
|
||||
{
|
||||
self.vw_size
|
||||
}
|
||||
|
||||
#[inline] fn new(name: String) -> Self
|
||||
{
|
||||
Self {
|
||||
vw_size: 1.0,
|
||||
vw_bounds: Rect::new(),
|
||||
name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a treemap from this graph.
|
||||
pub fn treemap(_cfg: &Config, graph: &INodeInfoGraph, (w, h): (f64, f64)) -> Treemap
|
||||
{
|
||||
let layout = TreemapLayout::new();
|
||||
let mut nodes = Vec::with_capacity(graph.len());
|
||||
//TODO: Recursively walk the graph, halving size with each iteration. (Maybe we need `INodeInfoGraph` here, not `Hierarchicalinodegraph`?)
|
||||
let total_size = graph.total_size();
|
||||
|
||||
let size = 1.0;
|
||||
|
||||
fn calc_path<'a, I: IntoIterator<Item = INodeInfoGraphEntry<'a>>>(insert: &'a mut Vec<MapNode>, from: I, total_size: u64, size: f64, scale: f64)
|
||||
{
|
||||
for top in from {
|
||||
let path = top.path();
|
||||
match top.info() {
|
||||
FsInfo::Directory(_) => {
|
||||
//TODO: Do we add dir itself? I think not?
|
||||
// Add children
|
||||
let size = size * 0.5;
|
||||
calc_path(insert, top.level().unwrap(), total_size, size, scale);
|
||||
},
|
||||
&FsInfo::File(sz, _) => {
|
||||
let fract = (sz as f64) / (total_size as f64);
|
||||
insert.push(MapNode {
|
||||
name: path.to_string_lossy().into_owned(),
|
||||
vw_size: fract * scale,
|
||||
vw_bounds: Rect::new(),
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calc_path(&mut nodes, graph.top_level(), total_size, size, 1.0);
|
||||
|
||||
layout.layout_items(&mut nodes[..], Rect {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
w, h,
|
||||
});
|
||||
|
||||
Treemap {
|
||||
//layout,
|
||||
nodes
|
||||
}
|
||||
}
|
||||
|
||||
impl Mappable for MapNode
|
||||
{
|
||||
fn size(&self) -> f64
|
||||
{
|
||||
self.vw_size
|
||||
}
|
||||
fn bounds(&self) -> &Rect
|
||||
{
|
||||
&self.vw_bounds
|
||||
}
|
||||
fn set_bounds(&mut self, bounds: Rect)
|
||||
{
|
||||
self.vw_bounds = bounds;
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
//! Graph inspection REPL
|
||||
use super::*;
|
||||
use std::{fmt, error};
|
||||
use std::path::PathBuf;
|
||||
use std::io;
|
||||
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::Editor;
|
||||
|
||||
mod env;
|
||||
mod command;
|
||||
mod opcodes;
|
||||
|
||||
/// Default history file name
|
||||
///
|
||||
/// # Path lookup
|
||||
/// * To make this an absolute path, start it with `/`
|
||||
/// * To make this path relative to the user's home directory, start it with `~/` (Note: If we are unable to find the user's home directory, it is considered a lookup **failure** (*not* a **disable**) and `calculate_history_path()` will return `Err`.)
|
||||
/// * Otherwise, the path is taken relative to the current working directory
|
||||
///
|
||||
/// # Notes
|
||||
/// This is only used when the `save-history` feature is enabled.
|
||||
const DEFAULT_HISTORY_FILE: &'static str = "~/.dirstat_history";
|
||||
|
||||
/// Get the path to the history file.
|
||||
///
|
||||
/// # Lookup
|
||||
/// * If the `DIRSTAT_HISTORY` envvar is set and not empty, use this file path.
|
||||
/// * If the `DIRSTAT_HISTORY` envvar is set and empty, saving history is considered **disabled**, we return `Ok(None)`.
|
||||
/// * Otherwise, refer to lookup rules for `DEFAULT_HISTORY_FILE`.
|
||||
pub fn calculate_history_path() -> io::Result<Option<PathBuf>>
|
||||
{
|
||||
cfg_if! {
|
||||
if #[cfg(feature="save-history")] {
|
||||
todo!()
|
||||
} else {
|
||||
unreachable!("Tried to calculate repl history path when binary was compiled with history saving perma-disabled.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inspect the graph with commands
|
||||
///
|
||||
/// # Note
|
||||
/// This function synchronously blocks the current thread.
|
||||
pub fn inspect(cfg: &Config, graph: &HierarchicalINodeGraph) -> Result<(), ReplExitError>
|
||||
{
|
||||
let mut repl = Editor::<()>::new(); //TODO: Change `()` to our completer, when we have a decent idea of how they'll work.
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature="save-history")] {
|
||||
let history_path = match calculate_history_path() {
|
||||
Ok(Some(path)) => {
|
||||
if let Err(err) = repl.load_history(&path)
|
||||
{
|
||||
cfg_eprintln!(cfg, "Failed to load repl history from {:?}: {}", path, err);
|
||||
}
|
||||
Some(path)
|
||||
},
|
||||
Ok(None) => None,
|
||||
Err(err)
|
||||
{
|
||||
cfg_eprintln!(cfg, "Failed to find repl history: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let res: Result<(), ReplExitError> = try {
|
||||
loop {
|
||||
let line = repl.readline("> ")?;
|
||||
repl.add_history_entry(&line);
|
||||
|
||||
//TODO: How to interpret commands?
|
||||
todo!("Interpret commands from `line`.");
|
||||
}
|
||||
};
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature="save-history")] {
|
||||
if let Some(path) = history_path {
|
||||
if let Err(err) = repl.save_history(&path)
|
||||
{
|
||||
cfg_eprintln!(cfg, "Failed to save repl history to {:?}: {}", path, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// When the inspection repl exists abnormally.
|
||||
#[derive(Debug)]
|
||||
pub enum ReplExitError
|
||||
{
|
||||
ReadLine(ReadlineError),
|
||||
}
|
||||
|
||||
impl From<ReadlineError> for ReplExitError
|
||||
{
|
||||
#[inline] fn from(from: ReadlineError) -> Self
|
||||
{
|
||||
Self::ReadLine(from)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for ReplExitError
|
||||
{
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
Some(match &self
|
||||
{
|
||||
Self::ReadLine(rl) => rl
|
||||
})
|
||||
}
|
||||
}
|
||||
impl fmt::Display for ReplExitError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
match self
|
||||
{
|
||||
Self::ReadLine(ReadlineError::Eof) |
|
||||
Self::ReadLine(ReadlineError::Interrupted) => write!(f, "exit"),
|
||||
Self::ReadLine(_) => write!(f, "readline error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,54 +0,0 @@
|
||||
//! Repl commands
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::env::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context<'a>
|
||||
{
|
||||
/// Environment containing variable name mappings.
|
||||
env: &'a mut Lexenv,
|
||||
}
|
||||
|
||||
/// Trait for commands.
|
||||
///
|
||||
/// # Defining commands
|
||||
/// A command object should be created once only, and then referenced and executed using `params` and through mutating `cx`.
|
||||
pub trait Command: fmt::Debug
|
||||
{
|
||||
fn execute(&self, cx: &mut Context<'_>, params: Vec<Value>) -> eyre::Result<()>;
|
||||
}
|
||||
|
||||
/// Command structurally parsed.
|
||||
///
|
||||
/// Can be converted into `Command` with the `TryInto` trait.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct IR
|
||||
{
|
||||
op: String,
|
||||
params: Vec<Value>,
|
||||
}
|
||||
|
||||
impl FromStr for IR
|
||||
{
|
||||
type Err = CommandParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Error when parsing a command into `IR`.
|
||||
#[derive(Debug)]
|
||||
pub struct CommandParseError(String);
|
||||
|
||||
impl error::Error for CommandParseError{}
|
||||
|
||||
impl fmt::Display for CommandParseError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "failed to parse command from {:?}", self.0)
|
||||
}
|
||||
}
|
@ -1,242 +0,0 @@
|
||||
//! Execution environment for repl
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Lexenv
|
||||
{
|
||||
/// Maps symbol name to value in generations.
|
||||
kvstack: BTreeMap<usize, HashMap<String, Value>>,
|
||||
/// Current generation of the satck
|
||||
current_generation: usize,
|
||||
}
|
||||
impl Lexenv
|
||||
{
|
||||
/// The generation number of the current level.
|
||||
pub fn depth(&self) -> usize
|
||||
{
|
||||
self.current_generation
|
||||
}
|
||||
/// Create a new empty lexenv
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self {
|
||||
kvstack: BTreeMap::new(),
|
||||
current_generation: 0,
|
||||
}
|
||||
}
|
||||
/// All valid symbols at this level.
|
||||
///
|
||||
/// # Ordering
|
||||
/// Each symbol's level will appear in the order from level 0 to the current level, however the order of intra-level symbols is undefined.
|
||||
pub fn symbols(&self) -> impl Iterator<Item = &'_ Value> + '_
|
||||
{
|
||||
self.kvstack.range(0..=self.current_generation).flat_map(|(_, v)| v.values())
|
||||
}
|
||||
|
||||
/// All valid symbols **in** this level.
|
||||
pub fn symbols_local(&self) -> impl Iterator<Item = &'_ Value> + '_
|
||||
{
|
||||
OptionIterator::from(self.kvstack.get(&self.current_generation).map(|x| x.values()))
|
||||
}
|
||||
|
||||
/// Remove the current level, but leave its memory allocated for further use.
|
||||
pub fn pop(&mut self)
|
||||
{
|
||||
self.kvstack.entry(self.current_generation).or_insert_with(|| HashMap::new()).clear();
|
||||
if self.current_generation > 0 {
|
||||
self.current_generation-=1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a symbol from the **current** level.
|
||||
pub fn remove(&mut self, name: &str) -> Option<Value>
|
||||
{
|
||||
self.kvstack.entry(self.current_generation).or_insert_with(|| HashMap::new()).remove(name)
|
||||
}
|
||||
|
||||
/// Insert a new value mapping into the current level.
|
||||
pub fn insert(&mut self, name: String, value: Value)
|
||||
{
|
||||
self.kvstack.entry(self.current_generation).or_insert_with(|| HashMap::new()).insert(name, value);
|
||||
}
|
||||
|
||||
/// Look up a symbol in this or any of the above levels.
|
||||
pub fn lookup(&self, name: &str) -> Option<&Value>
|
||||
{
|
||||
for (_, lvmap) in self.kvstack.range(0..=self.current_generation).rev()
|
||||
{
|
||||
let m = lvmap.get(name);
|
||||
if m.is_some() {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Look up a symbol in this level.
|
||||
pub fn lookup_local(&self, name: &str) -> Option<&Value>
|
||||
{
|
||||
self.kvstack.get(&self.current_generation).map(|map| map.get(name)).flatten()
|
||||
}
|
||||
|
||||
/// Create a new, empty level.
|
||||
pub fn push(&mut self)
|
||||
{
|
||||
self.current_generation+=1;
|
||||
}
|
||||
|
||||
/// Remove the current level, deallocating any memory it was using.
|
||||
pub fn pop_clear(&mut self)
|
||||
{
|
||||
if self.current_generation > 0 {
|
||||
self.kvstack.remove(&self.current_generation);
|
||||
self.current_generation -=1;
|
||||
} else {
|
||||
self.kvstack.entry(0).or_insert_with(|| HashMap::new()).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The value type
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Value
|
||||
{
|
||||
String(String),
|
||||
Symbol(String),
|
||||
List(Vec<Value>),
|
||||
}
|
||||
|
||||
impl Value
|
||||
{
|
||||
/// Parse from an iterator of `char`s.
|
||||
pub fn parse_chars<T>(ch: &mut T) -> Result<Self, ValueParseError>
|
||||
where T: Iterator<Item = char>
|
||||
{
|
||||
|
||||
match ch.next()
|
||||
{
|
||||
Some('(') => {
|
||||
todo!("list");
|
||||
},
|
||||
Some('"') => {
|
||||
todo!("string");
|
||||
},
|
||||
Some(first_chr) => {
|
||||
todo!("symbol");
|
||||
},
|
||||
_ => Err(ValueParseError(String::default())),
|
||||
}
|
||||
}
|
||||
/// Parse a `Value` from this string and then return the rest of the string.
|
||||
#[deprecated]
|
||||
pub fn parse_running(s: &str) -> Result<(Self, &'_ str), ValueParseError>
|
||||
{
|
||||
match s.trim().as_bytes()
|
||||
{
|
||||
& [b'(', ..] => {
|
||||
todo!("list");
|
||||
},
|
||||
& [b'"', ..] => {
|
||||
todo!("string");
|
||||
},
|
||||
_ => {
|
||||
todo!("shmbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Value
|
||||
{
|
||||
type Err = ValueParseError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::parse_running(s).map(|(x, _)| x)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value
|
||||
{
|
||||
pub fn try_as_symbol(&self) -> Result<&str, ValueTypeError>
|
||||
{
|
||||
match self {
|
||||
Self::Symbol(s) => Ok(&s[..]),
|
||||
_ => Err(ValueTypeError::Symbol),
|
||||
}
|
||||
}
|
||||
pub fn try_as_string(&self) -> Result<&str, ValueTypeError>
|
||||
{
|
||||
match self {
|
||||
Self::Symbol(s) |
|
||||
Self::String(s) => Ok(&s[..]),
|
||||
_ => Err(ValueTypeError::String),
|
||||
}
|
||||
}
|
||||
pub fn try_as_list(&self) -> Result<&[Value], ValueTypeError>
|
||||
{
|
||||
match self {
|
||||
Self::List(l) => Ok(&l[..]),
|
||||
_ => Err(ValueTypeError::List),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_symbol(&self) -> Option<&str>
|
||||
{
|
||||
match self {
|
||||
Self::Symbol(s) => Some(&s[..]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn as_string(&self) -> Option<&str>
|
||||
{
|
||||
match self {
|
||||
Self::Symbol(s) |
|
||||
Self::String(s) => Some(&s[..]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn as_list(&self) -> Option<&[Value]>
|
||||
{
|
||||
match self {
|
||||
Self::List(l) => Some(&l[..]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error when using `try_as_*` functions on `Value`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
|
||||
pub enum ValueTypeError
|
||||
{
|
||||
Symbol,
|
||||
String,
|
||||
List,
|
||||
}
|
||||
|
||||
/// Error when parsing a `Value` from a stirng.
|
||||
#[derive(Debug)]
|
||||
pub struct ValueParseError(String);
|
||||
|
||||
impl error::Error for ValueParseError{}
|
||||
impl fmt::Display for ValueParseError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "cannot parse {:?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for ValueTypeError{}
|
||||
impl fmt::Display for ValueTypeError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "type error: expected ")?;
|
||||
match self {
|
||||
Self::Symbol => write!(f, "symbol"),
|
||||
Self::String => write!(f, "string"),
|
||||
Self::List => write!(f, "list"),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
//! Defined commands
|
||||
use super::*;
|
||||
|
||||
use env::*;
|
||||
use command::*;
|
||||
|
||||
/// Contains all operations
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Operations
|
||||
{
|
||||
|
||||
}
|
||||
|
Loading…
Reference in new issue