initial userland commit

This commit is contained in:
Able 2022-12-03 12:15:03 -06:00
commit 14dd080dba
49 changed files with 1377 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

262
Cargo.lock generated Normal file
View file

@ -0,0 +1,262 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "basic_driver"
version = "0.1.0"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clparse"
version = "0.1.0"
dependencies = [
"hashbrown 0.13.1",
"log",
"toml 0.5.9 (git+https://git.ablecorp.us/theoddgarlic/toml-rs)",
]
[[package]]
name = "derelict_microarchitecture"
version = "0.1.0"
[[package]]
name = "getrandom"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "ground"
version = "0.1.0"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash 0.7.6",
]
[[package]]
name = "hashbrown"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
dependencies = [
"ahash 0.8.2",
]
[[package]]
name = "libc"
version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "no_video"
version = "0.1.0"
[[package]]
name = "once_cell"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "proc-macro2"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ron"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
dependencies = [
"base64",
"bitflags",
"serde",
]
[[package]]
name = "serde"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tar"
version = "0.1.0"
dependencies = [
"versioning",
]
[[package]]
name = "thiserror"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "git+https://git.ablecorp.us/theoddgarlic/toml-rs#34db433429f3ad38921d13ac9aba74c8a706f376"
dependencies = [
"hashbrown 0.12.3",
"serde",
]
[[package]]
name = "trash_manifest"
version = "0.1.0"
dependencies = [
"ron",
"serde",
"thiserror",
"toml 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "versioning"
version = "0.1.2"
dependencies = [
"serde",
]
[[package]]
name = "vgable"
version = "0.1.0"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

22
Cargo.toml Normal file
View file

@ -0,0 +1,22 @@
[workspace]
members = [
"drivers/basic_driver",
"drivers/graphics/derelict_microarchitecture",
"drivers/graphics/ground",
"drivers/graphics/novideo",
"drivers/graphics/vgable",
"libraries/clparse",
"libraries/tar",
"libraries/trash_manifest",
"libraries/versioning",
# "programs/delete",
# "programs/list",
# "programs/shell",
# "programs/table",
# "programs/table_view",
# "programs/undelete",
]

21
LICENSE.md Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 AbleCorp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

View file

@ -0,0 +1,8 @@
[package]
name = "basic_driver"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,8 @@
#![no_std]
#![no_main]
#[no_mangle]
fn start() -> i32 {
// Simple driver
1
}

View file

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

View file

@ -0,0 +1,8 @@
[package]
name = "derelict_microarchitecture"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,8 @@
#![no_std]
#![no_main]
#[no_mangle]
fn start() -> i32 {
// Simple driver
1
}

View file

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

View file

@ -0,0 +1,8 @@
[package]
name = "ground"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,8 @@
#![no_std]
#![no_main]
#[no_mangle]
fn start() -> i32 {
// Simple driver
1
}

View file

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

View file

@ -0,0 +1,8 @@
[package]
name = "no_video"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,8 @@
#![no_std]
#![no_main]
#[no_mangle]
fn start() -> i32 {
// Simple driver
1
}

View file

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

View file

@ -0,0 +1,8 @@
[package]
name = "vgable"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,8 @@
#![no_std]
#![no_main]
#[no_mangle]
fn start() -> i32 {
// Simple driver
1
}

0
drivers/readme.md Normal file
View file

View file

@ -0,0 +1,25 @@
[package]
name = "clparse"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["std"]
std = []
[dependencies]
log = "*"
hashbrown = "*"
[dependencies.toml]
git = "https://git.ablecorp.us:443/theoddgarlic/toml-rs"
# version = "0.5.8"
default-features = false
# [dependencies.thiserror]
# version = "1.0"
# default-features = false

17
libraries/clparse/SPEC.md Normal file
View file

@ -0,0 +1,17 @@
# CLParse
## Arguments
An argument must be passed in the form of `abc=xyz`
arguments may ***NOT*** have implicit argument anywhere but the final spot and it optionally may have ***only*** one(1)
This is different that you may be used to
For example; the `cat` program of unix-like machines can be called like
`cat abc.c xyz.c`
and the equivelent on ableOS would be
`append src=abc.c dest=xyz.c`
## Config
clparse also loads configs into toml values and passes them along

View file

@ -0,0 +1,3 @@
[list]
size = true
last_modified = true

View file

@ -0,0 +1,16 @@
[kernel]
boot = true
[system]
[system.bootloader]
[shared.list]
size = true
last_modified = true
[user.able.list]
size = false
last_modified = true
[user.able.shell.alias]
ls = "list"

View file

@ -0,0 +1,210 @@
//! # clparse
//! simple command line parser for ableOS
#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use hashbrown;
use hashbrown::HashMap;
use alloc::string::String;
pub use toml;
pub type Argmap = HashMap<String, String>;
#[derive(Debug, Clone)]
pub struct Arguments {
pub arguments: Argmap,
}
impl Arguments {
// TODO: Maybe check if a key is truthy, like if it's value equals "true", "1", "on", etc.
// NOTE: The above is out of scope for this pr but will be considered -able
pub fn parse(command: String) -> Result<Arguments, CLparseErrors> {
// TODO: Parse one implicit arg.
// NOTE: The above is out of scope for this pr but will be considered -able
let mut cmd = Arguments {
arguments: HashMap::new(),
};
if !command.is_empty() {
let command_split = command.split(" ");
for kv_pair in command_split {
if kv_pair.contains("=") {
// println!("{kv_pair}");
let mut kv_split = kv_pair.split("=");
let key = kv_split.next().unwrap().to_string();
// println!("{key}");
let value = kv_split.next().unwrap().to_string();
// println!("{value}");
cmd.arguments.insert(key, value);
} else {
return Err(CLparseErrors::MissingEquals);
}
}
}
Ok(cmd)
}
pub fn parse_from_string<S: Into<String>>(
string: S,
) -> Result<(Value, Arguments), CLparseErrors> {
let config: toml::Value = Value::String("".to_string());
let args = Arguments::parse(string.into())?;
return Ok((config, args));
}
#[cfg(feature = "std")]
pub fn parse_from_args() -> Result<(Value, Arguments), CLparseErrors> {
use std::{
collections::{BTreeMap, HashMap},
env,
fs::File,
io::{Read, Write},
println,
};
use alloc::format;
let args = env::args();
let mut args_str = String::new();
let dir = env::current_dir().unwrap();
let mut config: toml::Value = Value::String("".to_string());
match env::current_exe() {
Ok(bin) => {
if let Some(exe_name) = bin.as_path().file_name() {
let exe_name = exe_name.to_str().unwrap();
// println!("{exe_name}");
let path = format!(
"/home/{}/configs/{}/config.toml",
env::var("USER").unwrap(),
exe_name
);
let path = std::path::Path::new(&path);
if path.exists() {
let display = path.display();
let mut file = match File::open(&path) {
Err(why) => panic!("couldn't open {}: {}", display, why),
Ok(file) => file,
};
let mut s = String::new();
match file.read_to_string(&mut s) {
Err(why) => panic!("couldn't read {}: {}", display, why),
Ok(_) => {
// print!("{} contains:\n{}", display, s)
}
}
config = toml::from_str(&s).unwrap();
} else {
let mut file = File::create(path);
println!("Make a file in your home dir called \"configs\"");
file.unwrap().write_all(b"Hello, world!").unwrap();
}
}
}
Err(_) => todo!(),
}
for (count, arg) in args.enumerate() {
if count == 0 || count == 1 {
// Ignore stdio + program name on unix
} else {
args_str.push_str(&arg);
args_str.push(' ');
}
}
// needed to remove the very last ` ` which is unneeded
args_str.pop();
// println!("{args_str}");
let ret = Arguments::parse(args_str)?;
return Ok((config, ret));
}
pub fn is_truthy(&self, key: &str) -> bool {
match self.arguments.get(key) {
Some(val) => {
let val = val.to_lowercase();
if val == "true" || val == "1" || val == "on" || val == "t" {
return true;
}
false
}
None => false,
}
}
}
#[derive(Debug)]
pub enum CLparseErrors {
MalformendArgument,
MissingEquals,
}
impl Display for CLparseErrors {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self)?;
Ok(())
}
}
use core::fmt::Display;
use crate::alloc::string::ToString;
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "std")]
fn test_arg_parse() {
use std::println;
let argline = "abc=xyz".to_string();
match Arguments::parse(argline) {
Ok(ok) => {
println!("{ok:?}")
}
Err(err) => panic!("{err:?}"),
}
}
#[test]
fn fail_test_arg_parse() {
let argline = "abcxyz".to_string();
match Arguments::parse(argline) {
Ok(ok) => panic!("{ok:?}"),
Err(_) => {}
}
}
}
use toml::Value;
#[test]
#[cfg(feature = "std")]
pub fn load_config() {
use std::println;
let toml_str = include_str!("../assets/example.toml");
let decoded: Value = toml::from_str(toml_str).unwrap();
println!("{}", decoded);
}

