Compare commits

...

33 commits

Author SHA1 Message Date
edcec991dc
auto release with ci?
Some checks failed
Cargo Check, Format, Fix and Test / cargo CI (push) Successful in 2m8s
Cargo Check, Format, Fix and Test / cargo CI (pull_request) Has been cancelled
2024-01-17 00:26:11 +01:00
Gitea CI
de9ef8d841 automatic cargo CI changes 2024-01-16 18:43:48 +00:00
0155465332
hedu limit
All checks were successful
Cargo Check, Format, Fix and Test / cargo CI (push) Successful in 2m1s
2024-01-16 19:41:51 +01:00
521ae9a193
skip line counting 2024-01-16 19:03:56 +01:00
Gitea CI
15687ca0d7 automatic cargo CI changes 2024-01-16 17:29:38 +00:00
441669a2c3
more configs
All checks were successful
Cargo Check, Format, Fix and Test / cargo CI (push) Successful in 2m41s
can't detect dups like this

dedup lines
2024-01-16 18:13:39 +01:00
b2435f3963
sub crates are now version agnostic to other local crates
All checks were successful
Cargo Check, Format, Fix and Test / cargo CI (push) Successful in 2m13s
2024-01-16 15:59:04 +01:00
b58dc3dc44
fix small formatting issue in hedu 2024-01-16 15:58:40 +01:00
Gitea CI
747c3e6eac automatic cargo CI changes 2024-01-16 15:12:03 +00:00
5b8753c45d
get rid of unreachable code
All checks were successful
Cargo Check, Format, Fix and Test / cargo CI (push) Successful in 2m10s
2024-01-16 15:45:12 +01:00
aa560b0e05
hack log together for more configs 2024-01-16 15:44:53 +01:00
39a21d64e9
add log to hedu 2024-01-16 14:04:06 +01:00
0515e221f9
adjust bin interface with log 2024-01-16 14:03:55 +01:00
a9cf78ee0d
i think it works 2024-01-16 12:47:38 +01:00
Gitea CI
66932f70a3 automatic cargo CI changes 2024-01-16 10:33:21 +00:00
3f59e99b88
early hedu
All checks were successful
Cargo Check, Format, Fix and Test / cargo CI (push) Successful in 2m20s
2024-01-16 11:31:05 +01:00
b7fbef73b4
remove versions for workspace crates
All checks were successful
Cargo Check, Format, Fix and Test / cargo CI (push) Successful in 1m58s
2024-01-16 10:33:14 +01:00
Gitea CI
b3e5a99718 automatic cargo CI changes 2024-01-16 09:30:55 +00:00
eaea5ac1d7
Merge branch 'master' of https://git.cscherr.de/PlexSheep/pt
All checks were successful
Cargo Check, Format, Fix and Test / cargo CI (push) Successful in 2m31s
2024-01-16 10:28:11 +01:00
6181f2bff8
remove the goddamn python interface 2024-01-16 10:28:08 +01:00
43f27493b4
remove the goddamn python interface
Some checks failed
Cargo Check, Format, Fix and Test / cargo CI (push) Failing after 38s
2024-01-16 10:22:07 +01:00
c85e785802
py logger removal
Some checks failed
Cargo Check, Format, Fix and Test / cargo CI (push) Failing after 2m21s
2024-01-16 10:18:39 +01:00
074dcf9c0f
log error handlin 2024-01-16 10:18:29 +01:00
c679bed538
Merge branch 'master' of https://git.cscherr.de/PlexSheep/pt
Some checks failed
Cargo Check, Format, Fix and Test / cargo CI (push) Failing after 1m50s
2024-01-15 23:30:39 +01:00
3e2ecf1189
Merge branch 'master' of https://git.cscherr.de/PlexSheep/pt 2024-01-15 23:30:34 +01:00
50fea61bb0
Merge branch 'master' of https://git.cscherr.de/PlexSheep/pt
Some checks failed
Cargo Check, Format, Fix and Test / cargo CI (push) Has been cancelled
2024-01-15 23:29:04 +01:00
9f393f0369
destruction of libpt-ccc 2024-01-15 23:28:01 +01:00
PlexSheep
43ac1e3fb8 Automatical formatting 2024-01-10 20:52:13 +00:00
6dcc45d722 remove trailing whitespaces
Some checks failed
Cargo Format, Check and Test / cargo fmt (push) Successful in 1m0s
Cargo Format, Check and Test / cargo check (push) Successful in 1m20s
Cargo Format, Check and Test / cargo test (push) Failing after 1m16s
2024-01-10 21:45:40 +01:00
6330c3da48 gitea workflow cargo
Some checks failed
Cargo Format, Check and Test / cargo test (push) Has been cancelled
2024-01-10 21:43:03 +01:00
0de0340f4e publish workspace 2023-09-29 18:42:47 +02:00
ceb8c98715 Merge branch 'devel' 2023-09-29 18:24:50 +02:00
1bdef8c493 make things hopefully ready for v0.1.7 2023-09-29 17:50:47 +02:00
50 changed files with 650 additions and 1581 deletions

View file

@ -0,0 +1,22 @@
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

@ -0,0 +1,27 @@
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>

