Compare commits

..

No commits in common. "edcec991dc1b31ac4d269acf217d3451b10e741c" and "a63262825e241003798eaca7b57ec8bcd18ac597" have entirely different histories.

50 changed files with 1576 additions and 645 deletions

View file

@ -1,22 +0,0 @@
name: Cargo Check, Format, Fix, Test and publish
on:
push:
branches:
- master
jobs:
format:
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:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --all-features --all-targets
- run: rustup component add rustfmt
- run: cargo fix --all-features --all-targets
- run: cargo fmt --all
- run: cargo test --all-features --all-targets
- run: cargo publish --index "https://git.cscherr.de/PlexSheep/_cargo-index.git --dry-run

View file

@ -1,27 +0,0 @@
name: Cargo Check, Format, Fix and Test
on: [push, pull_request]
jobs:
format:
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:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --all-features --all-targets
- run: rustup component add rustfmt
- run: cargo fix --all-features --all-targets
- run: cargo fmt --all
- run: cargo test --all-features --all-targets
- 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
commit_user_name: Gitea CI
commit_user_email: noreply@cscherr.de
commit_author: Gitea CI <noreply@cscherr.de>

120
.github/workflows/CI.yml vendored Normal file
View file

@ -0,0 +1,120 @@
# This file is autogenerated by maturin v0.14.17
# 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: --skip-existing *

View file

@ -1,19 +1,27 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ members = [
".", ".",
"members/libpt-core", "members/libpt-core",
"members/libpt-bintols", "members/libpt-bintols",
"members/libpt-math", "members/libpt-math",
"members/libpt-bin", "members/libpt-bin",
"members/libpt-log", "members/libpt-log",
"members/libpt-net", "members/libpt-net",
"members/libpt-hedu", "members/libpt-py",
"members/libpt-hedu",
]
default-members = [
".",
"members/libpt-bin",
"members/libpt-core",
"members/libpt-py",
"members/libpt-log",
"members/libpt-math",
] ]
default-members = [".", "members/libpt-bin", "members/libpt-core"]
[workspace.package] [workspace.package]
publish = true publish = false
version = "0.2.4" version = "0.1.7"
edition = "2021" edition = "2021"
authors = ["Christoph J. Scherr <software@cscherr.de>"] authors = ["Christoph J. Scherr <software@cscherr.de>"]
license = "MIT" license = "MIT"
@ -21,20 +29,15 @@ description = "Personal multitool"
readme = "README.md" readme = "README.md"
homepage = "https://git.cscherr.de/PlexSheep/libpt" homepage = "https://git.cscherr.de/PlexSheep/libpt"
repository = "https://git.cscherr.de/PlexSheep/libpt" repository = "https://git.cscherr.de/PlexSheep/libpt"
keywords = ["cli", "library"] keywords = ["cli", "python", "scriptable", "pyo3", "library"]
categories = [ categories = ["command-line-utilities", "development-tools", "development-tools::ffi"]
"command-line-utilities",
"development-tools",
"development-tools::ffi",
]
[workspace.dependencies] [workspace.dependencies]
anyhow = "1.0.79" pyo3 = "0.19"
thiserror = "1.0.56"
[package] [package]
name = "libpt" name = "libpt"
publish.workspace = true publish = true
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
authors.workspace = true authors.workspace = true
@ -47,32 +50,39 @@ keywords.workspace = true
categories.workspace = true categories.workspace = true
[features] [features]
default = ["log", "core"] default = ["log"]
all = ["default", "math", "log", "bintols", "net", "ccc", "hedu", "core"] all = [
core = [] "default",
fw = [] "math",
math = [] "log",
log = [] "bintols",
"net",
"ccc",
"hedu"
]
fw = []
math = []
log = []
bintols = [] bintols = []
net = [] net = []
ccc = ["math"] ccc = ["math"]
hedu = ["bintols"] hedu = ["bintols"]
[lib] [lib]
name = "libpt" name = "libpt"
crate-type = [ crate-type = [
"dylib", # .dll, .so, .dynlib "dylib", # .dll, .so, .dynlib
"staticlib", # .lib, .a "staticlib", # .lib, .a
"rlib", "rlib"
] ]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
libpt-bintols = { path = "members/libpt-bintols" } libpt-bintols = { version = "0.1.7", path = "members/libpt-bintols" }
libpt-core = { path = "members/libpt-core" } libpt-core = { version = "0.1.7", path = "members/libpt-core" }
libpt-hedu = { path = "members/libpt-hedu" } libpt-hedu = { version = "0.1.7", path = "members/libpt-hedu" }
libpt-log = { path = "members/libpt-log" } libpt-log = { version = "0.1.7", path = "members/libpt-log" }
libpt-math = { path = "members/libpt-math" } libpt-math = { version = "0.1.7", path = "members/libpt-math" }
libpt-ccc = { path = "members/libpt-ccc" } libpt-net = { version = "0.1.7", path = "members/libpt-net" }
libpt-net = { path = "members/libpt-net" } libpt-ccc = { version = "0.1.7", path = "members/libpt-ccc" }

Binary file not shown.

Binary file not shown.

View file

@ -19,10 +19,6 @@ categories.workspace = true
name = "ccc" name = "ccc"
path = "src/ccc/mod.rs" path = "src/ccc/mod.rs"
[[bin]]
name = "hedu"
path = "src/hedu/mod.rs"
[[bin]] [[bin]]
name = "libpt" name = "libpt"
path = "src/main/mod.rs" path = "src/main/mod.rs"
@ -31,4 +27,4 @@ path = "src/main/mod.rs"
clap = { version = "4.4.4", features = ["derive"] } clap = { version = "4.4.4", features = ["derive"] }
clap-num = "1.0.2" clap-num = "1.0.2"
clap-verbosity-flag = "2.0.1" clap-verbosity-flag = "2.0.1"
libpt = {path = "../..", features = ["default", "ccc", "math", "hedu", "net", "log"] } libpt = { version = "0.1.7", path = "../..", features = ["ccc", "math", "hedu", "net"] }

View file