9
libraries/tar/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "tar"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
versioning = { path = "../versioning" }

3
libraries/tar/readme.md Normal file
View file

@ -0,0 +1,3 @@
# Tar
instead of using the sortform name for tar we will be using full names
`mtime` becomes `modified_time`

55
libraries/tar/src/lib.rs Normal file
View file

@ -0,0 +1,55 @@
#![no_std]
pub const VERSION: versioning::Version = versioning::Version {
major: 0,
minor: 1,
patch: 0,
};
pub struct Header {
pub filename: [char; 100],
pub filemode: [char; 8],
pub user_id: [char; 8],
pub group_id: [char; 8],
pub size: [char; 12],
pub modified_time: [char; 12],
pub checksum: [char; 8],
pub typeflag: char,
}
impl From<[char; 512]> for Header {
fn from(item: [char; 512]) -> Self {
let mut filename: [char; 100] = ['\0'; 100];
filename[..100].clone_from_slice(&item[0..100]);
let mut filemode: [char; 8] = ['\0'; 8];
filemode[..8].clone_from_slice(&item[100..108]);
let mut user_id: [char; 8] = ['\0'; 8];
user_id[..8].clone_from_slice(&item[108..116]);
let mut group_id: [char; 8] = ['\0'; 8];
group_id[..8].clone_from_slice(&item[116..124]);
let mut size: [char; 12] = ['\0'; 12];
size[..12].clone_from_slice(&item[124..136]);
let mut modified_time: [char; 12] = ['\0'; 12];
modified_time[..12].clone_from_slice(&item[136..148]);
let mut checksum: [char; 8] = ['\0'; 8];
checksum[..8].clone_from_slice(&item[148..154]);
let typeflag: char = item[155];
Self {
filename,
filemode,
user_id,
group_id,
size,
modified_time,
checksum,
typeflag,
}
}
}