View file

@ -1,120 +0,0 @@
# 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

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

Binary file not shown.

BIN
data/256B-zero.img Normal file

Binary file not shown.

View file

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

View file

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

@ -0,0 +1,151 @@
//! # 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_verbosity_flag::{Verbosity, InfoLevel};
use clap_verbosity_flag::{InfoLevel, Verbosity};
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// short about section displayed in help
@ -103,13 +103,10 @@ pub enum NetCommands {
/// set a timeout (in ms)
#[clap(short, long, default_value_t = 100)]
timeout: u64
timeout: u64,
},
/// discover hosts in your network
Discover {
}
Discover {},
}
////////////////////////////////////////////////////////////////////////////////////////////////////

View file

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

View file

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

View file

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

View file

@ -59,8 +59,7 @@ where
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);
}
else {
} else {
unreachable!()
}
}

View file

@ -1,5 +1,5 @@
use libpt_bintols::*;
use libpt_bintols::display::*;
use libpt_bintols::*;
#[test]
fn btobin() {
@ -10,11 +10,16 @@ fn btobin() {
#[test]
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);
assert_eq!(r, format!("0b00001100_00011111_01010010_\
assert_eq!(
r,
format!(
"0b00001100_00011111_01010010_\
00100000_01111011_00100000_01011100_00010111_00001100\n\
_00100000_00001100_00000001_00000001_00000001"));
_00100000_00001100_00000001_00000001_00000001"
)
);
}
#[test]
@ -29,35 +34,35 @@ fn hmnbytes() {
assert_eq!(humanbytes(0), format!("0 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+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+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+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+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+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+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+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+1), format!("1.00 Y"));
assert_eq!(humanbytes(YOBI + 1), format!("1.00 Y"));
}

View file

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

View file

@ -1,225 +0,0 @@
//! # 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,14 +19,7 @@
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
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_log;
use libpt_math;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
@ -40,44 +33,8 @@ use libpt_math;
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// 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 ////////////////////////////////////////////////////////////////////////////////
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 //////////////////////////////////////////////////////////////////////////////

View file

@ -1,223 +0,0 @@
//! # 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,3 +13,6 @@ keywords.workspace = true
categories.workspace = true
[dependencies]
anyhow = "1.0.79"
cucumber = "0.20.2"
libpt-log = {path = "../libpt-log" }

View file

@ -10,11 +10,9 @@
// 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)]

View file

@ -6,11 +6,9 @@
// 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)]

View file

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

View file

@ -4,3 +4,175 @@
//! module.
//!
//! 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,7 +16,8 @@ categories.workspace = true
tracing = "0.1.37"
tracing-appender = "0.2.2"
tracing-subscriber = "0.3.17"
pyo3 = {workspace = true}
anyhow = { workspace = true }
thiserror = { workspace = true }
[dev-dependencies]
gag = "1.0.0"

View file

@ -12,12 +12,11 @@
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use pyo3::{exceptions::PyException, PyErr};
use anyhow;
use thiserror::Error;
use tracing::subscriber::SetGlobalDefaultError;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
/// a quick alias for a result with a [`Error`]
pub type Result<T> = std::result::Result<T, Error>;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
@ -27,12 +26,16 @@ pub type Result<T> = std::result::Result<T, Error>;
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
/// ## Errors for the [Logger](super::Logger)
#[derive(Error)]
pub enum Error {
/// Bad IO operation
#[error("Bad IO operation")]
IO(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),
}
@ -52,19 +55,6 @@ 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 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -76,18 +66,6 @@ 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 //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View file

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

View file

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

View file

@ -10,11 +10,9 @@
// 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)]

View file

@ -6,11 +6,9 @@
// 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)]

View file

@ -188,7 +188,11 @@ pub fn continuous_uptime_monitor(
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");
trace!(
?status,
?last_was_up,
"loop iteration for continuous uptime monitor"
);
if !status.success {
if last_was_up {
trace!("displaying status");

View file

@ -1,120 +0,0 @@
# 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 *

View file

@ -1,72 +0,0 @@
/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

@ -1,22 +0,0 @@
[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 }

View file

@ -1,21 +0,0 @@
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

@ -1,76 +0,0 @@
# 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

@ -1,25 +0,0 @@
[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

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

View file

@ -1,14 +0,0 @@
"""
# 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

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

View file

@ -1,14 +0,0 @@
"""
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

@ -1,65 +0,0 @@
"""
# 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

@ -1,7 +0,0 @@
"""
# 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

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

View file

@ -1,86 +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
"""
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

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

View file

@ -1,101 +0,0 @@
// 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(())
}

8
publish.sh Executable file
View file

@ -0,0 +1,8 @@
#!/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,10 +6,12 @@
//! `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.
#[cfg(feature = "core")]
pub use libpt_core as core;
#[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 = "hedu")]
pub use libpt_hedu as hedu;
#[cfg(feature = "log")]
@ -18,5 +20,3 @@ pub use libpt_log as log;
pub use libpt_math as math;
#[cfg(feature = "net")]
pub use libpt_net as net;
#[cfg(feature = "ccc")]
pub use libpt_ccc as ccc;