@ -15,11 +15,13 @@
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use libpt::ccc::*;
use libpt::log::*; use libpt::log::*;
use clap::Parser; use clap::Parser;
use clap_verbosity_flag::{InfoLevel, Verbosity}; use clap_verbosity_flag::{Verbosity, InfoLevel};
use std::path::PathBuf;
//// TYPES ///////////////////////////////////////////////////////////////////////////////////////// //// TYPES /////////////////////////////////////////////////////////////////////////////////////////
@ -62,7 +64,7 @@ pub struct Cli {
pub log_meta: bool, pub log_meta: bool,
/// your exporession(s) /// your exporession(s)
#[clap(trailing_var_arg = true)] #[clap(trailing_var_arg=true)]
pub expression: Vec<String>, pub expression: Vec<String>,
} }
@ -91,14 +93,49 @@ fn main() {
} }
}; };
if cli.log_meta { if cli.log_meta {
Logger::init(None, Some(ll)).expect("could not initialize Logger"); Logger::init_customized(
false,
PathBuf::from("/dev/null"),
true,
false,
true,
true,
ll,
false,
false,
false,
)
.expect("could not initialize Logger");
} else { } else {
// less verbose version // less verbose version
Logger::init_mini(Some(ll)).expect("could not initialize Logger"); Logger::init_customized(
false,
PathBuf::from("/dev/null"),
true,
false,
true,
false,
ll,
false,
false,
false,
)
.expect("could not initialize Logger");
} }
let mut expr: String = String::new(); let mut expr: String = String::new();
for part in cli.expression { for part in cli.expression {
expr += &part; expr += &part;
} }
debug!("exporssion: {}", expr);
let r = Calculator::oneshot(expr);
match r {
Ok(r) => {
println!("{r}");
}
Err(err) => {
error!("Could not compute: {err}");
}
}
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -1,151 +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::{hedu::*, log::*};
use clap::Parser;
use clap_verbosity_flag::{InfoLevel, Verbosity};
use std::{fs::File, io::IsTerminal};
//// 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.
pub data_source: Option<String>,
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
fn main() {
let cli = cli_parse();
let mut source: Box<dyn DataSource>;
if cli.data_source.is_some() && cli.data_source.clone().is_some_and(|val| val != "-") {
let data_source = cli.data_source.unwrap();
trace!("Trying to open '{}'", data_source);
source = match File::open(&data_source) {
Ok(file) => Box::new(file),
Err(err) => {
error!("Could not open file '{}': {err}", data_source);
std::process::exit(1);
}
};
} else {
trace!("Trying to open stdout");
let stdin = std::io::stdin();
if stdin.is_terminal() {
warn!("Refusing to dump from interactive terminal");
std::process::exit(2)
}
source = Box::new(stdin);
}
match dump(
&mut *source,
HeduConfig::new(cli.chars, cli.skip, cli.show_identical, cli.limit),
) {
Ok(_) => (),
Err(err) => {
error!("Could not dump data of file: {err}");
std::process::exit(3);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
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)).expect("could not initialize Logger");
} else {
// less verbose version
Logger::init_mini(Some(ll)).expect("could not initialize Logger");
}
return cli;
}

View file

@ -19,7 +19,7 @@ use clap::{Parser, Subcommand};
use clap_num::number_range; use clap_num::number_range;
use clap_verbosity_flag::{InfoLevel, Verbosity}; use clap_verbosity_flag::{Verbosity, InfoLevel};
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// short about section displayed in help /// short about section displayed in help
@ -103,10 +103,13 @@ pub enum NetCommands {
/// set a timeout (in ms) /// set a timeout (in ms)
#[clap(short, long, default_value_t = 100)] #[clap(short, long, default_value_t = 100)]
timeout: u64, timeout: u64
}, },
/// discover hosts in your network /// discover hosts in your network
Discover {}, Discover {
}
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -13,6 +13,7 @@
// enable clippy's extra lints, the pedantic version // enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use libpt::{log::*, net::monitoring::uptime}; use libpt::{log::*, net::monitoring::uptime};
@ -21,8 +22,11 @@ use clap::Parser;
pub mod args; pub mod args;
use args::*; use args::*;
use std::path::PathBuf;
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
#[allow(dead_code)] #[allow(dead_code)]
//// STATICS /////////////////////////////////////////////////////////////////////////////////////// //// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS //////////////////////////////////////////////////////////////////////////////////////// //// MACROS ////////////////////////////////////////////////////////////////////////////////////////
@ -49,10 +53,34 @@ pub fn main() {
} }
}; };
if cli.log_meta { if cli.log_meta {
Logger::init(None, Some(ll)).expect("could not initialize Logger"); Logger::init_customized(
false,
PathBuf::from("/dev/null"),
true,
false,
true,
true,
ll,
false,
false,
false,
)
.expect("could not initialize Logger");
} else { } else {
// less verbose version // less verbose version
Logger::init_mini(Some(ll)).expect("could not initialize Logger"); Logger::init_customized(
false,
PathBuf::from("/dev/null"),
true,
false,
true,
false,
ll,
false,
false,
false,
)
.expect("could not initialize Logger");
} }
trace!("started the main function"); trace!("started the main function");

View file

@ -16,5 +16,5 @@ categories.workspace = true
[dependencies] [dependencies]
num-traits = "0.2.16" num-traits = "0.2.16"
libpt-core = { path = "../libpt-core" } libpt-core = { version = "0.1.0", path = "../libpt-core" }
libpt-log = { path = "../libpt-log" } libpt-log = { version = "0.1.0", path = "../libpt-log" }

View file

@ -20,8 +20,7 @@ macro_rules! investigate_memory_layout {
let pointer = item as *const $t; let pointer = item as *const $t;
let mut memory: [u8; std::mem::size_of::<$t>()] = std::mem::transmute(item.clone()); let mut memory: [u8; std::mem::size_of::<$t>()] = std::mem::transmute(item.clone());
memory.reverse(); memory.reverse();
println!( println!("\
"\
\t{index:02x}\titem:\t\t{item:?}\n\ \t{index:02x}\titem:\t\t{item:?}\n\
\t\tpointer: \t{:X?}\n\ \t\tpointer: \t{:X?}\n\
\t\talign: \t{}\n\ \t\talign: \t{}\n\

View file

@ -59,7 +59,8 @@ where
return format!("{:.2} Z", total.to_f64().unwrap() / ZEBI as f64); return format!("{:.2} Z", total.to_f64().unwrap() / ZEBI as f64);
} else if YOBI <= total { } else if YOBI <= total {
return format!("{:.2} Y", total.to_f64().unwrap() / YOBI as f64); return format!("{:.2} Y", total.to_f64().unwrap() / YOBI as f64);
} else { }
else {
unreachable!() unreachable!()
} }
} }

View file