View file

@ -0,0 +1,12 @@
[package]
name = "trash_manifest"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
toml = "0.5"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
ron = "0.7"

View file

@ -0,0 +1,76 @@
use std::fs::File;
use std::io::{Read, Seek, Write};
use std::path::PathBuf;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct TrashFile {
pub name: String,
pub origin: PathBuf,
}
pub struct Manifest {
files: Vec<TrashFile>,
manifest: File,
}
impl Manifest {
/// Creates a new Manifest from a .manifest.ron file.
/// Expects File to be read and write.
pub fn new(mut manifest: File) -> Result<Self, TrashManifestError> {
let mut contents = String::new();
manifest.read_to_string(&mut contents)?;
manifest.rewind()?;
if contents.len() < 2 {
contents = "[]".to_string();
}
let files: Vec<TrashFile> = ron::from_str(&contents)?;
Ok(Self { files, manifest })
}
/// Get a certain file from the manifest by it's name.
pub fn get(&self, name: String) -> Option<TrashFile> {
self.files
.iter()
.find(|file| file.name == *name)
.map(|file| file.clone())
}
/// Get a clone of the underlying Vec of TrashFile.
pub fn get_all(&self) -> Vec<TrashFile> {
self.files.clone()
}
/// Remove a file from the manifest by it's name.
/// Consumes self and returns self back in a result to prevent inconsistencies
/// between self and the actual manifest file in case of Err.
pub fn remove(mut self, name: String) -> Result<Self, TrashManifestError> {
self.files.retain(|file| file.name != name);
let contents = ron::to_string(&self.files)?;
self.manifest.set_len(0)?;
self.manifest.rewind()?;
write!(&mut self.manifest, "{}", contents)?;
Ok(self)
}
/// Add a file to the manifest by it's name and path to origin.
/// Consumes self and returns self back in a result to prevent inconsistencies
/// between self and the actual manifest file in case of Err.
pub fn add(mut self, name: String, origin: PathBuf) -> Result<Self, TrashManifestError> {
self.files.push(TrashFile { name, origin });
dbg!(&self.files);
let contents = ron::to_string(&self.files)?;
self.manifest.set_len(0)?;
self.manifest.rewind()?;
write!(&mut self.manifest, "{}", contents)?;
Ok(self)
}
}
#[derive(thiserror::Error, Debug)]
pub enum TrashManifestError {
#[error("Could not read manifest file")]
IoError(#[from] std::io::Error),
#[error("Could not serialize or deserialize manifest file")]
SerializationError(#[from] ron::Error),
}

View file

@ -0,0 +1,12 @@
[package]
name = "versioning"
version = "0.1.2"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[dependencies.serde]
version = "1.0"
default-features = false
features = ["derive"]

View file

@ -0,0 +1,45 @@
// ! A unified versioning system for Rust.
#![no_std]
use core::{fmt::Display, prelude::rust_2021::derive};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Version {
pub major: u8,
pub minor: u8,
pub patch: u8,
}
impl Version {
pub fn new(major: u8, minor: u8, patch: u8) -> Self {
Self {
major,
minor,
patch,
}
}
pub fn new_from_env(vstring: &'static str) -> Self {
let mut spl = vstring.split(".");
if spl.clone().count() > 3 {
panic!("Improper version string");
}
// TODO: handle failing any of these
let major: u8 = spl.nth(0).unwrap().parse().unwrap();
let minor: u8 = spl.nth(1).unwrap().parse().unwrap();
let patch: u8 = spl.nth(2).unwrap().parse().unwrap();
Self {
major,
minor,
patch,
}
}
}
impl Display for Version {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "v{}.{}.{}", self.major, self.minor, self.patch)?;
Ok(())
}
}

