Merge pull request 'devel' (#73) from devel into master
cargo devel CI / cargo CI (push) Has been cancelled Details

Reviewed-on: #73
This commit is contained in:
Christoph J. Scherr 2024-03-01 21:20:29 +01:00
commit 4e486fde80
38 changed files with 562 additions and 1255 deletions

View File

@ -16,9 +16,11 @@ jobs:
- name: get repo
uses: actions/checkout@v4
- name: install rust
uses: dtolnay/rust-toolchain@stable
uses: https://github.com/dtolnay/rust-toolchain@stable
- name: install additional rust things
run: rustup component add rustfmt
run: |
rustup component add rustfmt
rustup component add clippy
- name: config custom registry
run: |
mkdir -p ~/.cargo/
@ -28,16 +30,16 @@ jobs:
echo '[registries.cscherr]' >> ~/.cargo/config.toml
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
cat ~/.cargo/config.toml
- name: cargo check
run: cargo check --all-features --all-targets
- name: cargo fix
run: cargo fix --all-features --all-targets
- name: cargo clippy check
run: cargo clippy --all-features --all-targets
- name: cargo clippy fix
run: cargo clippy --fix --all-features --all-targets
- name: cargo fmt
run: cargo fmt --all
- name: cargo test
run: cargo test --all-features --all-targets
- name: commit back to repository
uses: stefanzweifel/git-auto-commit-action@v5
uses: https://github.com/stefanzweifel/git-auto-commit-action@v5
with:
# Optional. Commit message for the created commit.
# Defaults to "Apply automatic changes"

47
.github/workflows/cargo.yaml vendored Normal file
View File

@ -0,0 +1,47 @@
name: cargo devel CI
on:
push:
branches:
- '**'
# - '!master'
jobs:
CI:
runs-on: ubuntu-latest
name: cargo CI
permissions:
# Give the default GITHUB_TOKEN write permission to commit and push the
# added or changed files to the repository.
contents: write
steps:
- name: get repo
uses: actions/checkout@v4
- name: install rust
uses: dtolnay/rust-toolchain@stable
- name: install additional rust things
run: |
rustup component add rustfmt
rustup component add clippy
- name: config custom registry
run: |
mkdir -p ~/.cargo/
echo "" > ~/.cargo/config.toml
echo "[registry]" >> ~/.cargo/config.toml
echo 'cscherr = "cscherr"' >> ~/.cargo/config.toml
echo '[registries.cscherr]' >> ~/.cargo/config.toml
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
cat ~/.cargo/config.toml
- name: cargo clippy check
run: cargo clippy --all-features --all-targets
- name: cargo clippy fix
run: cargo clippy --fix --all-features --all-targets
- name: cargo fmt
run: cargo fmt --all
- name: cargo test
run: cargo test --all-features --all-targets
- name: commit back to repository
uses: stefanzweifel/git-auto-commit-action@v5
with:
# Optional. Commit message for the created commit.
# Defaults to "Apply automatic changes"
commit_message: automatic cargo CI changes

120
.github/workflows/maturin.yml vendored Normal file
View File

@ -0,0 +1,120 @@
# This file is autogenerated by maturin v1.4.0
# To update, run
#
# maturin generate-ci github
#
name: CI
on:
push:
branches:
- main
- master
tags:
- '*'
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
linux:
runs-on: ubuntu-latest
strategy:
matrix:
target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
manylinux: auto
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
windows:
runs-on: windows-latest
strategy:
matrix:
target: [x64, x86]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
architecture: ${{ matrix.target }}
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
macos:
runs-on: macos-latest
strategy:
matrix:
target: [x86_64, aarch64]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist --find-interpreter
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
sdist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
- name: Upload sdist
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist
release:
name: Release
runs-on: ubuntu-latest
if: "startsWith(github.ref, 'refs/tags/')"
needs: [linux, windows, macos, sdist]
steps:
- uses: actions/download-artifact@v3
with:
name: wheels
- name: Publish to PyPI
uses: PyO3/maturin-action@v1
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
with:
command: upload
args: --non-interactive --skip-existing *

1
.python-version Normal file
View File

@ -0,0 +1 @@
rust

View File

@ -3,14 +3,14 @@ resolver = "2"
members = [
".",
"members/libpt-core",
"members/libpt-math",
"members/libpt-log",
"members/libpt-net",
"members/libpt-py",
]
default-members = [".", "members/libpt-core"]
[workspace.package]
publish = true
version = "0.3.11"
version = "0.4.0-alpha.1"
edition = "2021"
authors = ["Christoph J. Scherr <software@cscherr.de>"]
license = "MIT"
@ -18,7 +18,7 @@ description = "Personal multitool"
readme = "README.md"
homepage = "https://git.cscherr.de/PlexSheep/pt"
repository = "https://git.cscherr.de/PlexSheep/pt"
keywords = ["cli", "library"]
keywords = ["library"]
categories = [
"command-line-utilities",
"development-tools",
@ -28,11 +28,10 @@ categories = [
[workspace.dependencies]
anyhow = "1.0.79"
thiserror = "1.0.56"
libpt-core = { version = "0.3.11", path = "members/libpt-core" }
libpt-bintols = { version = "0.3.11", path = "members/libpt-bintols" }
libpt-log = { version = "0.3.11", path = "members/libpt-log" }
libpt-math = { version = "0.3.11", path = "members/libpt-math" }
libpt-net = { version = "0.3.11", path = "members/libpt-net" }
libpt-core = { version = "0.3.12", path = "members/libpt-core" }
libpt-bintols = { version = "0.3.12", path = "members/libpt-bintols" }
libpt-log = { version = "0.3.12", path = "members/libpt-log" }
libpt-py = { version = "0.3.12", path = "members/libpt-py" }
[package]
name = "libpt"
@ -49,13 +48,12 @@ keywords.workspace = true
categories.workspace = true
[features]
default = ["log", "core", "bin"]
default = ["log", "core"]
core = []
math = []
log = []
bintols = []
net = []
bin = ["dep:clap", "dep:clap-num", "dep:clap-verbosity-flag", "math", "bintols"]
full = ["default", "core", "log", "bintols"]
log = ["dep:libpt-log"]
bintols = ["dep:libpt-bintols", "log"]
# py = ["dep:libpt-py"]
[lib]
name = "libpt"
@ -65,23 +63,7 @@ crate-type = [
"rlib",
]
[[bin]]
name = "ccc"
path = "src/ccc/mod.rs"
required-features = ["bin", "math"]
[[bin]]
name = "hedu"
path = "src/hedu/mod.rs"
required-features = ["bin", "bintols"]
[dependencies]
libpt-core = { workspace = true }
libpt-bintols = { workspace = true }
libpt-log = { workspace = true }
libpt-math = { workspace = true }
libpt-net = { workspace = true }
clap = { version = "4.4.4", features = ["derive"], optional = true }
clap-num = { version = "1.0.2", optional = true }
clap-verbosity-flag = { version = "2.0.1", optional = true }
libpt-bintols = { workspace = true, optional = true }
libpt-log = { workspace = true, optional = true }

View File

@ -1,5 +1,11 @@
# pt / libpt
![Project badge](https://img.shields.io/badge/language-Rust-blue.svg)
![Crates.io License](https://img.shields.io/crates/l/libpt)
![Gitea Release](https://img.shields.io/gitea/v/release/PlexSheep/pt?gitea_url=https%3A%2F%2Fgit.cscherr.de)
![Gitea language count](https://img.shields.io/gitea/languages/count/PlexSheep/pt?gitea_url=https%3A%2F%2Fgit.cscherr.de)
[![cargo checks and tests](https://github.com/PlexSheep/pt/actions/workflows/cargo.yaml/badge.svg)](https://github.com/PlexSheep/pt/actions/workflows/cargo.yaml)
![pt-logo](data/media/pt-logo.svg)
`pt` stands for either one of "personal tool", "plex tool", "pete" or something among those lines.
@ -9,6 +15,12 @@ crate, python module or executable.
Let's see if I make it a bloated mess or stop committing after 30 hello worlds.
* [Original Repository](https://git.cscherr.de/PlexSheep/pt)
* [GitHub Mirror](https://github.com/PlexSheep/pt)
* [Codeberg Mirror](https://codeberg.org/PlexSheep/pt)
* [crates.io](https://crates.io/crates/libpt)
* [docs.rs](https://docs.rs/crate/libpt/)
## Dependencies
- See `cargo.toml`
@ -59,14 +71,6 @@ The documentation can be automatically generated with `cargo doc --open`.
An up to date version of the Documentation can be found [here](https://docs.rs/libpt/)
## Mirrored
The origin of this repository is [git.cscherr.de](https://git.cscherr.de/PlexSheep/pt)
It is mirrored to:
- [Codeberg](https://codeberg.org/PlexSheep/pt)
## License
**Pt is MIT Licensed**

View File

@ -1,7 +1,7 @@
[package]
name = "libpt-bintols"
publish.workspace = true
version.workspace = true
version = "0.3.12"
edition.workspace = true
authors.workspace = true
license.workspace = true

View File

@ -9,13 +9,13 @@ pub use num_traits::{PrimInt, ToPrimitive};
/// * `data` - The data you are trying to dump
pub fn bytes_to_bin(data: &[u8]) -> String {
let mut s = format!("0b{:08b}", data.first().unwrap());
for i in 1..data.len() {
s.push_str(&format!("_{:08b}", data[i]));
if i % 8 == 0 {
s.push_str("\n")
for dat in data {
s.push_str(&format!("_{:08b}", dat));
if dat % 8 == 0 {
s.push('\n')
}
}
return s;
s
}
/// Quickly format a number of Bytes [`usize`] with the corresponding
@ -34,17 +34,17 @@ where
T: std::fmt::Debug,
{
if total < T::from(KIBI).unwrap() {
return format!("{total} B");
format!("{total} B")
} else if T::from(KIBI).unwrap() <= total && total < T::from(MEBI).unwrap() {
return format!("{:.2} K", total.to_f64().unwrap() / KIBI as f64);
format!("{:.2} K", total.to_f64().unwrap() / KIBI as f64)
} else if T::from(MEBI).unwrap() <= total && total < T::from(GIBI).unwrap() {
return format!("{:.2} M", total.to_f64().unwrap() / MEBI as f64);
format!("{:.2} M", total.to_f64().unwrap() / MEBI as f64)
} else if T::from(GIBI).unwrap() <= total && total < T::from(TEBI).unwrap() {
return format!("{:.2} G", total.to_f64().unwrap() / GIBI as f64);
format!("{:.2} G", total.to_f64().unwrap() / GIBI as f64)
} else if T::from(TEBI).unwrap() <= total && total < T::from(PEBI).unwrap() {
return format!("{:.2} T", total.to_f64().unwrap() / TEBI as f64);
format!("{:.2} T", total.to_f64().unwrap() / TEBI as f64)
} else if T::from(PEBI).unwrap() <= total && total < T::from(EXBI).unwrap() {
return format!("{:.2} P", total.to_f64().unwrap() / PEBI as f64);
format!("{:.2} P", total.to_f64().unwrap() / PEBI as f64)
}
// now we are starting to reach the sizes that are pretty unrealistic
// (as of 2023 that is, hello future)
@ -53,9 +53,9 @@ where
// to work with a fixed, larger sized datatype
else {
let total: u128 = total.to_u128().unwrap();
if EXBI <= total && total < ZEBI {
if (EXBI..ZEBI).contains(&total) {
return format!("{:.2} E", total.to_f64().unwrap() / EXBI as f64);
} else if ZEBI <= total && total < YOBI {
} else if (ZEBI..YOBI).contains(&total) {
return format!("{:.2} Z", total.to_f64().unwrap() / ZEBI as f64);
} else if YOBI <= total {
return format!("{:.2} Y", total.to_f64().unwrap() / YOBI as f64);

View File

@ -1,239 +0,0 @@
//! # Dump data
//!
//! This crate is part of [`pt`](../libpt/index.html), but can also be used as a standalone
//! module.
//!
//! Hedu is made for hexdumping data. `libpt` offers a cli application using this module.
use crate::display::humanbytes;
use anyhow::{bail, Result};
use libpt_log::{debug, error, trace, warn};
use std::io::{prelude::*, Read, SeekFrom};
pub const BYTES_PER_LINE: usize = 16;
pub const LINE_SEP_HORIZ: char = '─';
pub const LINE_SEP_VERT: char = '│';
pub const CHAR_BORDER: &'static str = "|";
#[derive(Debug)]
pub struct Hedu {
pub chars: bool,
pub skip: usize,
pub show_identical: bool,
pub limit: usize,
stop: bool,
len: usize,
data_idx: usize,
rd_counter: usize,
buf: [[u8; BYTES_PER_LINE]; 2],
alt_buf: usize,
pub display_buf: String,
first_iter: bool,
}
impl Hedu {
pub fn new(chars: bool, skip: usize, show_identical: bool, limit: usize) -> Self {
Hedu {
chars,
skip,
show_identical,
limit,
stop: false,
len: 0,
data_idx: 0,
rd_counter: 0,
buf: [[0; BYTES_PER_LINE]; 2],
alt_buf: 0,
display_buf: String::new(),
first_iter: true,
}
}
#[inline]
pub fn display(&mut self) {
println!("{}", self.display_buf);
self.display_buf = String::new();
}
#[inline]
pub fn sep(&mut self) {
if self.chars {
self.display_buf += &format!("{LINE_SEP_HORIZ}").repeat(80);
} else {
self.display_buf += &format!("{LINE_SEP_HORIZ}").repeat(59);
}
self.display();
}
#[inline]
pub fn newline(&mut self) {
self.display_buf += "\n";
self.display();
}
fn dump_a_line(&mut self) {
self.display_buf += &format!("{:08X} {LINE_SEP_VERT} ", self.data_idx);
if self.len != 0 {
for i in 0..self.len {
if i as usize % BYTES_PER_LINE == BYTES_PER_LINE / 2 {
self.display_buf += " ";
}
self.display_buf += &format!("{:02X} ", self.buf[self.alt_buf][i]);
}
if self.len == BYTES_PER_LINE / 2 {
self.display_buf += " "
}
for i in 0..(BYTES_PER_LINE - self.len) {
if i as usize % BYTES_PER_LINE == BYTES_PER_LINE / 2 {
self.display_buf += " ";
}
self.display_buf += " ";
}
} else {
self.display_buf += &format!("{:49}", "");
}
if self.chars {
self.display_buf += &format!("{LINE_SEP_VERT} ");
if self.len != 0 {
self.display_buf += CHAR_BORDER;
for i in 0..self.len {
self.display_buf +=
&format!("{}", mask_chars(self.buf[self.alt_buf][i] as char));
}
self.display_buf += CHAR_BORDER;
} else {
self.display_buf += &format!("{:^8}", "");
}
}
self.display();
}
fn skip_lines(&mut self, data: &mut dyn DataSource) -> Result<()> {
trace!(buf = format!("{:?}", self.buf), "found a duplicating line");
let start_line = self.data_idx;
while self.buf[0] == self.buf[1] && self.len == BYTES_PER_LINE {
self.rd_data(data)?;
}
self.display_buf += &format!(
"******** {LINE_SEP_VERT} {:<49}",
format!(
"(repeats {} lines)",
self.data_idx - start_line / (BYTES_PER_LINE) + 1
)
);
if self.chars {
self.display_buf += &format!("{LINE_SEP_VERT}");
}
trace!(
buf = format!("{:X?}", self.buf),
"dumping buf after line skip"
);
self.alt_buf ^= 1; // read into the other buf, so we can check for sameness
self.display();
Ok(())
}
pub fn dump(&mut self, data: &mut dyn DataSource) -> Result<()> {
// skip a given number of bytes
if self.skip > 0 {
data.skip(self.skip)?;
self.rd_counter += self.skip;
debug!(
data_idx = self.data_idx,
"Skipped {}",
humanbytes(self.skip)
);
}
// print the head
self.display_buf += &format!("DATA IDX {LINE_SEP_VERT} DATA AS HEX");
if self.chars {
self.display_buf += &format!("{:width$} {LINE_SEP_VERT} DATA AS CHAR", "", width = 37);
}
self.display();
self.sep();
// data dump loop
self.rd_data(data)?;
self.data_idx = 0;
while self.len > 0 || self.first_iter {
self.first_iter = false;
self.dump_a_line();
// loop breaker logic
if self.stop || self.len < BYTES_PER_LINE {
break;
}
self.rd_data(data)?;
// after line logic
if self.buf[0] == self.buf[1] && self.len == BYTES_PER_LINE && !self.show_identical {
self.skip_lines(data)?;
}
}
self.data_idx += BYTES_PER_LINE;
self.sep();
self.display_buf += &format!(
"{:08X} {LINE_SEP_VERT} read total:\t\t {:<8} {:<15}",
self.rd_counter,
humanbytes(self.rd_counter),
format!("({} B)", self.rd_counter)
);
if self.chars {
self.display_buf += &format!("{LINE_SEP_VERT}");
}
self.display();
Ok(())
}
#[inline]
fn adjust_counters(&mut self) {
self.rd_counter += self.len;
self.data_idx += self.len;
}
fn rd_data(&mut self, data: &mut dyn DataSource) -> Result<()> {
match data.read(&mut self.buf[self.alt_buf]) {
Ok(mut len) => {
if self.limit != 0 && self.rd_counter + (BYTES_PER_LINE - 1) >= self.limit {
len = self.limit % BYTES_PER_LINE;
self.stop = true;
}
self.len = len;
self.adjust_counters();
return Ok(());
}
Err(err) => {
error!("error while reading data: {err}");
bail!(err)
}
}
}
}
pub trait DataSource: Read {
fn skip(&mut self, length: usize) -> std::io::Result<()>;
}
impl DataSource for std::io::Stdin {
fn skip(&mut self, _length: usize) -> std::io::Result<()> {
warn!("can't skip bytes for the stdin!");
Ok(())
}
}
impl DataSource for std::fs::File {
fn skip(&mut self, length: usize) -> std::io::Result<()> {
self.seek(SeekFrom::Current(length as i64))?;
// returns the new position from the start, we don't need it here.
Ok(())
}
}
fn mask_chars(c: char) -> char {
if c.is_ascii_graphic() {
return c;
} else if c == '\n' {
return '↩';
} else if c == ' ' {
return '␣';
} else if c == '\t' {
return '⭾';
} else {
return '<27>';
}
}

View File

@ -25,4 +25,3 @@ pub const YOBI: u128 = 2u128.pow(80);
// use libpt_core;
pub mod datalayout;
pub mod display;
pub mod hedu;

View File

@ -2,6 +2,6 @@ use libpt_bintols::*;
#[test]
fn mkdmp() {
let v = vec![true, true, false];
let v = [true, true, false];
investigate_memory_layout!(bool, v);
}

View File

@ -1,7 +1,7 @@
[package]
name = "libpt-core"
publish.workspace = true
version.workspace = true
version = "0.3.12"
edition.workspace = true
authors.workspace = true
license.workspace = true

View File

@ -6,33 +6,7 @@
//! This crate implements core functionality useful for many use cases, such as macros,
//! formatting functions and more.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
/// macros to make things faster in your code
pub mod macros;
/// some general use printing to stdout tools
pub mod printing;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View File

@ -2,33 +2,14 @@
//!
//! This module implements macros for use with `libpt`.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
pub use crate::get_stdout_for;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
/// ## catches what the expression would write to the `stdout`
///
/// This macro takes an expression, executes it, and catches what it would write to the stdout.
/// The buffer of the stdout will then be returned for further use.
///
/// This is especially useful when testing loggers or other frontend CLI functions.
///
/// Inspiration: [users.rust-lang.org](https://users.rust-lang.org/t/how-to-test-functions-that-use-println/67188/5)
#[macro_export]
macro_rules! get_stdout_for {
($test:expr) => {{
@ -46,13 +27,3 @@ macro_rules! get_stdout_for {
output
}};
}
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View File

@ -1,49 +1,11 @@
//! # tools that make printing stuff better
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
// reimport our macros to this module, so the user does not get confused when importing the macros
pub use crate::divider;
pub use crate::print_divider;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
/// Quickly get a one line visual divider
#[macro_export]
macro_rules! divider {
() => {{
format!("{:=^80}", "=")
}};
pub fn divider() -> String {
format!("{:=^80}", "=")
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Quickly print a one line visual divider
#[macro_export]
macro_rules! print_divider {
() => {{
println!("{}", divider!())
}};
pub fn print_divider() {
println!("{:=^80}", "=")
}
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View File

@ -1,7 +1,7 @@
[package]
name = "libpt-log"
publish.workspace = true
version.workspace = true
version = "0.3.12"
edition.workspace = true
authors.workspace = true
license.workspace = true

View File

@ -2,70 +2,22 @@
//!
//! This module handles errors in logging contexts.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use anyhow;
use thiserror::Error;
use tracing::subscriber::SetGlobalDefaultError;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
/// ## Errors for the [Logger](super::Logger)
#[derive(Error)]
#[derive(Error, Debug)]
pub enum Error {
/// Bad IO operation
#[error("Bad IO operation")]
IO(std::io::Error),
IO(#[from] std::io::Error),
/// Various errors raised when the messenger is used in a wrong way
#[error("Bad usage")]
Usage(String),
/// Could not assign logger as the global default
#[error("Could not assign as global default")] // TODO: make this more clear
SetGlobalDefaultFail(SetGlobalDefaultError),
#[error("Could not assign logger as global default")]
SetGlobalDefaultFail(#[from] SetGlobalDefaultError),
/// any other error type, wrapped in [anyhow::Error](anyhow::Error)
#[error(transparent)]
Other(#[from] anyhow::Error),
}
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Error::IO(value)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl From<SetGlobalDefaultError> for Error {
fn from(value: SetGlobalDefaultError) -> Self {
Error::SetGlobalDefaultFail(value)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::IO(e) => write!(f, "<IO Error {e:?}>"),
Error::Usage(e) => write!(f, "<Usage Error {e:?}>"),
Error::SetGlobalDefaultFail(e) => write!(f, "<SetGlobalDefaultFail {e:?}>"),
}
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View File

@ -14,12 +14,8 @@
//! - [`tracing_appender`]: Used to log to files
//! - [`tracing_subscriber`]: Used to do actual logging, formatting, to stdout
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use std::{
fmt,
ops::Deref,
path::PathBuf,
sync::atomic::{AtomicBool, Ordering},
};
@ -28,55 +24,34 @@ pub mod error;
use error::*;
pub use tracing::{debug, error, info, trace, warn, Level};
use tracing_appender::{
self,
non_blocking::{NonBlocking, WorkerGuard},
};
use tracing_subscriber::{
fmt::{
format::FmtSpan,
time::{self, FormatTime},
},
prelude::*,
};
use tracing_appender::{self, non_blocking::NonBlocking};
use tracing_subscriber::fmt::{format::FmtSpan, time};
use anyhow::{bail, Result};
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// The log level used when none is specified
pub const DEFAULT_LOG_LEVEL: Level = Level::INFO;
/// The path where logs are stored when no path is given.
///
/// Currently, this is `/dev/null`, meaning they will be written to the void = discarded.
pub const DEFAULT_LOG_DIR: &'static str = "/dev/null";
pub const DEFAULT_LOG_DIR: &str = "/dev/null";
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
static INITIALIZED: AtomicBool = AtomicBool::new(false);
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// ## Logger for [`pt`](../libpt/index.html)
///
/// This struct exists mainly for the python module, so that we can use the same logger with both
/// python and rust.
pub struct Logger;
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
/// ## Main implementation
impl Logger {
/// ## create a `Logger`
///
/// Creates a new uninitialized [`Logger`] object.
pub fn new() -> Self {
let l = Logger {};
l
}
/// ## initializes the logger
///
/// Will enable the logger to be used.
///
/// Assumes some defaults, use [`init_customized`](Self::init_customized) for more control
pub fn init(log_dir: Option<PathBuf>, max_level: Option<Level>, uptime: bool) -> Result<()> {
Self::init_customized(
pub fn build(log_dir: Option<PathBuf>, max_level: Option<Level>, uptime: bool) -> Result<Self> {
Self::build_customized(
log_dir.is_some(),
log_dir.unwrap_or(PathBuf::from(DEFAULT_LOG_DIR)),
true,
@ -99,8 +74,8 @@ impl Logger {
/// useful in cases with only one sender to the logging framework.
///
/// Assumes some defaults, use [`init_customized`](Self::init_customized) for more control
pub fn init_mini(max_level: Option<Level>) -> Result<()> {
Self::init_customized(
pub fn build_mini(max_level: Option<Level>) -> Result<Self> {
Self::build_customized(
false,
PathBuf::from(DEFAULT_LOG_DIR),
true,
@ -117,10 +92,12 @@ impl Logger {
)
}
// TODO: make the args a struct for easy access
//
/// ## initializes the logger
///
/// Will enable the logger to be used.
pub fn init_customized(
pub fn build_customized(
log_to_file: bool,
log_dir: PathBuf,
ansi: bool,
@ -134,11 +111,11 @@ impl Logger {
pretty: bool,
show_time: bool,
uptime: bool, // uptime instead of system time
) -> Result<()> {
) -> Result<Self> {
// only init if no init has been performed yet
if INITIALIZED.load(Ordering::Relaxed) {
warn!("trying to reinitialize the logger, ignoring");
bail!(Error::Usage(format!("logging is already initialized")));
bail!(Error::Usage("logging is already initialized".to_string()));
}
let subscriber = tracing_subscriber::fmt::Subscriber::builder()
.with_level(display_level)
@ -222,7 +199,7 @@ impl Logger {
}
}
INITIALIZED.store(true, Ordering::Relaxed);
Ok(())
Ok(Logger {})
}
/// ## logging at [`Level::ERROR`]
@ -262,7 +239,6 @@ impl Logger {
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl fmt::Debug for Logger {
/// ## DEBUG representation for [`Logger`]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -274,10 +250,7 @@ impl fmt::Debug for Logger {
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
fn new_file_appender(log_dir: PathBuf) -> NonBlocking {
let file_appender = tracing_appender::rolling::daily(log_dir.clone(), "log");
return tracing_appender::non_blocking(file_appender).0;
tracing_appender::non_blocking(file_appender).0
}

View File

@ -1,21 +0,0 @@
[package]
name = "libpt-math"
publish.workspace = true
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
description.workspace = true
readme.workspace = true
homepage.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
num = "0.4.1"
num-traits = "0.2.16"
libpt-core = { workspace = true }
libpt-log = { workspace = true }

View File

@ -1,41 +0,0 @@
//! # Calculate expressions
//!
//! This crate is part of [`pt`](../libpt/index.html), but can also be used as a standalone
//! module.
//!
//! Calculate Calculations with your Calculator (`ccc`)
//!
//! This modules aim is to take a term of any kind ([String]) and calculate it's value, be it
//! variable based or a concrete numerical value. It implements different operators and
//! (mathematical) functions.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -1,38 +0,0 @@
//! # General Mathmatics functionalities
//!
//! This crate is part of [`pt`](../libpt/index.html), but can also be used as a standalone
//! module.
//!
//! This module is currently empty, but will contain many math functionalities in a future version.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
pub mod ccc;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -1,24 +0,0 @@
[package]
name = "libpt-net"
publish.workspace = true
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
description.workspace = true
readme.workspace = true
homepage.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
humantime = "2.1.0"
libpt-core = { workspace = true }
libpt-log = { workspace = true }
libpt-math = { workspace = true }
reqwest = { version = "0.11.20", features = ["blocking"] }
serde = { version = "1.0.188", features = ["serde_derive"] }
serde_json = "1.0.107"

View File

@ -1,40 +0,0 @@
//! # various networking tools
//!
//! This crate is part of [`pt`](../libpt/index.html), but can also be used as a standalone
//! module.
//!
//! The networking module contains various tools related to connections. For example, it contains a
//! tool that has the purpose to check if your connection is consistently available.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
/// monitor your connection
pub mod monitoring;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// how long to wait for a remove server to respond in ms
pub const DEFAULT_REQUEST_TIMEOUT: u64 = 2000;
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View File

@ -1,33 +0,0 @@
//! # monitor your network
//!
//! This module offers functions to monitor your network.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
pub mod uptime;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View File

@ -1,275 +0,0 @@
//! # monitor your network uptime
//!
//! This method offers a way to monitor your networks/hosts uptime. This is achieved by making
//! HTTPS requests to a given list of
//!
//! Warning: This module is not unit tested.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
use std::{fmt, time::Duration};
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use libpt_log::*;
use reqwest;
use humantime::{format_duration, format_rfc3339};
use std::time::SystemTime;
use serde::{Deserialize, Serialize};
use serde_json;
use libpt_core::divider;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// urls used for checking by default
pub const DEFAULT_CHECK_URLS: &'static [&'static str] =
&["https://www.cscherr.de", "https://www.cloudflare.com"];
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// ## Describes an uptime status
///
/// [`UptimeStatus`] describes the result of an uptime check.
#[derive(Serialize, Deserialize)]
pub struct UptimeStatus {
/// true if the [`UptimeStatus`] is considered successful
pub success: bool,
/// the percentage of reachable urls out of the total urls
pub success_ratio: u8,
/// the percentage of reachable urls out of the total urls that need to be reachable in order
/// for this [`UptimeStatus`] to be considered a success.
pub success_ratio_target: u8,
/// the number of reachable [`urls`](UptimeStatus::urls)
pub reachable: usize,
/// which urls to check in [`check()`](UptimeStatus::check)
pub urls: Vec<String>,
/// timeout length for requests (in ms)
pub timeout: u64,
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
/// Main implementation
impl UptimeStatus {
/// ## create a new `UptimeStatus` and perform it's check
pub fn new(success_ratio_target: u8, urls: Vec<String>, timeout: u64) -> Self {
assert!(success_ratio_target <= 100);
let mut status = UptimeStatus {
success: false,
success_ratio: 0,
success_ratio_target,
reachable: 0,
urls,
timeout,
};
status.urls.dedup();
status.check();
return status;
}
/// ## check for success with the given urls
///
/// Makes the actual https requests and updates fields accordingly.
///
/// Note: Blocking execution for all requests, timeout is set to
/// [REQUEST_TIMEOUT](crate::networking::REQUEST_TIMEOUT).
pub fn check(&mut self) {
self.reachable = 0;
self.urls.iter().for_each(|url| {
let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(self.timeout))
.build()
.expect("could not build a client for https requests");
let response = client.get(url.clone()).send();
if response.is_ok() {
self.reachable += 1
}
});
self.calc_success();
}
/// ## calculate the success based on the `reachable` and `total`
///
/// Calculates the ratio of [`reachable`](UptimeStatus::reachable) /
/// (length of [urls](UptimeStatus::urls)).
///
/// Calculates a [`success_ratio`](UptimeStatus::success_ratio) (as [u8]) from that,
/// by multiplying with 100, then flooring.
///
/// If the [`success_ratio`](UptimeStatus::success_ratio) is greater than or equal to the
/// [`success_ratio_target`](UptimeStatus::success_ratio_target), the [`UptimeStatus`] will be
/// considered a success.
///
/// In the special case that no URLs to check for have been provided, the check will be
/// considered a success, but the [`success_ratio`](UptimeStatus::success_ratio) will be `0`.
///
/// Note: does not check for networking, use [`check()`](UptimeStatus::check) for that.
pub fn calc_success(&mut self) {
// if no urls need to be checked, success without checking
if self.urls.len() == 0 {
self.success = true;
self.success_ratio = 0;
return;
}
let ratio: f32 = (self.reachable as f32) / (self.urls.len() as f32) * 100f32;
trace!("calculated success_ratio: {}", ratio);
self.success_ratio = ratio.floor() as u8;
self.success = self.success_ratio >= self.success_ratio_target;
trace!("calculated success as: {}", self.success)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl fmt::Debug for UptimeStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut urls: Vec<&str> = Vec::new();
for url in &self.urls {
urls.push(url.as_str());
}
write!(f, "{}", serde_json::to_string(self).unwrap())
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl fmt::Display for UptimeStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut urls: Vec<&str> = Vec::new();
for url in &self.urls {
urls.push(url.as_str());
}
write!(f, "{}", serde_json::to_string_pretty(self).unwrap())
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
/// ## Uptime monitor
///
/// This function continuously monitors the uptime of your host/network.
///
/// On change of status, an update will be logged at [INFO Level](log::Level::Info), containing
/// information on your current status, including timestamps of the last up/down time and durations
/// since.
pub fn continuous_uptime_monitor(
success_ratio_target: u8,
urls: Vec<String>,
interval: u64,
timeout: u64,
) {
if urls.len() == 0 {
error!("No URLs provided. There is nothing to monitor.");
return;
}
let interval = std::time::Duration::from_millis(interval);
let mut last_downtime: Option<SystemTime> = None;
let mut last_uptime: Option<SystemTime> = None;
let mut status = UptimeStatus::new(success_ratio_target, urls, timeout);
// we assume that the last status was up, so the binary shows the first status if its a
// failure.
let mut last_was_up: bool = true;
let mut last_ratio: u8 = status.success_ratio;
loop {
trace!(
?status,
?last_was_up,
"loop iteration for continuous uptime monitor"
);
if !status.success {
if last_was_up {
trace!("displaying status");
display_uptime_status("fail", last_uptime, last_downtime, &status)
}
last_downtime = Some(SystemTime::now());
last_was_up = false;
} else if status.success_ratio < 100 {
if status.success_ratio != last_ratio {
let msg = format!(
"uptime check: not all urls are reachable ({}%)",
status.success_ratio
);
display_uptime_status(&msg, last_uptime, last_downtime, &status)
}
last_uptime = Some(SystemTime::now());
last_was_up = true;
} else {
if !last_was_up {
display_uptime_status("success", last_uptime, last_downtime, &status)
}
last_uptime = Some(SystemTime::now());
last_was_up = true;
}
last_ratio = status.success_ratio;
std::thread::sleep(interval);
status.check();
}
}
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
/// Displays the current status for the [continuous uptime monitor](continuous_uptime_monitor)
fn display_uptime_status(
msg: &str,
last_uptime: Option<SystemTime>,
last_downtime: Option<SystemTime>,
status: &UptimeStatus,
) {
// I know it's weird that this has two spaces too much, but somehow just the tabs is missing
// two spaces.
info!("uptime check: {}", msg);
info!("last uptime: {}", match_format_time(last_uptime));
info!("last downtime: {}", match_format_time(last_downtime));
info!(
"since downtime: {}",
match_format_duration_since(last_downtime)
);
info!(
"since uptime: {}",
match_format_duration_since(last_uptime)
);
debug!("\n{}", status);
info!("{}", divider!());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Returns "None" if the given [Option] is [None](Option::None). Otherwise, returns the time stamp
/// formatted according to rfc3999.
fn match_format_time(time: Option<SystemTime>) -> String {
match time {
Some(time) => format_rfc3339(time).to_string(),
None => String::from("None"),
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Returns "None" if the given [Option] is [None](Option::None). Otherwise, returns duration since
/// that time in a human readable format.
fn match_format_duration_since(time: Option<SystemTime>) -> String {
match time {
Some(time) => format_duration(
SystemTime::now()
.duration_since(time)
.expect("could not calculate elapsed time"),
)
.to_string(),
None => String::from("None"),
}
}

72
members/libpt-py/.gitignore vendored Normal file
View File

@ -0,0 +1,72 @@
/target
# Byte-compiled / optimized / DLL files
__pycache__/
.pytest_cache/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
.venv/
env/
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
include/
man/
venv/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
pip-selfcheck.json
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
.DS_Store
# Sphinx documentation
docs/_build/
# PyCharm
.idea/
# VSCode
.vscode/
# Pyenv
.python-version

View File

@ -0,0 +1,23 @@
[package]
name = "libpt-py"
version.workspace = true
edition.workspace = true
[package.metadata.maturin]
name = "libpt"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
libpt = { version = "0.4.0-alpha.1", path = "../.." }
pyo3 = { version = "0.19.0", features = ["full"] }
anyhow.workspace = true
[features]
default = ["log", "core", "full"]
core = []
full = ["default", "core", "log", "bintols"]
log = ["libpt/log"]
bintols = ["libpt/bintols", "log"]

View File

@ -0,0 +1,16 @@
[build-system]
requires = ["maturin>=1.4,<2.0"]
build-backend = "maturin"
[project]
name = "libpt"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dynamic = ["version"]
[tool.maturin]
features = ["pyo3/extension-module"]

View File

@ -0,0 +1,56 @@
use pyo3::prelude::*;
use libpt::bintols as origin;
mod display {
use pyo3::prelude::*;
use libpt::bintols::display as origin;
#[pyfunction]
pub fn bytes_to_bin(data: &[u8]) -> String {
origin::bytes_to_bin(data)
}
#[pyfunction]
pub fn byte_bit_display(data: usize) -> String {
origin::byte_bit_display(data)
}
#[pyfunction]
pub fn humanbytes(total: u128) -> String {
origin::humanbytes(total)
}
/// implement a python module in Rust
pub fn submodule(py: Python, parent: &PyModule) -> PyResult<()> {
let module = PyModule::new(py, "display")?;
module.add_function(wrap_pyfunction!(bytes_to_bin, module)?)?;
module.add_function(wrap_pyfunction!(byte_bit_display, module)?)?;
module.add_function(wrap_pyfunction!(humanbytes, module)?)?;
parent.add_submodule(module)?;
Ok(())
}
}
/// implement a python module in Rust
pub fn submodule(py: Python, parent: &PyModule) -> PyResult<()> {
let module = PyModule::new(py, "bintols")?;
// binary constants
module.add("KIBI", origin::KIBI)?;
module.add("MEBI", origin::MEBI)?;
module.add("GIBI", origin::GIBI)?;
module.add("TEBI", origin::TEBI)?;
module.add("PEBI", origin::PEBI)?;
module.add("EXBI", origin::EXBI)?;
module.add("ZEBI", origin::ZEBI)?;
module.add("YOBI", origin::YOBI)?;
display::submodule(py, module)?;
parent.add_submodule(module)?;
Ok(())
}

View File

@ -0,0 +1,11 @@
use pyo3::prelude::*;
mod printing;
/// implement a python module in Rust
pub fn submodule(py: Python, parent: &PyModule) -> PyResult<()> {
let module = PyModule::new(py, "core")?;
printing::submodule(py, module)?;
parent.add_submodule(module)?;
Ok(())
}

View File

@ -0,0 +1,24 @@
use pyo3::prelude::*;
use libpt::core::printing as origin;
/// Quickly get a one line visual divider
#[pyfunction]
pub fn divider() -> String {
origin::divider()
}
/// Quickly print a one line visual divider
#[pyfunction]
pub fn print_divider() {
origin::print_divider()
}
/// implement a python module in Rust
pub fn submodule(py: Python, parent: &PyModule) -> PyResult<()> {
let module = PyModule::new(py, "printing")?;
module.add_function(wrap_pyfunction!(divider, module)?)?;
module.add_function(wrap_pyfunction!(print_divider, module)?)?;
parent.add_submodule(module)?;
Ok(())
}

View File

@ -0,0 +1,30 @@
//! Python bindings for [`libpt`](libpt)
#[cfg(feature = "bintols")]
mod bintols;
#[cfg(feature = "core")]
mod core;
#[cfg(feature = "log")]
mod log;
use pyo3::prelude::*;
/// return the version of libpt
#[pyfunction]
fn version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
/// implement a python module in Rust
#[pymodule]
#[pyo3(name = "libpt")]
fn libpt_py(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(version, m)?)?;
#[cfg(feature = "core")]
core::submodule(py, m)?;
#[cfg(feature = "log")]
log::submodule(py, m)?;
#[cfg(feature = "bintols")]
bintols::submodule(py, m)?;
Ok(())
}

View File

@ -0,0 +1,81 @@
use std::path::PathBuf;
use pyo3::prelude::*;
use libpt::log as origin;
#[derive(Clone)]
#[pyclass]
pub enum Level {
Error,
Warn,
Info,
Debug,
Trace,
}
impl From<Level> for origin::Level {
fn from(value: Level) -> Self {
match value {
Level::Error => origin::Level::ERROR,
Level::Warn => origin::Level::WARN,
Level::Info => origin::Level::INFO,
Level::Debug => origin::Level::DEBUG,
Level::Trace => origin::Level::TRACE,
}
}
}
#[pyclass]
pub struct Logger {
inner: origin::Logger,
}
impl From<origin::Logger> for Logger {
fn from(inner: origin::Logger) -> Self {
Self { inner }
}
}
#[pymethods]
impl Logger {
#[new]
pub fn build(
log_dir: Option<PathBuf>,
max_level: Option<Level>,
uptime: Option<bool>,
) -> anyhow::Result<Self> {
// concert our wrapper type
let max_level = max_level.map(origin::Level::from);
Ok(origin::Logger::build(log_dir, max_level, uptime.unwrap_or(false))?.into())
}
/// ## logging at [`Level::ERROR`]
pub fn error(&self, printable: String) {
self.inner.error(printable)
}
/// ## logging at [`Level::WARN`]
pub fn warn(&self, printable: String) {
self.inner.warn(printable)
}
/// ## logging at [`Level::INFO`]
pub fn info(&self, printable: String) {
self.inner.info(printable)
}
/// ## logging at [`Level::DEBUG`]
pub fn debug(&self, printable: String) {
self.inner.debug(printable)
}
/// ## logging at [`Level::StringRACE`]
pub fn trace(&self, printable: String) {
self.inner.trace(printable)
}
}
/// implement a python module in Rust
pub fn submodule(py: Python, parent: &PyModule) -> PyResult<()> {
let module = PyModule::new(py, "log")?;
module.add_class::<Logger>()?;
parent.add_submodule(module)?;
Ok(())
}

View File

View File

@ -7,7 +7,7 @@ BODY="
$(git log $(git describe --tags --abbrev=0)..HEAD --pretty="- %s" --oneline --decorate)
"
USER=PlexSheep
git tag "v$NEW_VERSION-test" || echo "could not tag"
git tag "v$NEW_VERSION" || echo "could not tag"
curl -X 'POST' \
'https://git.cscherr.de/api/v1/repos/PlexSheep/pt/releases' \
-H 'accept: application/json' \

View File

@ -1,104 +0,0 @@
//! # Executable for the math/compute submodule
//!
//! Calculate Calculations with your Computer!
//!
//! This command line tool allows you to input a mathematical expression. It will then process the
//! expression.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use libpt::log::*;
use clap::Parser;
use clap_verbosity_flag::{InfoLevel, Verbosity};
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// short about section displayed in help
const ABOUT_ROOT: &'static str = r##"
Calculate Calculations with your Computer
This commandline tool allows you to calculate complex mathematical expressions right in your
shell.
"##;
/// longer about section displayed in help, is combined with [the short help](ABOUT_ROOT)
static LONG_ABOUT_ROOT: &'static str = r##"
libpt is a personal general purpose library, offering this executable, a python module and a
dynamic library.
"##;
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
/// defines CLI interface
#[derive(Debug, Clone, Parser)]
#[command(
author,
version,
about = ABOUT_ROOT,
long_about = format!("{}{}", ABOUT_ROOT ,LONG_ABOUT_ROOT),
help_template =
r#"libpt: {version}{about-section}Author:
{author-with-newline}
{usage-heading} {usage}{all-args}{tab}"#
)]
pub struct Cli {
// clap_verbosity_flag seems to make this a global option implicitly
/// set a verbosity, multiple allowed (f.e. -vvv)
#[command(flatten)]
pub verbose: Verbosity<InfoLevel>,
/// show logger meta
#[arg(short, long, global = true)]
pub log_meta: bool,
/// your exporession(s)
#[clap(trailing_var_arg = true)]
pub expression: Vec<String>,
}
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
fn main() {
let cli = Cli::parse();
let ll: Level = match cli.verbose.log_level().unwrap().as_str() {
"TRACE" => Level::TRACE,
"DEBUG" => Level::DEBUG,
"INFO" => Level::INFO,
"WARN" => Level::WARN,
"ERROR" => Level::ERROR,
_ => {
eprintln!("'{}' is not a valid loglevel", cli.verbose.to_string());
std::process::exit(1);
}
};
if cli.log_meta {
Logger::init(None, Some(ll), false).expect("could not initialize Logger");
} else {
// less verbose version
Logger::init_mini(Some(ll)).expect("could not initialize Logger");
}
let mut expr: String = String::new();
for part in cli.expression {
expr += &part;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -1,174 +0,0 @@
//! # Executable for the hedu submodule
//!
//! Dump data to a fancy format.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use libpt::{bintols::hedu::*, log::*};
use clap::Parser;
use clap_verbosity_flag::{InfoLevel, Verbosity};
use std::{fs::File, io::IsTerminal, path::PathBuf};
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// short about section displayed in help
const ABOUT_ROOT: &'static str = r##"
Dumps data in fancy formats.
"##;
/// longer about section displayed in help, is combined with [the short help](ABOUT_ROOT)
static LONG_ABOUT_ROOT: &'static str = r##"
libpt is a personal general purpose library, offering this executable, a python module and a
dynamic library.
"##;
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// defines CLI interface
#[derive(Debug, Clone, Parser)]
#[command(
author,
version,
about = ABOUT_ROOT,
long_about = format!("{}{}", ABOUT_ROOT ,LONG_ABOUT_ROOT),
help_template =
r#"{about-section}
{usage-heading} {usage}
{all-args}{tab}
libpt: {version}
Author: {author-with-newline}
"#
)]
pub struct Cli {
// clap_verbosity_flag seems to make this a global option implicitly
/// set a verbosity, multiple allowed (f.e. -vvv)
#[command(flatten)]
pub verbose: Verbosity<InfoLevel>,
/// show additional logging meta data
#[arg(long)]
pub meta: bool,
/// show character representation
#[arg(short, long)]
pub chars: bool,
/// skip first N bytes
#[arg(short, long, default_value_t = 0)]
pub skip: usize,
/// only interpret N bytes (end after N)
#[arg(short, long, default_value_t = 0)]
pub limit: usize,
/// show identical lines
#[arg(short = 'i', long)]
pub show_identical: bool,
/// a data source, probably a file.
///
/// If left empty or set as "-", the program will read from stdin.
// TODO: take many sources #60
pub data_source: Vec<String>,
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
fn main() {
let mut cli = cli_parse();
let mut sources: Vec<Box<dyn DataSource>> = Vec::new();
if cli.data_source.len() > 0 && cli.data_source[0] != "-" {
for data_source in &cli.data_source {
let data_source: PathBuf = PathBuf::from(data_source);
if data_source.is_dir() {
warn!("Not a file {:?}, skipping", data_source);
// std::process::exit(1);
continue;
}
trace!("Trying to open '{:?}'", data_source);
match File::open(&data_source) {
Ok(file) => sources.push(Box::new(file)),
Err(err) => {
error!("Could not open '{:?}': {err}", data_source);
std::process::exit(1);
}
};
}
} else {
trace!("Trying to open stdin");
let stdin = std::io::stdin();
if stdin.is_terminal() {
warn!("Refusing to dump from interactive terminal");
std::process::exit(2)
}
// just for the little header
cli.data_source = Vec::new();
cli.data_source.push(format!("stdin"));
sources.push(Box::new(stdin));
}
for (i, source) in sources.iter_mut().enumerate() {
let mut config = Hedu::new(cli.chars, cli.skip, cli.show_identical, cli.limit);
// FIXME: find a better way to get the file name
// Currently, skipped sources make an extra newline here.
match config.chars {
false => {
println!("{:─^59}", format!(" {} ", cli.data_source[i]));
}
true => {
println!("{:─^80}", format!(" {} ", cli.data_source[i]));
}
}
match config.dump(&mut **source) {
Ok(_) => (),
Err(err) => {
error!("Could not dump data of file: {err}");
std::process::exit(3);
}
}
if i < cli.data_source.len() - 1 {
config.newline();
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
fn cli_parse() -> Cli {
let cli = Cli::parse();
let ll: Level = match cli.verbose.log_level().unwrap().as_str() {
"TRACE" => Level::TRACE,
"DEBUG" => Level::DEBUG,
"INFO" => Level::INFO,
"WARN" => Level::WARN,
"ERROR" => Level::ERROR,
_ => {
unreachable!();
}
};
if cli.meta {
Logger::init(None, Some(ll), false).expect("could not initialize Logger");
} else {
// less verbose version
Logger::init_mini(Some(ll)).expect("could not initialize Logger");
}
return cli;
}

View File

@ -8,13 +8,9 @@
#[cfg(feature = "bintols")]
pub use libpt_bintols as bintols;
#[cfg(feature = "ccc")]
pub use libpt_ccc as ccc;
#[cfg(feature = "core")]
pub use libpt_core as core;
#[cfg(feature = "log")]
pub use libpt_log as log;
#[cfg(feature = "math")]
pub use libpt_math as math;
#[cfg(feature = "net")]
pub use libpt_net as net;
#[cfg(feature = "py")]
pub use libpt_py as py;