@ -1,5 +1,5 @@
use libpt_bintols::display::*;
use libpt_bintols::*; use libpt_bintols::*;
use libpt_bintols::display::*;
#[test] #[test]
fn btobin() { fn btobin() {
@ -10,16 +10,11 @@ fn btobin() {
#[test] #[test]
fn big_btobin() { fn big_btobin() {
let data = [12, 31, 82, 32, 123, 32, 92, 23, 12, 32, 12, 1, 1, 1]; let data = [12,31,82,32,123,32,92,23,12,32,12,1,1,1];
let r = bytes_to_bin(&data); let r = bytes_to_bin(&data);
assert_eq!( assert_eq!(r, format!("0b00001100_00011111_01010010_\
r,
format!(
"0b00001100_00011111_01010010_\
00100000_01111011_00100000_01011100_00010111_00001100\n\ 00100000_01111011_00100000_01011100_00010111_00001100\n\
_00100000_00001100_00000001_00000001_00000001" _00100000_00001100_00000001_00000001_00000001"));
)
);
} }
#[test] #[test]
@ -31,38 +26,38 @@ fn bybit() {
#[test] #[test]
fn hmnbytes() { fn hmnbytes() {
assert_eq!(humanbytes(0), format!("0 B")); assert_eq!(humanbytes(0), format!("0 B"));
assert_eq!(humanbytes(1), format!("1 B")); assert_eq!(humanbytes(1), format!("1 B"));
assert_eq!(humanbytes(KIBI - 1), format!("1023 B")); assert_eq!(humanbytes(KIBI-1), format!("1023 B"));
assert_eq!(humanbytes(KIBI), format!("1.00 K")); assert_eq!(humanbytes(KIBI), format!("1.00 K"));
assert_eq!(humanbytes(KIBI + 1), format!("1.00 K")); assert_eq!(humanbytes(KIBI+1), format!("1.00 K"));
assert_eq!(humanbytes(MEBI - 1), format!("1024.00 K")); assert_eq!(humanbytes(MEBI-1), format!("1024.00 K"));
assert_eq!(humanbytes(MEBI), format!("1.00 M")); assert_eq!(humanbytes(MEBI), format!("1.00 M"));
assert_eq!(humanbytes(MEBI + 1), format!("1.00 M")); assert_eq!(humanbytes(MEBI+1), format!("1.00 M"));
assert_eq!(humanbytes(GIBI - 1), format!("1024.00 M")); assert_eq!(humanbytes(GIBI-1), format!("1024.00 M"));
assert_eq!(humanbytes(GIBI), format!("1.00 G")); assert_eq!(humanbytes(GIBI), format!("1.00 G"));
assert_eq!(humanbytes(GIBI + 1), format!("1.00 G")); assert_eq!(humanbytes(GIBI+1), format!("1.00 G"));
assert_eq!(humanbytes(TEBI - 1), format!("1024.00 G")); assert_eq!(humanbytes(TEBI-1), format!("1024.00 G"));
assert_eq!(humanbytes(TEBI), format!("1.00 T")); assert_eq!(humanbytes(TEBI), format!("1.00 T"));
assert_eq!(humanbytes(TEBI + 1), format!("1.00 T")); assert_eq!(humanbytes(TEBI+1), format!("1.00 T"));
assert_eq!(humanbytes(PEBI - 1), format!("1024.00 T")); assert_eq!(humanbytes(PEBI-1), format!("1024.00 T"));
assert_eq!(humanbytes(PEBI), format!("1.00 P")); assert_eq!(humanbytes(PEBI), format!("1.00 P"));
assert_eq!(humanbytes(PEBI + 1), format!("1.00 P")); assert_eq!(humanbytes(PEBI+1), format!("1.00 P"));
assert_eq!(humanbytes(EXBI - 1), format!("1024.00 P")); assert_eq!(humanbytes(EXBI-1), format!("1024.00 P"));
assert_eq!(humanbytes(EXBI), format!("1.00 E")); assert_eq!(humanbytes(EXBI), format!("1.00 E"));
assert_eq!(humanbytes(EXBI + 1), format!("1.00 E")); assert_eq!(humanbytes(EXBI+1), format!("1.00 E"));
assert_eq!(humanbytes(ZEBI - 1), format!("1024.00 E")); assert_eq!(humanbytes(ZEBI-1), format!("1024.00 E"));
assert_eq!(humanbytes(ZEBI), format!("1.00 Z")); assert_eq!(humanbytes(ZEBI), format!("1.00 Z"));
assert_eq!(humanbytes(ZEBI + 1), format!("1.00 Z")); assert_eq!(humanbytes(ZEBI+1), format!("1.00 Z"));
assert_eq!(humanbytes(YOBI - 1), format!("1024.00 Z")); assert_eq!(humanbytes(YOBI-1), format!("1024.00 Z"));
assert_eq!(humanbytes(YOBI), format!("1.00 Y")); assert_eq!(humanbytes(YOBI), format!("1.00 Y"));
assert_eq!(humanbytes(YOBI + 1), format!("1.00 Y")); assert_eq!(humanbytes(YOBI+1), format!("1.00 Y"));
} }

View file

@ -17,6 +17,6 @@ categories.workspace = true
[dependencies] [dependencies]
num = "0.4.1" num = "0.4.1"
num-traits = "0.2.16" num-traits = "0.2.16"
libpt-core = { path = "../libpt-core" } libpt-core = { version = "0.1.7", path = "../libpt-core" }
libpt-log = { path = "../libpt-log" } libpt-log = { version = "0.1.7", path = "../libpt-log" }
libpt-math = { path = "../libpt-math" } libpt-math = { version = "0.1.7", path = "../libpt-math" }

View file

@ -0,0 +1,225 @@
//! # Results and Errors for the calculate module
//!
//! This module defines the errors and results that can be processed from any given term.
//// 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 std::fmt::Display;
pub use num_traits::PrimInt;
#[allow(unused_imports)] // we possibly want to use all log levels
use libpt_log::*;
#[allow(unused_imports)] // import more complex math stuff from there
use libpt_math;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
/// Quick Result with a ccc error
pub type Result<T> = std::result::Result<T, Error>;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
/// ## Supported Operations
///
/// This `enum` contains all operations supported in this module.
#[non_exhaustive]
#[derive(Debug)]
pub enum Operator {
/// Mathmatical addition
Addition,
/// Mathmatical subtraction
Subtraction,
/// Mathmatical multiplication
Multiplication,
/// Mathmatical division
Division,
/// Mathmatical modulo, finite field arithmetic
Modulo,
/// Any function, seel [`Function`]
Function(Function)
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// ## Supported Functions
///
/// This `enum` contains all functions supported in this module.
///
/// A function has a name followed by braces directly afterwards.
/// A function may have 0 to 31 Arguments.
///
/// Example: `sqrt(19)`, `floor(19.9)`
#[non_exhaustive]
#[derive(Debug)]
pub enum Function {
/// Draw the mathmatical root, attribute n is the nth root
Root(u16),
/// round up
Floor,
/// round down
Ceil,
/// round to nearest integer
/// (commercial rounding)
Round,
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Top Level Error Type
///
/// Contains many variants of other errors, that can occur when using the crate.
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
/// The term has bad syntax
SyntaxError(String)
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Represents some kind of computed value
#[derive(Debug)]
pub enum Value {
/// Variable value
Variable(VarVal),
/// Numerical value
Numerical(NumVal),
/// Complex number value
Complex(ComplVal),
}
/// Represents some kind of numeric value
#[non_exhaustive]
#[derive(Debug)]
pub enum NumVal {
/// Value > 0
Signed(i128),
/// Value can be negative
Unsigned(u128),
/// Value is not an integer
Float(f64)
}
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// Represents a Value with at least one variable,
///
/// currently not implemented
#[derive(Debug)]
pub struct VarVal {
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Represents a Value with a complex number,
///
/// currently not implemented
#[derive(Debug)]
pub struct ComplVal {
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl<T: num_traits::PrimInt> From<T> for NumVal where
u128: TryFrom<T>,
u128: TryFrom<T> {
fn from(value: T) -> Self {
if T::min_value().is_zero() {
// unsigned data types
// `u128` is the largest unsigned datatype, any other type will fit.
NumVal::Unsigned(value.to_u128().unwrap())
}
else {
// signed data types
// `i128` is the largest unsigned datatype, any other type will fit.
NumVal::Signed(value.to_i128().unwrap())
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Display Errors with a nice little reason
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SyntaxError(reason) => {
write!(f, "Syntax Error: {}", reason)
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl<T: PrimInt> From<T> for Value where
u128: TryFrom<T>,
u128: TryFrom<T> {
fn from(value: T) -> Self {
NumVal::from(value).into()
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl From<NumVal> for Value {
fn from(value: NumVal) -> Self {
Value::Numerical(value)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Numerical(val) => {
write!(f, "{}", val)
}
Value::Complex(val) => {
write!(f, "{}", val)
}
Value::Variable(val) => {
write!(f, "{}", val)
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Display for NumVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NumVal::Float(val) => {
write!(f, "{val}")
}
NumVal::Signed(val) => {
write!(f, "{val}")
}
NumVal::Unsigned(val) => {
write!(f, "{val}")
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Display for ComplVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "")
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Display for VarVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "")
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -19,7 +19,14 @@
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use libpt_log; pub mod base;
pub use base::{Error, Result, Value};
pub mod term;
pub use term::*;
#[allow(unused_imports)] // we possibly want to use all log levels
use libpt_log::*;
#[allow(unused_imports)] // import more complex math stuff from there
use libpt_math; use libpt_math;
//// TYPES ///////////////////////////////////////////////////////////////////////////////////////// //// TYPES /////////////////////////////////////////////////////////////////////////////////////////
@ -33,8 +40,44 @@ use libpt_math;
//// ENUMS ///////////////////////////////////////////////////////////////////////////////////////// //// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS /////////////////////////////////////////////////////////////////////////////////////// //// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// ## A Calculator struct
///
/// This struct does not do anything at the moment, but aims to be the target for high level
/// control. Instead of using the [`Calculator`], you could just use the [`Term`] struct for
/// lower level control.
#[derive(Debug)]
pub struct Calculator;
//// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// //// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl Calculator {
/// Do a single calculation without doing anything else
pub fn oneshot(t: String) -> Result<Value> {
trace!(orig=t, "parsing original string to Term");
let t = Term::new(t)?;
trace!("term has been parsed, starting Calculation");
debug!("parsed term: {t:#?}");
Self::calc(t)
}
/// ## Calculate a [`Term`]
///
/// This method makes use of the
/// [shunting yard algorithm](https://en.wikipedia.org/wiki/Shunting_yard_algorithm) to
/// Calculate the final value of any term.
///
/// This method only processes a single term at a time, without caching.
pub fn calc(mut t: Term) -> Result<Value> {
trace!("Calculating term {t:?}");
t.prepare()?;
t.process()?;
if t.result.is_none() {
let reason = format!("Term was processed but no result was assigned.");
// FIXME: unfitting error type
return Err(Error::SyntaxError(reason))
}
return t.result.unwrap()
}
}
//// PUBLIC FUNCTIONS ////////////////////////////////////////////////////////////////////////////// //// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,223 @@
//! # A term that can be the input for calculation
//!
//! Short description
//!
//! Details
//!
//! ## Section 1
//!
//! ## Section 2
//// 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::collections::VecDeque;
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
pub use super::{Error, Result, Value, base::{self, *}};
#[allow(unused_imports)] // we possibly want to use all log levels
use libpt_log::*;
#[allow(unused_imports)] // import more complex math stuff from there
use libpt_math;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
/// ## Parsed value to be calculated
///
/// This enum represents anything that goes to the output queue of [`Term::prepare()`] and will
/// then be used to actually calculate something in [`Term::process()`].
#[derive(Debug)]
enum Token {
/// Some kind of operator
#[allow(unused)] // tmp
Operator(Operator),
/// A concrete value that we can calculate something with. May be a constant, integer, float,
/// etc.
/// The Token has a value that can be used in calculation
Value(super::base::Value),
/// A variable of some kind that will be present in the result
#[allow(unused)] // tmp
Variable(char),
}
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// ## Term that can be calculated
///
/// Represents a signular term, that can be calculated. Terms will be evaluated by the [`Term::prepare`]
/// function, afterwards calculated (as much as possible) in the [`Term::process`] function.
///
#[derive(Debug)]
pub struct Term {
/// the original expression to calculate
pub original: String,
/// the filtered text of the expression, only with relevant information
pub text: String,
/// the calculated result, may be of diffrent types, see [`crate::math::calculator::result`].
pub result: Option<Result<Value>>,
/////////////////////////////////////
///// internal values following /////
/////////////////////////////////////
#[allow(unused)] // tmp
operator_stack: Vec<Operator>,
output_queue: VecDeque<Token>
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl Term {
/// Build a new term from an expression
///
/// Invalid terms will result in an [`Err`].
pub fn new(orig: String) -> Result<Term> {
return Ok(
Term {
original: orig,
text: String::new(), // will be initialized in `prepare()`
result: None,
operator_stack: Vec::new(),
output_queue: VecDeque::new()
}
)
}
/// Prepare the term for the processing.
pub fn prepare(&mut self) -> Result<()> {
trace!("preparing term: {:#?}", self);
self.text = Self::filter(&self.original)?;
// Storage for unfinished tokens
let _unfinished_chars: Vec<char> = Vec::new();
for (_index, c) in self.original.chars().enumerate() {
// this will be a mess, but it has to be before i can sort the mess.
match c {
// TODO: make function to check if character is an operator, use it
_ => {
let reason = format!("The meaning of '{c}' could not be identified.");
warn!(reason);
return Err(Error::SyntaxError(reason));
}
}
}
Ok(())
}
/// Process a prepared term, calculating it's result
pub fn process(&mut self) -> Result<()> {
debug!("processing term: {:#?}", self);
debug!("queue: {:#?}", self.output_queue);
// TODO: process RPN and set result
self.result = Some(Ok(19.into()));
Ok(())
}
/// Convert a character into a token
///
/// Returns: A tuple with a [`Token`] and a [`bool`]. If the bool is false, the [`Token`] is
/// marked as "incomplete", meaning that the character cannot be used yet.
#[allow(unused)] // tmp
fn to_tok(_s: Vec<char>) -> Result<Token> {
Ok(19.into())
}
/// only leave relevant chars for calculation
// TODO: make function to check if character is an operator, use it
fn filter(s: &str) -> Result<String> {
// pre checks
// NOTE: Apperently, "alphanumeric" in Rust is a pretty broad term.
// Even CJK characters or Japanese Kana are allowed:
// - 'さ' alphanumeric
// - '数' alphanumeric
// - '学' alphanumeric
// - '+' not alphanumeric
for c in s.chars() {
#[cfg(debug_assertions)] {
debug!("filter checks for '{c}':
alphanumeric: {}
allowed special: {}
EXCEPT IF
ascii control: {}
",
!c.is_alphanumeric(),
!Self::is_allowed_special_c(&c),
c.is_ascii_control(),
)
}
if
(
!c.is_alphanumeric() ||
!Self::is_allowed_special_c(&c)
)
&&
(
c.is_ascii_control()
)
{
// TODO: allow any unicode char to be a variable
let reason = format!("'{c}' is not a valid character, only alphanumeric, punctuation, operators are allowed.");
warn!(reason);
return Err(Error::SyntaxError(reason));
}
}
// filter out single chars
let mut filtered = String::new();
for c in s.chars() {
if !Self::is_ignore(&c) {
filtered.push(c);
}
}
return Ok(filtered)
}
/// check if we should ignore this character
fn is_ignore(c: &char) -> bool {
match *c {
' ' => true,
_ => false
}
}
/// allowed special chars
fn is_allowed_special_c(c: &char) -> bool {
match *c {
'+' | '-' | '*' | '/' | '%' => true,
_ => false
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Helper methods for Tokens
impl Token { }
////////////////////////////////////////////////////////////////////////////////////////////////////
impl<T> From<T> for Token where
T: Into<Value>,
T: PrimInt,
u128: TryFrom<T>
{
fn from(value: T) -> Self {
Token::Value(base::Value::from(value))
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -13,6 +13,3 @@ keywords.workspace = true
categories.workspace = true categories.workspace = true
[dependencies] [dependencies]
anyhow = "1.0.79"
cucumber = "0.20.2"
libpt-log = {path = "../libpt-log" }

View file

@ -10,9 +10,11 @@
// we want docs // we want docs
#![warn(missing_docs)] #![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)] #![warn(rustdoc::missing_crate_level_docs)]
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere. // we want Debug everywhere.
#![warn(missing_debug_implementations)] #![warn(missing_debug_implementations)]
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version // enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]

View file

@ -6,9 +6,11 @@
// we want docs // we want docs
#![warn(missing_docs)] #![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)] #![warn(rustdoc::missing_crate_level_docs)]
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere. // we want Debug everywhere.
#![warn(missing_debug_implementations)] #![warn(missing_debug_implementations)]
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version // enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]

View file

@ -15,7 +15,3 @@ categories.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = { workspace = true }
thiserror = { workspace = true }
libpt-log = {path = "../libpt-log" }
libpt-bintols = {path = "../libpt-bintols" }

View file

@ -4,175 +4,3 @@
//! module. //! module.
//! //!
//! This crate is currently empty. //! This crate is currently empty.
use anyhow::{bail, Result};
use libpt_bintols::display::{bytes_to_bin, humanbytes};
use libpt_log::{debug, error, info, trace, warn};
use std::io::{prelude::*, BufReader, Read, SeekFrom};
const BYTES_PER_LINE: usize = 16;
const LINE_SEP_HORIZ: char = '─';
const LINE_SEP_VERT: char = '│';
pub struct HeduConfig {
pub chars: bool,
pub skip: usize,
pub show_identical: bool,
pub limit: usize,
stop: bool,
}
impl HeduConfig {
pub fn new(chars: bool, skip: usize, show_identical: bool, limit: usize) -> Self {
HeduConfig {
chars,
skip,
show_identical,
limit: limit,
stop: false,
}
}
}
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(())
}
}
pub fn dump(data: &mut dyn DataSource, mut config: HeduConfig) -> Result<()> {
// prepare some variables
let mut buf: [[u8; BYTES_PER_LINE]; 2] = [[0; BYTES_PER_LINE]; 2];
let mut alt_buf = 0usize;
let mut byte_counter: usize = 0;
let mut len: usize;
// skip a given number of bytes
if config.skip > 0 {
data.skip(config.skip)?;
byte_counter += config.skip;
debug!("Skipped {}", humanbytes(config.skip));
}
// print the head
print!("DATA IDX {LINE_SEP_VERT} DATA AS HEX");
if config.chars {
print!("{:width$} {LINE_SEP_VERT} FOO", "", width = 37);
}
println!();
if config.chars {
println!("{}", format!("{LINE_SEP_HORIZ}").repeat(80));
} else {
println!("{}", format!("{LINE_SEP_HORIZ}").repeat(59));
}
// data dump loop
len = rd_data(data, &mut buf, &mut alt_buf, &mut byte_counter, &mut config).unwrap();
while len > 0 {
print!("{:08X} {LINE_SEP_VERT} ", byte_counter);
for i in 0..len {
if i as usize % BYTES_PER_LINE == BYTES_PER_LINE / 2 {
print!(" ");
}
print!("{:02X} ", buf[alt_buf][i]);
}
if len == BYTES_PER_LINE / 2 {
print!(" ")
}
for i in 0..(BYTES_PER_LINE - len) {
if i as usize % BYTES_PER_LINE == BYTES_PER_LINE / 2 {
print!(" ");
}
print!(" ");
}
if config.chars {
print!("{LINE_SEP_VERT} |");
for i in 0..len {
print!("{}", mask_chars(buf[alt_buf][i] as char));
}
print!("|");
}
println!();
// loop breaker logic
if config.stop {
break;
}
// after line logic
len = rd_data(data, &mut buf, &mut alt_buf, &mut byte_counter, &mut config).unwrap();
alt_buf ^= 1; // toggle the alt buf
if buf[0] == buf[1] && len == BYTES_PER_LINE && !config.show_identical {
trace!(buf = format!("{:?}", buf), "found a duplicating line");
let start_line = byte_counter;
while buf[0] == buf[1] && len == BYTES_PER_LINE {
len =
rd_data(data, &mut buf, &mut alt_buf, &mut byte_counter, &mut config).unwrap();
byte_counter += BYTES_PER_LINE;
}
println!(
"^^^^^^^^ {LINE_SEP_VERT} (repeats {} lines)",
byte_counter - start_line
);
}
// switch to the second half of the buf, the original half is stored the old buffer
// We detect duplicate lines with this
alt_buf ^= 1; // toggle the alt buf
}
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>';
}
}
fn rd_data(
data: &mut dyn DataSource,
buf: &mut [[u8; BYTES_PER_LINE]; 2],
alt_buf: &mut usize,
byte_counter: &mut usize,
config: &mut HeduConfig,
) -> Result<usize> {
match data.read(&mut buf[*alt_buf]) {
Ok(mut len) => {
*byte_counter += len; // FIXME: incremented too early! dump always starts at 0x10
if config.limit != 0 && *byte_counter >= config.limit {
trace!(
byte_counter,
limit = config.limit,
len,
nlen = (config.limit % BYTES_PER_LINE),
"byte counter is farther than limit"
);
len = config.limit % BYTES_PER_LINE;
config.stop = true;
}
return Ok(len);
}
Err(err) => {
error!("error while reading data: {err}");
bail!(err)
}
}
}

View file

@ -16,8 +16,7 @@ categories.workspace = true
tracing = "0.1.37" tracing = "0.1.37"
tracing-appender = "0.2.2" tracing-appender = "0.2.2"
tracing-subscriber = "0.3.17" tracing-subscriber = "0.3.17"
anyhow = { workspace = true } pyo3 = {workspace = true}
thiserror = { workspace = true }
[dev-dependencies] [dev-dependencies]
gag = "1.0.0" gag = "1.0.0"

View file

@ -12,11 +12,12 @@
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use anyhow; use pyo3::{exceptions::PyException, PyErr};
use thiserror::Error;
use tracing::subscriber::SetGlobalDefaultError; use tracing::subscriber::SetGlobalDefaultError;
//// TYPES ///////////////////////////////////////////////////////////////////////////////////////// //// TYPES /////////////////////////////////////////////////////////////////////////////////////////
/// a quick alias for a result with a [`Error`]
pub type Result<T> = std::result::Result<T, Error>;
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
@ -26,16 +27,12 @@ use tracing::subscriber::SetGlobalDefaultError;
//// ENUMS ///////////////////////////////////////////////////////////////////////////////////////// //// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
/// ## Errors for the [Logger](super::Logger) /// ## Errors for the [Logger](super::Logger)
#[derive(Error)]
pub enum Error { pub enum Error {
/// Bad IO operation /// Bad IO operation
#[error("Bad IO operation")]
IO(std::io::Error), IO(std::io::Error),
/// Various errors raised when the messenger is used in a wrong way /// Various errors raised when the messenger is used in a wrong way
#[error("Bad usage")]
Usage(String), Usage(String),
/// Could not assign logger as the global default /// Could not assign logger as the global default
#[error("Could not assign as global default")] // TODO: make this more clear
SetGlobalDefaultFail(SetGlobalDefaultError), SetGlobalDefaultFail(SetGlobalDefaultError),
} }
@ -55,6 +52,19 @@ impl From<SetGlobalDefaultError> for Error {
} }
} }
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Into<PyErr> for Error {
fn into(self) -> PyErr {
match self {
Error::IO(err) => PyException::new_err(format!("LoggerError: IO {err:?}")),
Error::Usage(err) => PyException::new_err(format!("LoggerError: Usage {err}")),
Error::SetGlobalDefaultFail(err) => {
PyException::new_err(format!("LoggerError: SetGlobalDefaultFail {err}"))
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
impl std::fmt::Debug for Error { impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -66,6 +76,18 @@ impl std::fmt::Debug for Error {
} }
} }
////////////////////////////////////////////////////////////////////////////////////////////////////
impl std::fmt::Display 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 ////////////////////////////////////////////////////////////////////////////// //// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS ///////////////////////////////////////////////////////////////////////////// //// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View file

@ -19,7 +19,6 @@
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use std::{ use std::{
fmt, fmt,
ops::Deref,
path::PathBuf, path::PathBuf,
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
}; };
@ -28,20 +27,10 @@ pub mod error;
use error::*; use error::*;
pub use tracing::{debug, error, info, trace, warn, Level}; pub use tracing::{debug, error, info, trace, warn, Level};
use tracing_appender::{ use tracing_appender;
self, use tracing_subscriber::{prelude::*, fmt::format::FmtSpan};
non_blocking::{NonBlocking, WorkerGuard},
};
use tracing_subscriber::{
fmt::{
format::FmtSpan,
time::{self, FormatTime},
},
prelude::*,
};
use anyhow::{bail, Result};
use pyo3::prelude::*;
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// The log level used when none is specified /// The log level used when none is specified
pub const DEFAULT_LOG_LEVEL: Level = Level::INFO; pub const DEFAULT_LOG_LEVEL: Level = Level::INFO;
@ -58,7 +47,8 @@ static INITIALIZED: AtomicBool = AtomicBool::new(false);
/// ///
/// This struct exists mainly for the python module, so that we can use the same logger with both /// This struct exists mainly for the python module, so that we can use the same logger with both
/// python and rust. /// python and rust.
pub struct Logger; #[pyclass]
pub struct Logger {}
//// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// //// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
/// ## Main implementation /// ## Main implementation
@ -87,33 +77,6 @@ impl Logger {
false, false,
false, false,
false, false,
false,
true,
false,
)
}
/// ## initializes the logger
///
/// Will enable the logger to be used. This is a version that shows less information,
/// 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(
false,
PathBuf::from(DEFAULT_LOG_DIR),
true,
false,
true,
false,
max_level.unwrap_or(DEFAULT_LOG_LEVEL),
false,
false,
false,
false,
false,
false,
) )
} }
@ -131,98 +94,57 @@ impl Logger {
display_thread_ids: bool, display_thread_ids: bool,
display_thread_names: bool, display_thread_names: bool,
display_line_number: bool, display_line_number: bool,
pretty: bool,
show_time: bool,
uptime: bool, // uptime instead of system time
) -> Result<()> { ) -> Result<()> {
// only init if no init has been performed yet // only init if no init has been performed yet
if INITIALIZED.load(Ordering::Relaxed) { if INITIALIZED.load(Ordering::Relaxed) {
warn!("trying to reinitialize the logger, ignoring"); warn!("trying to reinitialize the logger, ignoring");
bail!(Error::Usage(format!("logging is already initialized"))); return Err(Error::Usage(format!("logging is already initialized")));
} } else {
let subscriber = tracing_subscriber::fmt::Subscriber::builder() let filter = tracing_subscriber::filter::FilterFn::new(|_metadata| {
.with_level(display_level) // let mut filter = false;
.with_max_level(max_level) //
.with_ansi(ansi) // // if it's this lib, continue
.with_target(display_target) // filter |= metadata.target().contains(env!("CARGO_PKG_NAME"));
.with_file(display_filename) // filter |= metadata.target().contains("libpt");
.with_thread_ids(display_thread_ids) //
.with_line_number(display_line_number) // // if it's another crate, only show above debug
.with_thread_names(display_thread_names) // filter |= metadata.level() > &Level::DEBUG;
.with_span_events(FmtSpan::FULL); //
// I know this is hacky, but I couldn't get it any other way. I couldn't even find a // filter
// project that could do it any other way. You can't apply one after another, because the // FIXME: Make the filter customizable with sane defaults. Don't block the
// type is changed every time. When using Box<dyn Whatever>, some methods complain about // executing crate.
// not being in trait bounds. true
// TODO: somehow find a better solution for this });
match (log_to_file, show_time, pretty, uptime) {
(true, true, true, true) => { let basic_subscriber = tracing_subscriber::fmt::Subscriber::builder()
let subscriber = subscriber // subscriber configuration
.with_writer(new_file_appender(log_dir)) .with_ansi(ansi)
.with_timer(time::uptime()) .with_file(display_filename)
.pretty() .with_level(display_level)
.finish(); .with_target(display_target)
tracing::subscriber::set_global_default(subscriber)?; .with_max_level(max_level)
} .with_thread_ids(display_thread_ids)
(true, true, true, false) => { .with_line_number(display_line_number)
let subscriber = subscriber .with_thread_names(display_thread_names)
.with_writer(new_file_appender(log_dir)) .with_span_events(FmtSpan::FULL)
.pretty() //.pretty // too verbose and over multiple lines, a bit like python tracebacks
.finish(); .finish()
tracing::subscriber::set_global_default(subscriber)?; // add layers
} .with(filter);
(true, false, true, _) => {
let subscriber = subscriber if log_to_file {
.with_writer(new_file_appender(log_dir)) let file_appender = tracing_appender::rolling::daily(log_dir, "log");
.without_time()
.pretty()
.finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(true, true, false, true) => {
let subscriber = subscriber
.with_writer(new_file_appender(log_dir))
.with_timer(time::uptime())
.finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(true, true, false, false) => {
let subscriber = subscriber.with_writer(new_file_appender(log_dir)).finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(true, false, false, _) => {
let file_appender = tracing_appender::rolling::daily(log_dir.clone(), "log");
let (file_writer, _guard) = tracing_appender::non_blocking(file_appender); let (file_writer, _guard) = tracing_appender::non_blocking(file_appender);
let subscriber = subscriber.with_writer(file_writer).without_time().finish(); let layered_subscriber = basic_subscriber
tracing::subscriber::set_global_default(subscriber)?; .with(tracing_subscriber::fmt::Layer::default().with_writer(file_writer));
} tracing::subscriber::set_global_default(layered_subscriber)?;
(false, true, true, true) => { } else {
let subscriber = subscriber.pretty().with_timer(time::uptime()).finish(); tracing::subscriber::set_global_default(basic_subscriber)?;
tracing::subscriber::set_global_default(subscriber)?;
}
(false, true, true, false) => {
let subscriber = subscriber.pretty().with_timer(time::uptime()).finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, false, true, _) => {
let subscriber = subscriber.without_time().pretty().finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, true, false, true) => {
let subscriber = subscriber.with_timer(time::uptime()).finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, true, false, false) => {
let subscriber = subscriber.finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, false, false, _) => {
let subscriber = subscriber.without_time().finish();
tracing::subscriber::set_global_default(subscriber)?;
} }
INITIALIZED.store(true, Ordering::Relaxed);
Ok(())
} }
INITIALIZED.store(true, Ordering::Relaxed);
Ok(())
} }
/// ## logging at [`Level::ERROR`] /// ## logging at [`Level::ERROR`]
@ -262,6 +184,61 @@ impl Logger {
} }
} }
////////////////////////////////////////////////////////////////////////////////////////////////////
/// ## Implementation of the python interface
#[pymethods]
impl Logger {
/// ## Python version of [`new()`](Logger::new)
#[new]
pub fn py_new() -> PyResult<Self> {
Ok(Logger::new())
}
/// ## Python version of [`init()`](Logger::init)
#[pyo3(name = "init")]
#[staticmethod]
pub fn py_init(log_dir: Option<PathBuf>, max_level: Option<String>) -> Result<()> {
Self::init(
log_dir,
match max_level {
Some(s) => match s.to_uppercase().as_str() {
"TRACE" => Some(tracing::Level::TRACE),
"DEBUG" => Some(tracing::Level::DEBUG),
"INFO" => Some(tracing::Level::INFO),
"WARN" => Some(tracing::Level::WARN),
"ERROR" => Some(tracing::Level::ERROR),
_ => return Err(Error::Usage(format!("'{s}' is not a valid log level"))),
},
None => None,
},
)
}
/// ## Python version of [`error()`](Logger::error)
#[pyo3(name = "error")]
pub fn py_error(&self, printable: String) {
self.error(printable)
}
/// ## Python version of [`warn()`](Logger::warn)
#[pyo3(name = "warn")]
pub fn py_warn(&self, printable: String) {
self.warn(printable)
}
/// ## Python version of [`info()`](Logger::info)
#[pyo3(name = "info")]
pub fn py_info(&self, printable: String) {
self.info(printable)
}
/// ## Python version of [`debug()`](Logger::debug)
#[pyo3(name = "debug")]
pub fn py_debug(&self, printable: String) {
self.debug(printable)
}
/// ## Python version of [`trace()`](Logger::trace)
#[pyo3(name = "trace")]
pub fn py_trace(&self, printable: String) {
self.trace(printable)
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
impl fmt::Debug for Logger { impl fmt::Debug for Logger {
/// ## DEBUG representation for [`Logger`] /// ## DEBUG representation for [`Logger`]
@ -277,7 +254,4 @@ impl fmt::Debug for Logger {
//// PUBLIC FUNCTIONS ////////////////////////////////////////////////////////////////////////////// //// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE 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;
}

View file

@ -16,9 +16,9 @@ categories.workspace = true
[dependencies] [dependencies]
humantime = "2.1.0" humantime = "2.1.0"
libpt-core = { path = "../libpt-core" } libpt-core = { version = "0.1.0", path = "../libpt-core" }
libpt-log = { path = "../libpt-log" } libpt-log = { version = "0.1.0", path = "../libpt-log" }
libpt-math = { path = "../libpt-math" } libpt-math = { version = "0.1.0", path = "../libpt-math" }
reqwest = { version = "0.11.20", features = ["blocking"] } reqwest = { version = "0.11.20", features = ["blocking"] }
serde = { version = "1.0.188", features = ["serde_derive"] } serde = { version = "1.0.188", features = ["serde_derive"] }
serde_json = "1.0.107" serde_json = "1.0.107"

View file

@ -10,9 +10,11 @@
// we want docs // we want docs
#![warn(missing_docs)] #![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)] #![warn(rustdoc::missing_crate_level_docs)]
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere. // we want Debug everywhere.
#![warn(missing_debug_implementations)] #![warn(missing_debug_implementations)]
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version // enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]

View file

@ -6,9 +6,11 @@
// we want docs // we want docs
#![warn(missing_docs)] #![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)] #![warn(rustdoc::missing_crate_level_docs)]
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// we want Debug everywhere. // we want Debug everywhere.
#![warn(missing_debug_implementations)] #![warn(missing_debug_implementations)]
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// enable clippy's extra lints, the pedantic version // enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]

View file

@ -188,11 +188,7 @@ pub fn continuous_uptime_monitor(
let mut last_was_up: bool = true; let mut last_was_up: bool = true;
let mut last_ratio: u8 = status.success_ratio; let mut last_ratio: u8 = status.success_ratio;
loop { loop {
trace!( trace!(?status, ?last_was_up, "loop iteration for continuous uptime monitor");
?status,
?last_was_up,
"loop iteration for continuous uptime monitor"
);
if !status.success { if !status.success {
if last_was_up { if last_was_up {
trace!("displaying status"); trace!("displaying status");

View file

@ -0,0 +1,120 @@
# This file is autogenerated by maturin v1.1.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: --skip-existing *

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,22 @@
[package]
name = "libpt-py"
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
[lib]
name = "libpt_py"
crate-type = ["cdylib"]
[dependencies]
libpt = { version = "0.1.7", path = "../..", features = ["all"] }
pyo3 = { workspace = true }

21
members/libpt-py/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Christoph Johannes Scherr
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,76 @@
# pt / libpt
![pt-logo](data/media/pt-logo.svg)
`pt` stands for either one of "personal tool", "plex tool", "pete" or something among those lines.
It is a collection of tools that i might or might not use. The intended purpose of this repo is that
I program whatever i feel is worth having in a personal thing into it, then use it as either a lib,
crate, python module or executable.
Let's see if I make it a bloated mess or stop committing after 30 hello worlds.
#### But the name `pt` / `libpt` already exists!
So what? I don't care. Besides, there is not enough names to name everything unique.
## Dependencies
- See `cargo.toml`
- [openssl bindings for rust](https://docs.rs/openssl/latest/openssl/)
- [Python](https://www.python.org/)
- [`maturin`](https://maturin.rs) - `pip install maturin`
## Compiling & Installing from source
If you only want the rust library, you can simply build it with `cargo build`. Add it to your
project like any other local dependency.
If you want to use the python variant too, you need to compile with maturing.
- Install in `venv`: `maturin develop --release`
- Install in system: `maturin build --release && pip install target/wheels/libpt-x.x.x-*`
## Installing from [pypi](https://pypi.org)
`libpt` has been packaged for [pypi.org](https://pypi.org/project/libpt/).
You can install it with `pip install libpt`
## Installing from [crates.io](https://crates.io)
`libpt` has been packaged for [crates.io](https://crates.io/crates/libpt).
You can add the library to your project with `cargo add libpt`.
## Installing from my personal package registry
`libpt` has been packaged for [git.cscherr.de](https://git.cscherr.de).
You can add the registry to your `config.toml` and then `cargo add libpt`
[Package](https://git.cscherr.de/PlexSheep/-/packages/cargo/libpt/)
## Testing
Testing needs to be done separately for the rust and python parts:
- Rust testing with `cargo test`
- Python testing with `./scripts/pytests.sh` or `python -m unittest discover -fs tests/python`
## Documentation
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

@ -0,0 +1,25 @@
[build-system]
requires = ["maturin>=0.14,<0.15"]
build-backend = "maturin"
[project]
name = "libpt"
readme = "README.md"
requires-python = ">=3.7"
authors = [{ name = "Christoph J. Scherr", email = "software@cscherr.de" }]
license = { file = "LICENSE" }
keywords = ["pyo3"]
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
[project.urls]
Homepage = "https://git.cscherr.de/PlexSheep/libpt"
[tool.maturin]
features = ["pyo3/extension-module"]
python-source = "python"
module-name = "libpt._libpt"

View file

@ -0,0 +1 @@
from ._libpt import *

View file

@ -0,0 +1,14 @@
"""
# libpt python bindings
`libpt` is originally implemented in rust, but offers a python module too.
"""
from . import logger
from . import networking
from . import common
def is_loaded() -> bool:
"""
returns true if `libpt` has been loaded
"""
...

View file

@ -0,0 +1,7 @@
"""
common functionalities
This module implements common functionality useful for many use cases, such as macros,
Formatting functions and more.
"""
from . import printing

View file

@ -0,0 +1,14 @@
"""
tools that make printing stuff better
"""
def divider() -> str:
"""
Quickly get a one line visual divider
"""
...
def print_divider():
"""
Quickly print a one line visual divider
"""
...

View file

@ -0,0 +1,65 @@
"""
# A specialized Logger for `libpt`
"""
""" the default log level """
DEFAULT_LOG_LEVEL = "INFO"
""" Set the value of this key as envar to set a loglevel """
LOGGER_ENV_KEY = "LIBPT_LOGLEVEL"
class Logger:
"""
`libpt` logger
Call `init` once before usage, else all logging attempts will be ignored.
"""
def __init__(self):
"""
get a new logger
"""
...
@staticmethod
def init():
"""
initialize the logger before the first usage
"""
...
@staticmethod
def init_specialized(color: bool):
"""
initialize the logger before the first usage, but with the ability to tweak things a bit
"""
...
def error(self, printable: str):
"""
log at level `error`
"""
...
def warn(self, printable: str):
"""
log at level `error`
"""
...
def info(self, printable: str):
"""
log at level `error`
"""
...
def debug(self, printable: str):
"""
log at level `error`
"""
...
def trace(self, printable: str):
"""
log at level `error`
"""
...

View file

@ -0,0 +1,7 @@
"""
# various networking tools
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.
"""
from . import monitoring

View file

@ -0,0 +1,7 @@
"""
# monitor your network
This module offers functions to monitor your network.
"""
from . import uptime

View file

@ -0,0 +1,86 @@
"""
# 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
"""
class UptimeStatus:
"""
Describes an uptime status
UptimeStatus describes the result of an uptime check.
"""
""" true if the [`UptimeStatus`] is considered successful"""
success: bool
""" the percentage of reachable urls out of the total urls"""
success_ratio: int
""" 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.
"""
success_ratio_target: int
""" the number of reachable [`urls`](UptimeStatus::urls) """
reachable: int
""" set a timeout (in ms) """
timeout: int
"""URL list cant be ported to python, use UptimeStatus.urls()"""
__urls: ...
def __init__(self, success_ratio_target: int, url_strs: list[str], timeout: int) -> None:
"""
create a new UptimeStatus and check it
`success_ratio_target` should never be more than 100 (it represents a success percentage)
"""
...
def check(self) -> None:
"""
checks if the stored urls
Makes the actual https requests and updates fields accordingly.
This method can block some time, as the web requests are implemented as blocking and
executed by the shared library (not in python)
"""
...
def calc_success(self) -> None:
"""
calculate the success based on the `reachable` and `total`
Calculates the ratio of [reachable]/
(length of [__urls]).
Calculates a [`success_ratio`] from that, by multiplying with 100, then flooring.
If the [`success_ratio`] is greater than or equal to the [`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`] will be `0`.
Note: does not check for networking, use [`check()`] for that.
"""
...
def urls(self) -> list[str]:
"""
get urls for python
Since [`__urls`] has no python equivalent, return the urls as a `list[str]` in
Python.
"""
...
def continuous_uptime_monitor(success_ratio_target: int, urls: list[str], interval: int, timeout: int) -> None:
"""
Uptime monitor
This function continuously monitors the uptime of your host/network.
On change of status, an update will be logged at INFO Level, containing
information on your current status, including timestamps of the last up/down time and durations
since.
"""
...

View file

View file

@ -0,0 +1 @@
python -m unittest discover -fs tests/python

101
members/libpt-py/src/lib.rs Normal file
View file

@ -0,0 +1,101 @@
// FIXME: Using a local dependency does not work with maturin as it seems?
use libpt::{
log::*,
};
use pyo3::prelude::*;
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
/// ## Check if [`libpt`](crate) has been loaded
///
/// Always returns `true` if you can execute it.
#[pyfunction]
pub fn is_loaded() -> bool {
true
}
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
/// ## Python module: logger
#[pymodule]
fn py_logger(py: Python, parent: &PyModule) -> PyResult<()> {
let module = PyModule::new(py, "logger")?;
module.add_class::<Logger>()?;
parent.add_submodule(module)?;
Ok(())
}
//
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// /// ## Python module: common
// #[pymodule]
// fn py_common(py: Python, parent: &PyModule) -> PyResult<()> {
// let module = PyModule::new(py, "common")?;
// py_common_printing(py, module)?;
//
// parent.add_submodule(module)?;
// Ok(())
// }
//
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// /// ## Python module: common.printing
// #[pymodule]
// fn py_common_printing(py: Python, parent: &PyModule) -> PyResult<()> {
// let module = PyModule::new(py, "printing")?;
// module.add_function(wrap_pyfunction!(common::printing::divider, module)?)?;
// module.add_function(wrap_pyfunction!(common::printing::print_divider, module)?)?;
//
// parent.add_submodule(module)?;
// Ok(())
// }
//
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// /// ## Python module: networking
// #[pymodule]
// fn py_networking(py: Python, parent: &PyModule) -> PyResult<()> {
// let module = PyModule::new(py, "networking")?;
// py_networking_monitoring(py, module)?;
//
// parent.add_submodule(module)?;
// Ok(())
// }
//
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// /// ## Python module: networking.monitoring
// #[pymodule]
// fn py_networking_monitoring(py: Python, parent: &PyModule) -> PyResult<()> {
// let module = PyModule::new(py, "monitoring")?;
// py_networking_monitoring_uptime(py, module)?;
//
// parent.add_submodule(module)?;
// Ok(())
// }
//
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// /// ## Python module: networking.monitoring.uptime
// #[pymodule]
// fn py_networking_monitoring_uptime(py: Python, parent: &PyModule) -> PyResult<()> {
// let module = PyModule::new(py, "uptime")?;
// module.add_class::<networking::monitoring::uptime::UptimeStatus>()?;
// module.add_function(wrap_pyfunction!(
// networking::monitoring::uptime::py_continuous_uptime_monitor,
// module
// )?)?;
//
// parent.add_submodule(module)?;
// Ok(())
// }
////////////////////////////////////////////////////////////////////////////////////////////////////
/// ## Python module: root
///
/// This function is the entry point of [`PyO3`](pyo3). This is where the main module is built.
#[pymodule]
fn _libpt(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(is_loaded, m)?)?;
// load sub modules
// py_common(py, m)?;
py_logger(py, m)?;
// py_networking(py, m)?;
Ok(())
}

View file

@ -1,8 +0,0 @@
#!/bin/bash
set -e
PKGs=(libpt-{core,math,log,net,bintols,ccc,hedu,bin,py} libpt)
for PKG in "${PKGs[@]}"; do
echo "Package: $PKG"
cargo publish --registry cscherr -p "$PKG"
cargo publish -p "$PKG"
done

View file

@ -6,17 +6,17 @@
//! `pt` is a project consisting of multiple smaller crates, all bundled together in this //! `pt` is a project consisting of multiple smaller crates, all bundled together in this
//! "main crate". Most crates will only show up if you activate their feature. //! "main crate". Most crates will only show up if you activate their feature.
#[cfg(feature = "bintols")]
pub use libpt_bintols as bintols;
#[cfg(feature = "ccc")]
pub use libpt_ccc as ccc;
#[cfg(feature = "core")] #[cfg(feature = "core")]
pub use libpt_core as core; pub use libpt_core as core;
#[cfg(feature = "bintols")]
pub use libpt_bintols as bintols;
#[cfg(feature = "hedu")] #[cfg(feature = "hedu")]
pub use libpt_hedu as hedu; pub use libpt_hedu as hedu;
#[cfg(feature = "log")] #[cfg(feature = "log")]
pub use libpt_log as log; pub use libpt_log as log;
#[cfg(feature = "math")] #[cfg(feature = "math")]
pub use libpt_math as math; pub use libpt_math as math;
#[cfg(feature = "net")] #[cfg(feature = "net")]
pub use libpt_net as net; pub use libpt_net as net;
#[cfg(feature = "ccc")]
pub use libpt_ccc as ccc;