View file

@ -0,0 +1,12 @@
[package]
name = "delete"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clparse = { path = "../clparse", features = ["std"] }
trash_manifest = { path = "../trash_manifest" }
anyhow = "1.0"
fs_extra = "1.2"

15
programs/delete/README.md Normal file
View file

@ -0,0 +1,15 @@
# delete
Moves a file to your .trash directory and updates the .manifest file therein.
## Usage
Remove file:
* `delete path=foobar.txt`
* `delete p=foobar.txt`
Remove directory:
* `delete path=foobar/ dir=true`
* `delete path=foobar/ d=t`

View file

@ -0,0 +1,9 @@
# // - Always permanently delete files,
bypass_trash = true
# // - Set .trash path.
trashpath = "/home/user/.trash"
# // TODO: Configuration options in programs config file:
# // - Handle naming collisions by changing file name instead of erroring,

View file

@ -0,0 +1,66 @@
use fs_extra::{dir, file};
use std::env;
use std::fs::OpenOptions;
use std::path::PathBuf;
// TODO: Configuration options in programs config file:
// - Always permanently delete files,
// - Handle naming collisions by changing file name instead of erroring,
// - Set .trash path.
// TODO: Option to permanently delete file.
// TODO: .trash path option.
fn main() -> anyhow::Result<()> {
let ret = clparse::Arguments::parse_from_args()?;
let args = ret.1;
let trash_path = env::var("HOME")? + "/.trash/";
let from = PathBuf::from({
let p = args.arguments.get("p");
let path = args.arguments.get("path");
match (p, path) {
(None, None) => anyhow::bail!("No path specified"),
(None, Some(path)) => path,
(Some(path), None) => path,
(Some(_), Some(_)) => anyhow::bail!("Option p and path conflict"),
}
})
.canonicalize()?;
let name = from
.file_name()
.map(|name| name.to_str())
.flatten()
.ok_or(anyhow::anyhow!("Weird file name"))?;
let manifest = trash_manifest::Manifest::new(
OpenOptions::new()
.create(true)
.write(true)
.read(true)
.open(trash_path.clone() + ".manifest.ron")?,
)?;
let is_in_trash = if from.to_string_lossy() == trash_path.clone() + name {
true
} else {
false
};
match (
is_in_trash,
args.arguments.get("dir").is_some() || args.arguments.get("d").is_some(),
) {
(true, true) => fs_extra::dir::remove(from.clone())?,
(true, false) => fs_extra::file::remove(from.clone())?,
(false, true) => {
fs_extra::dir::move_dir(from.clone(), trash_path + name, &dir::CopyOptions::new())?;
}
(false, false) => {
fs_extra::file::move_file(from.clone(), trash_path + name, &file::CopyOptions::new())?;
}
}
if !is_in_trash {
manifest.add(name.to_string(), from.clone())?;
}
Ok(())
}

11
programs/list/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "list"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clparse = { path = "../clparse" }
number_prefix = "*"
table = { path = "../table" }

95
programs/list/src/main.rs Normal file
View file

@ -0,0 +1,95 @@
use std::{collections::HashMap, env, fs};
use number_prefix::NumberPrefix;
use table::{Row, Table};
fn main() -> std::io::Result<()> {
let ret = clparse::Arguments::parse_from_args().unwrap();
let args = ret.1;
let mut table = Table {
table: HashMap::new(),
};
let current_dir = env::current_dir()?;
let size_shown = args.is_truthy("s");
let last_modified_shown = args.is_truthy("lm");
let row = Row(vec![], 20);
table.table.insert("File Name".to_string(), row);
if size_shown {
let row = Row(vec![], 20);
table.table.insert("Size".to_string(), row);
}
if last_modified_shown {
let row = Row(vec![], 20);
table.table.insert("Last Modified".to_string(), row);
}
for entry in fs::read_dir(current_dir)? {
let entry = entry?;
let path = entry.path();
let metadata = fs::metadata(&path)?;
let abc = table.table.get_mut(&"File Name".to_string());
let mut path = format!(
"{}",
path.file_name()
.ok_or("No filename")
.unwrap()
.to_str()
.unwrap(),
);
if metadata.is_dir() {
path.push('/')
}
match abc {
Some(row) => row.0.push(path),
None => todo!(),
}
if size_shown {
let size = metadata.len();
let abc = table.table.get_mut(&"Size".to_string());
match abc {
Some(row) => row.0.push(maybe_bytes_suffix(size)),
None => todo!(),
}
}
if last_modified_shown {
let last_modified = metadata.modified()?.elapsed().unwrap().as_secs();
let abc = table.table.get_mut(&"Last Modified".to_string());
match abc {
Some(row) => row.0.push(format!("{} seconds ago", last_modified)),
None => todo!(),
}
// print!(" {} seconds ago |", last_modified);
}
print!("\n");
}
println!("{}", table);
Ok(())
}
fn maybe_bytes_suffix(amount: u64) -> String {
return match NumberPrefix::binary(amount as f64) {
NumberPrefix::Standalone(bytes) => {
format!("{}B", bytes)
}
NumberPrefix::Prefixed(prefix, n) => {
format!("{:.1} {}B", n, prefix.symbol())
}
};
}

View file

@ -0,0 +1,9 @@
[package]
name = "shell"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clparse = { path = "../clparse" }

View file

@ -0,0 +1,2 @@
[alias]
ls = "list"

View file

@ -0,0 +1,57 @@
use std::{
env,
io::{stdin, stdout, Read, Write},
path::Path,
process::{Child, Command, Stdio},
};
const VERSION: &str = env!("CARGO_PKG_VERSION");
fn main() {
let ret = clparse::Arguments::parse_from_args().unwrap();
let config = ret.0;
// println!("{}", config);
loop {
// use the `>` character as the prompt
// need to explicitly flush this to ensure it prints before read_line
print!("{} ~> ", std::env::current_dir().unwrap().display());
stdout().flush().unwrap();
let mut input = String::new();
stdin().read_line(&mut input).unwrap();
print!("{}", input);
let mut in_qoute = false;
let mut building_command = String::new();
let mut command_chain: Vec<String> = vec![];
for x in input.chars() {
if x == '"' {
in_qoute = !in_qoute;
}
if x == '+' || x == '\n' {
if !in_qoute {
command_chain.push(building_command.trim().to_string());
building_command = String::new();
}
} else {
building_command.push(x);
}
}
for mut command in command_chain {
let abc = config.get("alias");
for x in abc.unwrap().as_table().unwrap() {
if command == *x.0 {
command = (*x.1).to_string();
command.remove(0);
command.pop();
}
}
println!("Command: {}", command);
}
}
}

View file

@ -0,0 +1,8 @@
[package]
name = "table"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

19
programs/table/README.md Normal file
View file

@ -0,0 +1,19 @@
# Table
a table is a data structure similar to a 2d array with rows and coloumns
for example a list of files could be passed as a table to copy
using the following command you can generate a table
`list s=t e=t` which is shorthand for `list size=true executable=true`
> To generate a similar table use [this website](https://www.tablesgenerator.com/text_tables)
|file name|file size|executable|
|-------- |---------|----------|
|able.txt |54 |false |
|ex.wasm |34 |true |
using the following command you can merge all files into one
`list s=t e=t + append dest=all.txt`

80
programs/table/src/lib.rs Normal file
View file

@ -0,0 +1,80 @@
use core::fmt::Display;
use std::collections::HashMap;
pub type Header = String;
#[derive(Debug)]
pub struct Row(pub Vec<String>, pub usize);
impl Display for Row {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// let header = &self.0;
let column = &self.0;
let longest_length = &self.1;
// write!(f, "{}", header)?;
// for _ in 0..longest_length - header.len() {
// write!(f, " ")?;
// }
// write!(f, "|\n")?;
// for _ in 0..*longest_length {
// write!(f, "-")?;
// }
// write!(f, "|\n")?;
for xys in column {
write!(f, "{}", xys)?;
for _ in 0..longest_length - xys.len() {
write!(f, " ")?;
}
write!(f, "|\n")?;
}
Ok(())
}
}
pub struct Table {
pub table: HashMap<Header, Row>,
}
impl Display for Table {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (header, row) in &self.table {
write!(f, "{}", header)?;
for _ in 0..row.1 - header.len() {
write!(f, " ")?;
}
write!(f, "|\n")?;
for _ in 0..row.1 {
write!(f, "-")?;
}
write!(f, "|\n")?;
write!(f, "{}", row)?;
}
Ok(())
}
}
#[test]
fn test_print() {
let tbl = Table {
table: HashMap::new(),
};
/* tbl.table.append(&mut vec![Row(
"header".to_string(),
vec![
"hi".to_string(),
"hey whats up there".to_string(),
"hey".to_string(),
"whats".to_string(),
"up".to_string(),
"there".to_string(),
],
18,
)]);
*/
println!("{}", tbl);
}

View file

@ -0,0 +1,9 @@
[package]
name = "table_view"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
table = { path = "../table" }

View file

@ -0,0 +1,19 @@
use std::collections::HashMap;
use table::{Row, Table};
fn main() {
let mut tbl = Table {
table: HashMap::new(),
};
tbl.table.insert(
"Example".to_string(),
Row(
// "header".to_string(),
vec!["hi".to_string(), "hey whats up there".to_string()],
19,
),
);
println!("{}", tbl);
}

View file

@ -0,0 +1,12 @@
[package]
name = "undelete"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clparse = { path = "../clparse" }
trash_manifest = { path = "../trash_manifest" }
anyhow = "1.0"
fs_extra = "1.2"

View file

@ -0,0 +1,20 @@
# undelete
Moves a file from your .trash directory to current directory then updates the .manifest file.
## Usage
List deleted files:
* `undelete list=true`
* `undelete l=t`
Undelete file or directory:
* `undelete name=foobar.txt`
* `undelete n=foobar.txt`
Move to original location instead:
* `undelete name=foobar.txt origin=true`
* `undelete name=foobar.txt o=t`

View file

@ -0,0 +1,55 @@
use std::{env, fs::OpenOptions, path::PathBuf};
use fs_extra::{dir, file};
fn main() -> anyhow::Result<()> {
let ret = clparse::Arguments::parse_from_args()?;
let args = ret.1;
let trash_path = env::var("HOME")? + "/.trash/";
let manifest = trash_manifest::Manifest::new(
OpenOptions::new()
.create(true)
.write(true)
.read(true)
.open(trash_path.clone() + ".manifest.ron")?,
)?;
if !args.is_truthy("list") || !args.is_truthy("l") {
let name = {
let n = args.arguments.get("n");
let name = args.arguments.get("name");
match (n, name) {
(None, None) => anyhow::bail!("No name or operation specified"),
(None, Some(name)) => name,
(Some(name), None) => name,
(Some(_), Some(_)) => anyhow::bail!("Option n and name conflict"),
}
};
let file = manifest
.get(name.to_string())
.ok_or(anyhow::anyhow!("File not found in trash manifest"))?;
let from = PathBuf::from(trash_path.clone() + &file.name);
let to = if args.is_truthy("origin") || args.is_truthy("o") {
file.origin
} else {
env::current_dir()?
};
if from.is_dir() {
fs_extra::dir::move_dir(from, &to, &dir::CopyOptions::new())?;
} else {
fs_extra::file::move_file(from, &to, &file::CopyOptions::new())?;
}
manifest.remove(file.name.clone())?;
} else {
let mut output = String::new();
for file in manifest.get_all() {
output.push_str(&(file.name + "\n"));
}
println!("{}", output);
}
Ok(())
}