From c82e36516dbbd809b73484a4e24073a1ecf6242d Mon Sep 17 00:00:00 2001 From: AstroAir Date: Wed, 6 Nov 2024 17:20:27 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=91=BD=E5=90=8D=E7=A9=BA?= =?UTF-8?q?=E9=97=B4=EF=BC=8C=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E6=B7=BB=E5=8A=A0=20SHA1=20=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E5=AE=9E=E7=8E=B0=EF=BC=8C=E6=9B=B4=E6=96=B0=20CMake?= =?UTF-8?q?=20=E9=85=8D=E7=BD=AE=E4=BB=A5=E6=94=AF=E6=8C=81=20Python=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 + config/script/check.json | 154 ++--- modules/atom.algorithm/pymodule.cpp | 436 +++++++++++++ modules/atom.async/pymodule.cpp | 329 ++++++++++ modules/atom.connection/pymodule.cpp | 354 ++++++++++ modules/atom.extra/pymodule.cpp | 682 ++++++++++++++++++++ modules/atom.io/CMakeLists.txt | 5 + modules/atom.io/component.cpp | 86 ++- modules/atom.io/package.json | 11 +- modules/atom.io/pymodule.cpp | 500 +++++++++++++-- modules/atom.search/pymodule.cpp | 177 +++++ modules/atom.sysinfo/pymodule.cpp | 113 +++- modules/atom.web/pymodule.cpp | 249 +++++++ modules/lithium.config/pymodule.cpp | 68 +- modules/lithium.tools/pymodule.cpp | 235 +++++++ pysrc/tools/hotspot.py | 111 ++++ pysrc/tools/nginx.py | 169 +++++ src/atom/algorithm/sha1.cpp | 138 ++++ src/atom/algorithm/sha1.hpp | 41 ++ src/atom/connection/async_fifoclient.cpp | 2 +- src/atom/connection/async_fifoclient.hpp | 2 +- src/atom/connection/async_udpserver.cpp | 2 +- src/atom/connection/async_udpserver.hpp | 3 +- src/atom/connection/fifoclient.hpp | 6 - src/atom/extra/boost/charconv.hpp | 3 + src/atom/extra/boost/system.hpp | 2 + src/atom/io/compress.cpp | 114 ++++ src/atom/type/argsview.hpp | 63 +- src/atom/type/auto_table.hpp | 202 ++++-- src/atom/type/expected.hpp | 712 +++++++++++++++------ src/atom/utils/argsview.hpp | 390 +++++++++-- src/atom/web/address.cpp | 10 +- src/atom/web/address.hpp | 2 + src/script/checker.cpp | 127 +++- src/script/custom/shm.sh | 83 ++- src/server/controller/ScriptController.hpp | 6 +- src/tools/libastro.cpp | 2 +- src/tools/libastro.hpp | 2 +- tests/atom/algorithm/annealing.cpp | 73 +++ tests/atom/type/argsview.cpp | 172 +++-- tests/atom/type/auto_table.cpp | 152 +++-- tests/atom/type/expected.cpp | 303 +++++---- 42 files changed, 5439 insertions(+), 855 deletions(-) create mode 100644 modules/atom.algorithm/pymodule.cpp create mode 100644 modules/atom.async/pymodule.cpp create mode 100644 modules/atom.connection/pymodule.cpp create mode 100644 modules/atom.extra/pymodule.cpp create mode 100644 modules/atom.search/pymodule.cpp create mode 100644 modules/atom.web/pymodule.cpp create mode 100644 modules/lithium.tools/pymodule.cpp create mode 100644 pysrc/tools/hotspot.py create mode 100644 pysrc/tools/nginx.py create mode 100644 src/atom/algorithm/sha1.cpp create mode 100644 src/atom/algorithm/sha1.hpp create mode 100644 tests/atom/algorithm/annealing.cpp diff --git a/README.md b/README.md index aabeeabe..0e9d1703 100644 --- a/README.md +++ b/README.md @@ -69,3 +69,6 @@ Alternatively, utilize the provided quick-build scripts to streamline the proces ### Intellectual Inspiration Embarking on the journey with Lithium, we embrace curiosity and an unwavering pursuit of knowledge, echoing the adapted verse which reminds us that every attempt, though fraught with challenges and setbacks, is a necessary step toward wisdom and understanding. Together, let us navigate the vast cosmos of astronomical imaging, our technology the vessel, innovation our sail, advancing relentlessly forward. + +
+
diff --git a/config/script/check.json b/config/script/check.json index 4353c796..78f6a9d1 100644 --- a/config/script/check.json +++ b/config/script/check.json @@ -1,116 +1,80 @@ { - "danger_patterns": [ + "powershell_danger_patterns": [ { - "pattern": "\\brm\\s+-rf\\b", - "reason": "Potentially destructive operation" + "pattern": "Remove-Item -Recurse -Force", + "reason": "Potentially dangerous command that can delete files recursively and forcefully." }, { - "pattern": "\\bsudo\\b", - "reason": "Elevated permissions, dangerous" - }, - { - "pattern": "\\bmkfs\\b", - "reason": "Filesystem creation, dangerous operation" - }, - { - "pattern": "\\|", - "reason": "Pipeline usage might lead to unintended consequences" - }, - { - "pattern": "2>&1\\s*>\\s*/dev/null", - "reason": "Redirection might hide errors" - }, - { - "pattern": "\\bkill\\s+-9\\b", - "reason": "Forcefully killing processes, consider using safer signal" - }, - { - "pattern": "eval\\s+", - "reason": "Using eval can lead to security vulnerabilities" - }, - { - "pattern": "\\bshutdown\\b", - "reason": "Potentially shuts down or restarts the system" - }, - { - "pattern": "\\bdd\\s+iflag=fullblock", - "reason": "Low-level data copying can lead to data loss or corruption" - }, - { - "pattern": "\\bchmod\\s+([0-7]{3,4}|[ugoa]+\\+?)\\s+[^/].*", - "reason": "Changing file permissions may lead to security issues" - }, - { - "pattern": "\\bchown\\s+[^:]+:[^/]+\\s+[^/].*", - "reason": "Changing file ownership may lead to access issues" - }, - { - "pattern": "\\bssh\\s+root@[^\\s]+", - "reason": "SSH access as root user can be risky" - }, - { - "pattern": "\\bwget\\s+[^\\s]+", - "reason": "Downloading files might lead to unintended consequences" - }, - { - "pattern": "\\bcurl\\s+[^\\s]+", - "reason": "Fetching data from the internet can be risky" + "pattern": "Stop-Process -Force", + "reason": "Forcefully stopping a process can lead to data loss." } ], - "sensitive_patterns": [ - { - "pattern": "password\\s*=\\s*['\"].*['\"]", - "reason": "Possible plaintext password" - }, - { - "pattern": "AWS_SECRET_ACCESS_KEY", - "reason": "AWS secret key detected" - }, - { - "pattern": "GITHUB_TOKEN", - "reason": "GitHub token detected" - }, - { - "pattern": "PRIVATE_KEY", - "reason": "Private key detected" - }, - { - "pattern": "DB_PASSWORD\\s*=\\s*['\"].*['\"]", - "reason": "Database password detected" - }, + "windows_cmd_danger_patterns": [ { - "pattern": "SECRET_KEY\\s*=\\s*['\"].*['\"]", - "reason": "Application secret key detected" + "pattern": "del /s /q", + "reason": "Potentially dangerous command that can delete files recursively and quietly." }, { - "pattern": "API_KEY\\s*=\\s*['\"].*['\"]", - "reason": "API key detected" - }, + "pattern": "taskkill /F", + "reason": "Forcefully killing a task can lead to data loss." + } + ], + "bash_danger_patterns": [ { - "pattern": "TOKEN\\s*=\\s*['\"].*['\"]", - "reason": "Authorization token detected" + "pattern": "rm -rf /", + "reason": "Potentially dangerous command that can delete all files recursively and forcefully." }, { - "pattern": "PASSWORD\\s*=\\s*['\"].*['\"]", - "reason": "Password detected" + "pattern": "kill -9", + "reason": "Forcefully killing a process can lead to data loss." } ], - "environment_patterns": [ + "python_danger_patterns": [ { - "pattern": "\\$\\{?\\w+\\}?", - "reason": "Environment variable dependency detected" + "pattern": "os.system", + "reason": "Using os.system can be dangerous as it allows execution of arbitrary commands." }, { - "pattern": "\\$\\{[^\\}]+\\}", - "reason": "Environment variable with braces detected" - }, + "pattern": "subprocess.call", + "reason": "Using subprocess.call can be dangerous as it allows execution of arbitrary commands." + } + ], + "ruby_danger_patterns": [ { - "pattern": "\\$\\w+", - "reason": "Environment variable placeholder detected" + "pattern": "system", + "reason": "Using system can be dangerous as it allows execution of arbitrary commands." }, { - "pattern": "\\${HOME|USER|SHELL|PATH}", - "reason": "Common environment variables detected" + "pattern": "exec", + "reason": "Using exec can be dangerous as it allows execution of arbitrary commands." } - ] -} + ], + "replacements": { + "Remove-Item -Recurse -Force": "Remove-Item -Recurse", + "Stop-Process -Force": "Stop-Process", + "rm -rf /": "find . -type f -delete", + "kill -9": "kill -TERM" + }, + "external_commands": { + "powershell": [ + "Invoke-WebRequest", + "Invoke-RestMethod" + ], + "cmd": [ + "curl", + "wget" + ], + "bash": [ + "curl", + "wget" + ], + "python": [ + "os.system", + "subprocess.call" + ], + "ruby": [ + "system", + "exec" + ] + } +} \ No newline at end of file diff --git a/modules/atom.algorithm/pymodule.cpp b/modules/atom.algorithm/pymodule.cpp new file mode 100644 index 00000000..dfce0558 --- /dev/null +++ b/modules/atom.algorithm/pymodule.cpp @@ -0,0 +1,436 @@ +#include +#include +#include + +#include "atom/algorithm/algorithm.hpp" +#include "atom/algorithm/annealing.hpp" +#include "atom/algorithm/base.hpp" +#include "atom/algorithm/bignumber.hpp" +#include "atom/algorithm/convolve.hpp" +#include "atom/algorithm/error_calibration.hpp" +#include "atom/algorithm/fnmatch.hpp" +#include "atom/algorithm/hash.hpp" +#include "atom/algorithm/huffman.hpp" +#include "atom/algorithm/math.hpp" +#include "atom/algorithm/matrix_compress.hpp" +#include "atom/algorithm/mhash.hpp" +#include "atom/algorithm/perlin.hpp" +#include "atom/algorithm/tea.hpp" +#include "atom/algorithm/weight.hpp" + +namespace py = pybind11; +using namespace atom::algorithm; + +template +void bind_advanced_error_calibration(py::module &m, const std::string &name) { + py::class_>(m, name.c_str()) + .def(py::init<>()) + .def("linear_calibrate", &AdvancedErrorCalibration::linearCalibrate) + .def("polynomial_calibrate", + &AdvancedErrorCalibration::polynomialCalibrate) + .def("apply", &AdvancedErrorCalibration::apply) + .def("print_parameters", &AdvancedErrorCalibration::printParameters) + .def("get_residuals", &AdvancedErrorCalibration::getResiduals) + .def("plot_residuals", &AdvancedErrorCalibration::plotResiduals) + .def("bootstrap_confidence_interval", + &AdvancedErrorCalibration::bootstrapConfidenceInterval) + .def("outlier_detection", + &AdvancedErrorCalibration::outlierDetection) + .def("cross_validation", &AdvancedErrorCalibration::crossValidation) + .def("get_slope", &AdvancedErrorCalibration::getSlope) + .def("get_intercept", &AdvancedErrorCalibration::getIntercept) + .def("get_r_squared", &AdvancedErrorCalibration::getRSquared) + .def("get_mse", &AdvancedErrorCalibration::getMse) + .def("get_mae", &AdvancedErrorCalibration::getMae); +} + +template +void bind_weight_selector(py::module &m, const std::string &name) { + py::class_>(m, name.c_str()) + .def(py::init, + std::unique_ptr< + typename WeightSelector::SelectionStrategy>>(), + py::arg("input_weights"), + py::arg("custom_strategy") = std::make_unique< + typename WeightSelector::DefaultSelectionStrategy>()) + .def("set_selection_strategy", &WeightSelector::setSelectionStrategy) + .def("select", &WeightSelector::select) + .def("select_multiple", &WeightSelector::selectMultiple) + .def("update_weight", &WeightSelector::updateWeight) + .def("add_weight", &WeightSelector::addWeight) + .def("remove_weight", &WeightSelector::removeWeight) + .def("normalize_weights", &WeightSelector::normalizeWeights) + // .def("apply_function_to_weights", + // &WeightSelector::applyFunctionToWeights) + .def("batch_update_weights", &WeightSelector::batchUpdateWeights) + .def("get_weight", &WeightSelector::getWeight) + .def("get_max_weight_index", &WeightSelector::getMaxWeightIndex) + .def("get_min_weight_index", &WeightSelector::getMinWeightIndex) + .def("size", &WeightSelector::size) + .def("get_weights", &WeightSelector::getWeights) + .def("get_total_weight", &WeightSelector::getTotalWeight) + .def("reset_weights", &WeightSelector::resetWeights) + .def("scale_weights", &WeightSelector::scaleWeights) + .def("get_average_weight", &WeightSelector::getAverageWeight) + .def("print_weights", &WeightSelector::printWeights); + + py::class_::SelectionStrategy, + std::shared_ptr::SelectionStrategy>>( + m, (name + "SelectionStrategy").c_str()) + .def("select", &WeightSelector::SelectionStrategy::select); + + py::class_< + typename WeightSelector::DefaultSelectionStrategy, + typename WeightSelector::SelectionStrategy, + std::shared_ptr::DefaultSelectionStrategy>>( + m, (name + "DefaultSelectionStrategy").c_str()) + .def(py::init<>()); + + py::class_::BottomHeavySelectionStrategy, + typename WeightSelector::SelectionStrategy, + std::shared_ptr< + typename WeightSelector::BottomHeavySelectionStrategy>>( + m, (name + "BottomHeavySelectionStrategy").c_str()) + .def(py::init<>()); + + py::class_< + typename WeightSelector::RandomSelectionStrategy, + typename WeightSelector::SelectionStrategy, + std::shared_ptr::RandomSelectionStrategy>>( + m, (name + "RandomSelectionStrategy").c_str()) + .def(py::init()); + + py::class_::WeightedRandomSampler>( + m, (name + "WeightedRandomSampler").c_str()) + .def(py::init<>()) + .def("sample", &WeightSelector::WeightedRandomSampler::sample); + + py::class_, + typename WeightSelector::SelectionStrategy, + std::shared_ptr>>( + m, (name + "TopHeavySelectionStrategy").c_str()) + .def(py::init<>()); +} + +PYBIND11_MODULE(algorithm, m) { + py::class_(m, "KMP") + .def(py::init()) + .def("search", &KMP::search) + .def("set_pattern", &KMP::setPattern); + + py::class_(m, "BoyerMoore") + .def(py::init()) + .def("search", &BoyerMoore::search) + .def("set_pattern", &BoyerMoore::setPattern); + + py::class_>(m, "BloomFilter") + .def(py::init()) + .def("insert", &BloomFilter<1024>::insert) + .def("contains", &BloomFilter<1024>::contains); + + py::enum_(m, "AnnealingStrategy") + .value("LINEAR", AnnealingStrategy::LINEAR) + .value("EXPONENTIAL", AnnealingStrategy::EXPONENTIAL) + .value("LOGARITHMIC", AnnealingStrategy::LOGARITHMIC) + .export_values(); + + py::class_(m, "TSP") + .def(py::init> &>()) + .def("energy", &TSP::energy) + .def("neighbor", &TSP::neighbor) + .def("random_solution", &TSP::randomSolution); + + /* + py::class_>>(m, + "SimulatedAnnealing") + .def(py::init()) + .def("set_cooling_schedule", + &SimulatedAnnealing>::setCoolingSchedule) + .def("set_progress_callback", + &SimulatedAnnealing>::setProgressCallback) .def("set_stop_condition", + &SimulatedAnnealing>::setStopCondition) + .def("optimize", &SimulatedAnnealing>::optimize) .def("get_best_energy", + &SimulatedAnnealing>::getBestEnergy); + */ + + m.def("base64_encode", &base64Encode, "Base64 encoding function"); + m.def("base64_decode", &base64Decode, "Base64 decoding function"); + m.def("fbase64_encode", &fbase64Encode, "Faster Base64 encoding function"); + m.def("fbase64_decode", &fbase64Decode, "Faster Base64 decoding function"); + m.def("xor_encrypt", &xorEncrypt, "Encrypt string using XOR algorithm"); + m.def("xor_decrypt", &xorDecrypt, "Decrypt string using XOR algorithm"); + + py::class_(m, "BigNumber") + .def(py::init()) + .def(py::init()) + .def("add", &BigNumber::add) + .def("subtract", &BigNumber::subtract) + .def("multiply", &BigNumber::multiply) + .def("divide", &BigNumber::divide) + .def("pow", &BigNumber::pow) + .def("get_string", &BigNumber::getString) + .def("set_string", &BigNumber::setString) + .def("negate", &BigNumber::negate) + .def("trim_leading_zeros", &BigNumber::trimLeadingZeros) + .def("equals", py::overload_cast(&BigNumber::equals, + py::const_)) + .def("equals", py::overload_cast(&BigNumber::equals, + py::const_)) + .def("equals", py::overload_cast( + &BigNumber::equals, py::const_)) + .def("digits", &BigNumber::digits) + .def("is_negative", &BigNumber::isNegative) + .def("is_positive", &BigNumber::isPositive) + .def("is_even", &BigNumber::isEven) + .def("is_odd", &BigNumber::isOdd) + .def("abs", &BigNumber::abs) + .def("__str__", &BigNumber::getString) + .def(py::self + py::self) + .def(py::self - py::self) + .def(py::self * py::self) + .def(py::self / py::self) + .def(py::self == py::self) + .def(py::self > py::self) + .def(py::self < py::self) + .def(py::self >= py::self) + .def(py::self <= py::self) + .def("__iadd__", &BigNumber::operator+=) + .def("__isub__", &BigNumber::operator-=) + .def("__imul__", &BigNumber::operator*=) + .def("__idiv__", &BigNumber::operator/=) + .def("__neg__", &BigNumber::negate) + .def("__abs__", &BigNumber::abs) + .def("__len__", &BigNumber::digits) + .def("__getitem__", &BigNumber::operator[]) + .def( + "__iter__", + [](const BigNumber &bn) { + return py::make_iterator(bn.getString().begin(), + bn.getString().end()); + }, + py::keep_alive<0, 1>()); + + m.def("convolve", &convolve, "Perform 1D convolution operation", + py::arg("input"), py::arg("kernel")); + m.def("deconvolve", &deconvolve, "Perform 1D deconvolution operation", + py::arg("input"), py::arg("kernel")); + m.def("convolve2d", &convolve2D, "Perform 2D convolution operation", + py::arg("input"), py::arg("kernel"), py::arg("num_threads") = 1); + m.def("deconvolve2d", &deconvolve2D, "Perform 2D deconvolution operation", + py::arg("signal"), py::arg("kernel"), py::arg("num_threads") = 1); + m.def("dft2d", &dfT2D, "Perform 2D discrete Fourier transform", + py::arg("signal"), py::arg("num_threads") = 1); + m.def("idft2d", &idfT2D, "Perform 2D inverse discrete Fourier transform", + py::arg("spectrum"), py::arg("num_threads") = 1); + m.def("generate_gaussian_kernel", &generateGaussianKernel, + "Generate 2D Gaussian kernel", py::arg("size"), py::arg("sigma")); + m.def("apply_gaussian_filter", &applyGaussianFilter, + "Apply Gaussian filter", py::arg("image"), py::arg("kernel")); + + bind_advanced_error_calibration(m, "AdvancedErrorCalibrationFloat"); + bind_advanced_error_calibration(m, + "AdvancedErrorCalibrationDouble"); + + m.def("fnmatch", &fnmatch, "Match string with specified pattern", + py::arg("pattern"), py::arg("string"), py::arg("flags") = 0); + m.def("filter", + py::overload_cast &, std::string_view, + int>(&filter), + "Filter vector of strings based on specified pattern", + py::arg("names"), py::arg("pattern"), py::arg("flags") = 0); + m.def("filter", + py::overload_cast &, + const std::vector &, int>(&filter), + "Filter vector of strings based on multiple specified patterns", + py::arg("names"), py::arg("patterns"), py::arg("flags") = 0); + m.def("translate", &translate, + "Translate pattern to different representation", py::arg("pattern"), + py::arg("result"), py::arg("flags") = 0); + + m.def("compute_hash", + py::overload_cast(&computeHash), + "Compute hash value of a single hashable value"); + m.def("compute_hash", + py::overload_cast &>( + &computeHash), + "Compute hash value of a vector of strings"); + m.def("compute_hash", + py::overload_cast &>( + &computeHash), + "Compute hash value of a tuple of strings"); + m.def("compute_hash", + py::overload_cast &>( + &computeHash), + "Compute hash value of an array of strings"); + // m.def("compute_hash", py::overload_cast(&computeHash), + // "Compute hash value of std::any"); + m.def("hash", &hash, + "Compute hash value of a string using FNV-1a algorithm", + py::arg("str"), py::arg("basis") = 2166136261U); + m.def( + "operator" + "_hash", + &operator""_hash, "Compute hash value of a string literal"); + + py::class_>(m, "HuffmanNode") + .def(py::init()) + .def_readwrite("data", &HuffmanNode::data) + .def_readwrite("frequency", &HuffmanNode::frequency) + .def_readwrite("left", &HuffmanNode::left) + .def_readwrite("right", &HuffmanNode::right); + + m.def("create_huffman_tree", &createHuffmanTree, "Create Huffman tree", + py::arg("frequencies")); + + m.def("generate_huffman_codes", &generateHuffmanCodes, + "Generate Huffman codes", py::arg("root"), py::arg("code"), + py::arg("huffman_codes")); + + m.def("compress_text", &compressText, "Compress text", py::arg("text"), + py::arg("huffman_codes")); + + m.def("decompress_text", &decompressText, "Decompress text", + py::arg("compressed_text"), py::arg("root")); + + m.def("mul_div64", &mulDiv64, + "Perform 64-bit multiplication and division operation", + py::arg("operant"), py::arg("multiplier"), py::arg("divider")); + m.def("safe_add", &safeAdd, "Perform safe addition operation", py::arg("a"), + py::arg("b")); + m.def("safe_mul", &safeMul, "Perform safe multiplication operation", + py::arg("a"), py::arg("b")); + m.def("rotl64", &rotl64, "Perform 64-bit integer left rotation operation", + py::arg("n"), py::arg("c")); + m.def("rotr64", &rotr64, "Perform 64-bit integer right rotation operation", + py::arg("n"), py::arg("c")); + m.def("clz64", &clz64, "Count leading zeros of a 64-bit integer", + py::arg("x")); + m.def("normalize", &normalize, "Normalize a 64-bit integer", py::arg("x")); + m.def("safe_sub", &safeSub, "Perform safe subtraction operation", + py::arg("a"), py::arg("b")); + m.def("safe_div", &safeDiv, "Perform safe division operation", py::arg("a"), + py::arg("b")); + m.def("bit_reverse64", &bitReverse64, + "Compute bitwise reversal of a 64-bit integer", py::arg("n")); + m.def("approximate_sqrt", &approximateSqrt, + "Approximate square root of a 64-bit integer", py::arg("n")); + m.def("gcd64", &gcd64, + "Compute greatest common divisor of two 64-bit integers", + py::arg("a"), py::arg("b")); + m.def("lcm64", &lcm64, + "Compute least common multiple of two 64-bit integers", py::arg("a"), + py::arg("b")); + m.def("is_power_of_two", &isPowerOfTwo, + "Check if a 64-bit integer is a power of two", py::arg("n")); + m.def("next_power_of_two", &nextPowerOfTwo, + "Compute the next power of two of a 64-bit integer", py::arg("n")); + + py::class_(m, "MatrixCompressor") + .def_static("compress", &MatrixCompressor::compress, "Compress matrix", + py::arg("matrix")) + .def_static("decompress", &MatrixCompressor::decompress, + "Decompress data to matrix", py::arg("compressed"), + py::arg("rows"), py::arg("cols")) + .def_static("print_matrix", &MatrixCompressor::printMatrix, + "Print matrix", py::arg("matrix")) + .def_static("generate_random_matrix", + &MatrixCompressor::generateRandomMatrix, + "Generate random matrix", py::arg("rows"), py::arg("cols"), + py::arg("charset") = "ABCD") + .def_static("save_compressed_to_file", + &MatrixCompressor::saveCompressedToFile, + "Save compressed data to file", py::arg("compressed"), + py::arg("filename")) + .def_static("load_compressed_from_file", + &MatrixCompressor::loadCompressedFromFile, + "Load compressed data from file", py::arg("filename")) + .def_static("calculate_compression_ratio", + &MatrixCompressor::calculateCompressionRatio, + "Calculate compression ratio", py::arg("original"), + py::arg("compressed")) + .def_static("downsample", &MatrixCompressor::downsample, + "Downsample matrix", py::arg("matrix"), py::arg("factor")) + .def_static("upsample", &MatrixCompressor::upsample, "Upsample matrix", + py::arg("matrix"), py::arg("factor")) + .def_static("calculate_mse", &MatrixCompressor::calculateMSE, + "Calculate mean squared error between two matrices", + py::arg("matrix1"), py::arg("matrix2")); + +#if ATOM_ENABLE_DEBUG + m.def("performance_test", &performanceTest, + "Run performance test for matrix compression and decompression", + py::arg("rows"), py::arg("cols")); +#endif + + m.def("hexstring_from_data", &hexstringFromData, + "Convert string to hexadecimal string representation", + py::arg("data")); + m.def("data_from_hexstring", &dataFromHexstring, + "Convert hexadecimal string representation to binary data", + py::arg("data")); + + py::class_(m, "MinHash") + .def(py::init(), "Construct a MinHash object", + py::arg("num_hashes")) + .def( + "compute_signature", + [](const MinHash &self, const std::vector &set) { + return self.computeSignature(set); + }, + "Compute MinHash signature for a given set", py::arg("set")) + .def_static("jaccard_index", &MinHash::jaccardIndex, + "Compute Jaccard index between two sets", py::arg("sig1"), + py::arg("sig2")); + + m.def( + "keccak256", + [](const std::string &input) { + auto hash = keccak256( + reinterpret_cast(input.data()), input.size()); + return std::vector(hash.begin(), hash.end()); + }, + "Compute Keccak-256 hash value of input data", py::arg("input")); + + py::class_(m, "PerlinNoise") + .def(py::init(), "Construct a PerlinNoise object", + py::arg("seed") = std::default_random_engine::default_seed) + .def("noise", &PerlinNoise::noise, "Generate Perlin noise", + py::arg("x"), py::arg("y"), py::arg("z")) + .def("octave_noise", &PerlinNoise::octaveNoise, + "Generate octave Perlin noise", py::arg("x"), py::arg("y"), + py::arg("z"), py::arg("octaves"), py::arg("persistence")) + .def("generate_noise_map", &PerlinNoise::generateNoiseMap, + "Generate noise map", py::arg("width"), py::arg("height"), + py::arg("scale"), py::arg("octaves"), py::arg("persistence"), + py::arg("lacunarity"), + py::arg("seed") = std::default_random_engine::default_seed); + + m.def("tea_encrypt", &teaEncrypt, + "Encrypt two 32-bit values using TEA algorithm", py::arg("value0"), + py::arg("value1"), py::arg("key")); + m.def("tea_decrypt", &teaDecrypt, + "Decrypt two 32-bit values using TEA algorithm", py::arg("value0"), + py::arg("value1"), py::arg("key")); + m.def("xxtea_encrypt", &xxteaEncrypt, + "Encrypt vector of 32-bit values using XXTEA algorithm", + py::arg("input_data"), py::arg("input_key")); + m.def("xxtea_decrypt", &xxteaDecrypt, + "Decrypt vector of 32-bit values using XXTEA algorithm", + py::arg("input_data"), py::arg("input_key")); + m.def("xtea_encrypt", &xteaEncrypt, + "Encrypt two 32-bit values using XTEA algorithm", py::arg("value0"), + py::arg("value1"), py::arg("key")); + m.def("xtea_decrypt", &xteaDecrypt, + "Decrypt two 32-bit values using XTEA algorithm", py::arg("value0"), + py::arg("value1"), py::arg("key")); + m.def("to_uint32_vector", &toUint32Vector, + "Convert byte array to vector of 32-bit unsigned integers", + py::arg("data")); + m.def("to_byte_array", &toByteArray, + "Convert vector of 32-bit unsigned integers back to byte array", + py::arg("data")); + + bind_weight_selector(m, "WeightSelectorDouble"); +} \ No newline at end of file diff --git a/modules/atom.async/pymodule.cpp b/modules/atom.async/pymodule.cpp new file mode 100644 index 00000000..43425aeb --- /dev/null +++ b/modules/atom.async/pymodule.cpp @@ -0,0 +1,329 @@ +#include +#include +#include +#include + +#include "atom/async/limiter.hpp" +#include "atom/async/message_bus.hpp" +#include "atom/async/message_queue.hpp" +#include "atom/async/pool.hpp" +#include "atom/async/safetype.hpp" +#include "atom/async/timer.hpp" +#include "atom/async/trigger.hpp" + +namespace py = pybind11; +using namespace atom::async; + +template +void bind_message_queue(py::module &m, const std::string &name) { + py::class_>(m, name.c_str()) + .def(py::init(), "Constructor", + py::arg("io_context")) + .def("subscribe", &MessageQueue::subscribe, + "Subscribe to messages with a callback and optional filter and " + "timeout", + py::arg("callback"), py::arg("subscriber_name"), + py::arg("priority") = 0, py::arg("filter") = nullptr, + py::arg("timeout") = std::chrono::milliseconds::zero()) + .def("unsubscribe", &MessageQueue::unsubscribe, + "Unsubscribe from messages using the given callback", + py::arg("callback")) + .def("publish", &MessageQueue::publish, + "Publish a message to the queue, with an optional priority", + py::arg("message"), py::arg("priority") = 0) + .def("start_processing", &MessageQueue::startProcessing, + "Start processing messages in the queue") + .def("stop_processing", &MessageQueue::stopProcessing, + "Stop processing messages in the queue") + .def("get_message_count", &MessageQueue::getMessageCount, + "Get the number of messages currently in the queue") + .def("get_subscriber_count", &MessageQueue::getSubscriberCount, + "Get the number of subscribers currently subscribed to the queue") + .def("cancel_messages", &MessageQueue::cancelMessages, + "Cancel specific messages that meet a given condition", + py::arg("cancel_condition")); +} + +template +void bind_trigger(py::module &m, const std::string &name) { + using TriggerType = Trigger; + py::class_(m, name.c_str()) + .def(py::init<>()) + .def("registerCallback", &TriggerType::registerCallback, + py::arg("event"), py::arg("callback"), + py::arg("priority") = TriggerType::CallbackPriority::Normal) + .def("unregisterCallback", &TriggerType::unregisterCallback, + py::arg("event"), py::arg("callback")) + .def("trigger", &TriggerType::trigger, py::arg("event"), + py::arg("param")) + .def("scheduleTrigger", &TriggerType::scheduleTrigger, py::arg("event"), + py::arg("param"), py::arg("delay")) + .def("scheduleAsyncTrigger", &TriggerType::scheduleAsyncTrigger, + py::arg("event"), py::arg("param")) + .def("cancelTrigger", &TriggerType::cancelTrigger, py::arg("event")) + .def("cancelAllTriggers", &TriggerType::cancelAllTriggers); + + py::enum_( + m, (name + "CallbackPriority").c_str()) + .value("High", TriggerType::CallbackPriority::High) + .value("Normal", TriggerType::CallbackPriority::Normal) + .value("Low", TriggerType::CallbackPriority::Low); +} + +template +void bind_safe_type(py::module &m, const std::string &name) { + py::class_>(m, + std::format("LockFreeStack{}", name).c_str()) + .def(py::init<>()) + .def("push", + (void(LockFreeStack::*)(const T &)) & LockFreeStack::push) + .def("push", (void(LockFreeStack::*)(T &&)) & LockFreeStack::push) + .def("pop", &LockFreeStack::pop) + .def("top", &LockFreeStack::top) + .def("empty", &LockFreeStack::empty) + .def("size", &LockFreeStack::size); + + py::class_>( + m, std::format("ThreadSafeVector{}", name).c_str()) + .def(py::init()) + .def("pushBack", (void(ThreadSafeVector::*)(const T &)) & + ThreadSafeVector::pushBack) + .def("pushBack", (void(ThreadSafeVector::*)(T &&)) & + ThreadSafeVector::pushBack) + .def("popBack", &ThreadSafeVector::popBack) + .def("at", &ThreadSafeVector::at) + .def("empty", &ThreadSafeVector::empty) + .def("getSize", &ThreadSafeVector::getSize) + .def("getCapacity", &ThreadSafeVector::getCapacity) + .def("clear", &ThreadSafeVector::clear) + .def("shrinkToFit", &ThreadSafeVector::shrinkToFit) + .def("front", &ThreadSafeVector::front) + .def("back", &ThreadSafeVector::back) + .def("__getitem__", &ThreadSafeVector::operator[]); + + py::class_>(m, std::format("LockFreeList{}", name).c_str()) + .def(py::init<>()) + .def("pushFront", &LockFreeList::pushFront) + .def("popFront", &LockFreeList::popFront) + .def("empty", &LockFreeList::empty); +} + +PYBIND11_MODULE(async, m) { + py::class_>(m, "MessageBus") + .def(py::init(), "Constructor", + py::arg("io_context")) + .def_static("create_shared", &MessageBus::createShared, + "Create a shared instance of MessageBus", + py::arg("io_context")) + .def( + "publish", + [](MessageBus &self, const std::string &name, + const py::object &message, + std::optional delay) { + if (py::isinstance(message)) { + self.publish(name, message.cast(), delay); + } else if (py::isinstance(message)) { + self.publish(name, message.cast(), delay); + } else if (py::isinstance(message)) { + self.publish(name, message.cast(), delay); + } else { + throw std::runtime_error("Unsupported message type"); + } + }, + "Publish a message to the bus", py::arg("name"), py::arg("message"), + py::arg("delay") = std::nullopt) + .def( + "publish_global", + [](MessageBus &self, const py::object &message) { + if (py::isinstance(message)) { + self.publishGlobal(message.cast()); + } else if (py::isinstance(message)) { + self.publishGlobal(message.cast()); + } else if (py::isinstance(message)) { + self.publishGlobal(message.cast()); + } else { + throw std::runtime_error("Unsupported message type"); + } + }, + "Publish a message to all subscribers globally", py::arg("message")) + .def( + "subscribe", + [](MessageBus &self, const std::string &name, py::function handler, + bool async, bool once, py::function filter) { + if (handler.is_none()) { + throw std::runtime_error("Handler function cannot be None"); + } + if (filter.is_none()) { + filter = py::cpp_function( + [](const py::object &) { return true; }); + } + return self.subscribe( + name, + [handler](const std::string &msg) { + py::gil_scoped_acquire acquire; + handler(msg); + }, + async, once, + [filter](const std::string &msg) { + py::gil_scoped_acquire acquire; + return filter(msg).cast(); + }); + }, + "Subscribe to a message", py::arg("name"), py::arg("handler"), + py::arg("async") = true, py::arg("once") = false, + py::arg("filter") = py::none()) + .def("unsubscribe", &MessageBus::unsubscribe, + "Unsubscribe from a message using the given token", + py::arg("token")) + .def("unsubscribe_all", &MessageBus::unsubscribeAll, + "Unsubscribe all handlers for a given message name or namespace", + py::arg("name")) + .def("get_subscriber_count", + &MessageBus::getSubscriberCount, + "Get the number of subscribers for a given message name or " + "namespace", + py::arg("name")) + .def("has_subscriber", &MessageBus::hasSubscriber, + "Check if there are any subscribers for a given message name or " + "namespace", + py::arg("name")) + .def("clear_all_subscribers", &MessageBus::clearAllSubscribers, + "Clear all subscribers") + .def("get_active_namespaces", &MessageBus::getActiveNamespaces, + "Get the list of active namespaces") + .def("get_message_history", &MessageBus::getMessageHistory, + "Get the message history for a given message name", + py::arg("name"), + py::arg("count") = MessageBus::K_MAX_HISTORY_SIZE); + + bind_message_queue(m, "StringMessageQueue"); + bind_message_queue(m, "IntMessageQueue"); + bind_message_queue(m, "DoubleMessageQueue"); + + py::class_>>(m, "ThreadSafeQueue") + .def(py::init<>()) + .def("push_back", &ThreadSafeQueue>::pushBack, + "Push a task to the back of the queue", py::arg("value")) + .def("push_front", &ThreadSafeQueue>::pushFront, + "Push a task to the front of the queue", py::arg("value")) + .def("empty", &ThreadSafeQueue>::empty, + "Check if the queue is empty") + .def("size", &ThreadSafeQueue>::size, + "Get the size of the queue") + .def("pop_front", &ThreadSafeQueue>::popFront, + "Pop a task from the front of the queue") + .def("pop_back", &ThreadSafeQueue>::popBack, + "Pop a task from the back of the queue") + .def("steal", &ThreadSafeQueue>::steal, + "Steal a task from the back of the queue") + .def("rotate_to_front", + &ThreadSafeQueue>::rotateToFront, + "Rotate a task to the front of the queue", py::arg("item")) + .def("copy_front_and_rotate_to_back", + &ThreadSafeQueue>::copyFrontAndRotateToBack, + "Copy the front task and rotate it to the back of the queue") + .def("clear", &ThreadSafeQueue>::clear, + "Clear the queue"); + + py::class_>(m, "ThreadPool") + .def(py::init(), "Constructor", + py::arg("number_of_threads") = std::thread::hardware_concurrency()) + .def( + "enqueue", + [](ThreadPool<> &self, py::function func) { + return self.enqueue([func]() { + py::gil_scoped_acquire acquire; + func(); + }); + }, + "Enqueue a task and return a future") + .def( + "enqueue_detach", + [](ThreadPool<> &self, py::function func) { + self.enqueueDetach([func]() { + py::gil_scoped_acquire acquire; + func(); + }); + }, + "Enqueue a task and detach it") + .def("size", &ThreadPool<>::size, + "Get the number of threads in the pool") + .def("wait_for_tasks", &ThreadPool<>::waitForTasks, + "Wait for all tasks to complete"); + + py::class_(m, "TimerTask") + .def(py::init, unsigned int, int, int>(), + py::arg("func"), py::arg("delay"), py::arg("repeatCount"), + py::arg("priority")) + .def("run", &TimerTask::run) + .def("getNextExecutionTime", &TimerTask::getNextExecutionTime) + .def("__lt__", &TimerTask::operator<) + .def_readwrite("m_func", &TimerTask::m_func) + .def_readwrite("m_delay", &TimerTask::m_delay) + .def_readwrite("m_repeatCount", &TimerTask::m_repeatCount) + .def_readwrite("m_priority", &TimerTask::m_priority) + .def_readwrite("m_nextExecutionTime", &TimerTask::m_nextExecutionTime); + + py::class_(m, "Timer") + .def(py::init<>()) + .def("setTimeout", &Timer::setTimeout>, + py::arg("func"), py::arg("delay")) + .def("setInterval", &Timer::setInterval>, + py::arg("func"), py::arg("interval"), py::arg("repeatCount"), + py::arg("priority")) + .def("now", &Timer::now) + .def("cancelAllTasks", &Timer::cancelAllTasks) + .def("pause", &Timer::pause) + .def("resume", &Timer::resume) + .def("stop", &Timer::stop) + .def("setCallback", &Timer::setCallback>, + py::arg("func")) + .def("getTaskCount", &Timer::getTaskCount); + + bind_trigger(m, "TriggerInt"); + bind_trigger(m, "TriggerString"); + bind_trigger(m, "TriggerDouble"); + bind_trigger>(m, "TriggerFunction"); + + bind_safe_type(m, "Int"); + bind_safe_type(m, "String"); + bind_safe_type(m, "Double"); + bind_safe_type(m, "Float"); + + py::class_(m, "RateLimiterSettings") + .def(py::init(), + py::arg("max_requests") = 5, + py::arg("time_window") = std::chrono::seconds(1)) + .def_readwrite("maxRequests", &RateLimiter::Settings::maxRequests) + .def_readwrite("timeWindow", &RateLimiter::Settings::timeWindow); + + py::class_(m, "RateLimiter") + .def(py::init<>()) + .def("acquire", &RateLimiter::acquire) + .def("setFunctionLimit", &RateLimiter::setFunctionLimit) + .def("pause", &RateLimiter::pause) + .def("resume", &RateLimiter::resume) + .def("printLog", &RateLimiter::printLog) + .def("getRejectedRequests", &RateLimiter::getRejectedRequests); + + py::class_(m, "Debounce") + .def(py::init, std::chrono::milliseconds, bool, + std::optional>(), + py::arg("func"), py::arg("delay"), py::arg("leading") = false, + py::arg("maxWait") = std::nullopt) + .def("__call__", &Debounce::operator()) + .def("cancel", &Debounce::cancel) + .def("flush", &Debounce::flush) + .def("reset", &Debounce::reset) + .def("callCount", &Debounce::callCount); + + py::class_(m, "Throttle") + .def(py::init, std::chrono::milliseconds, bool, + std::optional>(), + py::arg("func"), py::arg("interval"), py::arg("leading") = false, + py::arg("maxWait") = std::nullopt) + .def("__call__", &Throttle::operator()) + .def("cancel", &Throttle::cancel) + .def("reset", &Throttle::reset) + .def("callCount", &Throttle::callCount); +} \ No newline at end of file diff --git a/modules/atom.connection/pymodule.cpp b/modules/atom.connection/pymodule.cpp new file mode 100644 index 00000000..6de7f844 --- /dev/null +++ b/modules/atom.connection/pymodule.cpp @@ -0,0 +1,354 @@ +#include +#include +#include +#include + +#include "atom/connection/async_fifoclient.hpp" +#include "atom/connection/async_fifoserver.hpp" +#include "atom/connection/async_sockethub.hpp" +#include "atom/connection/async_udpclient.hpp" +#include "atom/connection/async_udpserver.hpp" + +#include "atom/connection/fifoclient.hpp" +#include "atom/connection/fifoserver.hpp" +#include "atom/connection/sockethub.hpp" +#if __has_include() +#include "atom/connection/sshclient.hpp" +#endif +#include "atom/connection/sshserver.hpp" +#include "atom/connection/tcpclient.hpp" +#include "atom/connection/ttybase.hpp" +#include "atom/connection/udpclient.hpp" +#include "atom/connection/udpserver.hpp" + +namespace py = pybind11; +using namespace atom::connection; + +PYBIND11_MODULE(connection, m) { + m.doc() = "Atom Connection Module"; + + py::class_(m, "FifoClient") + .def(py::init(), py::arg("fifo_path")) + .def("write", &atom::async::connection::FifoClient::write, + py::arg("data"), py::arg("timeout") = std::nullopt, + "Writes data to the FIFO with an optional timeout.") + .def("read", &atom::async::connection::FifoClient::read, + py::arg("timeout") = std::nullopt, + "Reads data from the FIFO with an optional timeout.") + .def("is_open", &atom::async::connection::FifoClient::isOpen, + "Checks if the FIFO is currently open.") + .def("close", &atom::async::connection::FifoClient::close, + "Closes the FIFO."); + + py::class_(m, "FifoServer") + .def(py::init(), py::arg("fifo_path")) + .def("start", &atom::async::connection::FifoServer::start, + "Starts the server to listen for messages.") + .def("stop", &atom::async::connection::FifoServer::stop, + "Stops the server.") + .def("is_running", &atom::async::connection::FifoServer::isRunning, + "Checks if the server is running."); + + py::class_(m, "SocketHub") + .def(py::init(), py::arg("use_ssl") = false) + .def("start", &atom::async::connection::SocketHub::start, + py::arg("port"), "Starts the socket hub on the specified port.") + .def("stop", &atom::async::connection::SocketHub::stop, + "Stops the socket hub.") + .def("add_handler", &atom::async::connection::SocketHub::addHandler, + py::arg("handler"), + "Adds a message handler for incoming messages.") + .def("add_connect_handler", + &atom::async::connection::SocketHub::addConnectHandler, + py::arg("handler"), "Adds a handler for new connections.") + .def("add_disconnect_handler", + &atom::async::connection::SocketHub::addDisconnectHandler, + py::arg("handler"), "Adds a handler for disconnections.") + .def("broadcast_message", + &atom::async::connection::SocketHub::broadcastMessage, + py::arg("message"), + "Broadcasts a message to all connected clients.") + .def("send_message_to_client", + &atom::async::connection::SocketHub::sendMessageToClient, + py::arg("client_id"), py::arg("message"), + "Sends a message to a specific client.") + .def("is_running", &atom::async::connection::SocketHub::isRunning, + "Checks if the socket hub is currently running."); + + py::class_(m, "UdpClient") + .def(py::init<>()) + .def("bind", &atom::async::connection::UdpClient::bind, py::arg("port"), + "Binds the client to a specific port for receiving data.") + .def("send", &atom::async::connection::UdpClient::send, py::arg("host"), + py::arg("port"), py::arg("data"), + "Sends data to a specified host and port.") + .def("receive", &atom::async::connection::UdpClient::receive, + py::arg("size"), py::arg("remoteHost"), py::arg("remotePort"), + py::arg("timeout") = std::chrono::milliseconds::zero(), + "Receives data from a remote host.") + .def("set_on_data_received_callback", + &atom::async::connection::UdpClient::setOnDataReceivedCallback, + py::arg("callback"), + "Sets the callback function to be called when data is received.") + .def("set_on_error_callback", + &atom::async::connection::UdpClient::setOnErrorCallback, + py::arg("callback"), + "Sets the callback function to be called when an error occurs.") + .def("start_receiving", + &atom::async::connection::UdpClient::startReceiving, + py::arg("bufferSize"), "Starts receiving data asynchronously.") + .def("stop_receiving", + &atom::async::connection::UdpClient::stopReceiving, + "Stops receiving data."); + + py::class_(m, "UdpSocketHub") + .def(py::init<>()) + .def("start", &atom::async::connection::UdpSocketHub::start, + py::arg("port"), + "Starts the UDP socket hub and binds it to the specified port.") + .def("stop", &atom::async::connection::UdpSocketHub::stop, + "Stops the UDP socket hub.") + .def("is_running", &atom::async::connection::UdpSocketHub::isRunning, + "Checks if the UDP socket hub is currently running.") + .def("add_message_handler", + &atom::async::connection::UdpSocketHub::addMessageHandler, + py::arg("handler"), + "Adds a message handler function to the UDP socket hub.") + .def("remove_message_handler", + &atom::async::connection::UdpSocketHub::removeMessageHandler, + py::arg("handler"), + "Removes a message handler function from the UDP socket hub.") + .def("send_to", &atom::async::connection::UdpSocketHub::sendTo, + py::arg("message"), py::arg("ip"), py::arg("port"), + "Sends a message to the specified IP address and port."); + + py::class_(m, "FifoClient") + .def(py::init(), py::arg("fifo_path")) + .def("write", &FifoClient::write, py::arg("data"), + py::arg("timeout") = std::nullopt, + "Writes data to the FIFO with an optional timeout.") + .def("read", &FifoClient::read, py::arg("timeout") = std::nullopt, + "Reads data from the FIFO with an optional timeout.") + .def("is_open", &FifoClient::isOpen, + "Checks if the FIFO is currently open.") + .def("close", &FifoClient::close, "Closes the FIFO."); + + py::class_(m, "FIFOServer") + .def(py::init(), py::arg("fifo_path")) + .def("send_message", &FIFOServer::sendMessage, py::arg("message"), + "Sends a message through the FIFO pipe.") + .def("start", &FIFOServer::start, "Starts the FIFO server.") + .def("stop", &FIFOServer::stop, "Stops the FIFO server.") + .def("is_running", &FIFOServer::isRunning, + "Checks if the FIFO server is running."); + + py::class_(m, "SocketHub") + .def(py::init<>()) + .def("start", &SocketHub::start, py::arg("port"), + "Starts the socket service on the specified port.") + .def("stop", &SocketHub::stop, "Stops the socket service.") + .def("add_handler", &SocketHub::addHandler, py::arg("handler"), + "Adds a message handler for incoming messages.") + .def("is_running", &SocketHub::isRunning, + "Checks if the socket service is running."); + +#if __has_include() + py::class_(m, "SSHClient") + .def(py::init(), py::arg("host"), + py::arg("port") = DEFAULT_SSH_PORT) + .def("connect", &SSHClient::connect, py::arg("username"), + py::arg("password"), py::arg("timeout") = DEFAULT_TIMEOUT, + "Connects to the SSH server with the specified username and " + "password.") + .def("is_connected", &SSHClient::isConnected, + "Checks if the SSH client is connected to the server.") + .def("disconnect", &SSHClient::disconnect, + "Disconnects from the SSH server.") + .def("execute_command", &SSHClient::executeCommand, py::arg("command"), + py::arg("output"), "Executes a single command on the SSH server.") + .def("execute_commands", &SSHClient::executeCommands, + py::arg("commands"), py::arg("output"), + "Executes multiple commands on the SSH server.") + .def("file_exists", &SSHClient::fileExists, py::arg("remote_path"), + "Checks if a file exists on the remote server.") + .def("create_directory", &SSHClient::createDirectory, + py::arg("remote_path"), py::arg("mode") = DEFAULT_MODE, + "Creates a directory on the remote server.") + .def("remove_file", &SSHClient::removeFile, py::arg("remote_path"), + "Removes a file from the remote server.") + .def("remove_directory", &SSHClient::removeDirectory, + py::arg("remote_path"), + "Removes a directory from the remote server.") + .def("list_directory", &SSHClient::listDirectory, + py::arg("remote_path"), + "Lists the contents of a directory on the remote server.") + .def("rename", &SSHClient::rename, py::arg("old_path"), + py::arg("new_path"), + "Renames a file or directory on the remote server.") + .def("get_file_info", &SSHClient::getFileInfo, py::arg("remote_path"), + py::arg("attrs"), "Retrieves file information for a remote file.") + .def("download_file", &SSHClient::downloadFile, py::arg("remote_path"), + py::arg("local_path"), "Downloads a file from the remote server.") + .def("upload_file", &SSHClient::uploadFile, py::arg("local_path"), + py::arg("remote_path"), "Uploads a file to the remote server.") + .def("upload_directory", &SSHClient::uploadDirectory, + py::arg("local_path"), py::arg("remote_path"), + "Uploads a directory to the remote server."); +#endif + + py::class_(m, "SshServer") + .def(py::init(), py::arg("config_file")) + .def("start", &SshServer::start, "Starts the SSH server.") + .def("stop", &SshServer::stop, "Stops the SSH server.") + .def("is_running", &SshServer::isRunning, + "Checks if the SSH server is running.") + .def("set_port", &SshServer::setPort, py::arg("port"), + "Sets the port on which the SSH server listens for connections.") + .def("get_port", &SshServer::getPort, + "Gets the port on which the SSH server is listening.") + .def( + "set_listen_address", &SshServer::setListenAddress, + py::arg("address"), + "Sets the address on which the SSH server listens for connections.") + .def("get_listen_address", &SshServer::getListenAddress, + "Gets the address on which the SSH server is listening.") + .def("set_host_key", &SshServer::setHostKey, py::arg("key_file"), + "Sets the host key file used for SSH connections.") + .def("get_host_key", &SshServer::getHostKey, + "Gets the path to the host key file.") + .def("set_authorized_keys", &SshServer::setAuthorizedKeys, + py::arg("key_files"), + "Sets the list of authorized public key files for user " + "authentication.") + .def("get_authorized_keys", &SshServer::getAuthorizedKeys, + "Gets the list of authorized public key files.") + .def("allow_root_login", &SshServer::allowRootLogin, py::arg("allow"), + "Enables or disables root login to the SSH server.") + .def("is_root_login_allowed", &SshServer::isRootLoginAllowed, + "Checks if root login is allowed.") + .def("set_password_authentication", + &SshServer::setPasswordAuthentication, py::arg("enable"), + "Enables or disables password authentication for the SSH server.") + .def("is_password_authentication_enabled", + &SshServer::isPasswordAuthenticationEnabled, + "Checks if password authentication is enabled.") + .def("set_subsystem", &SshServer::setSubsystem, py::arg("name"), + py::arg("command"), + "Sets a subsystem for handling a specific command.") + .def("remove_subsystem", &SshServer::removeSubsystem, py::arg("name"), + "Removes a previously set subsystem by name.") + .def("get_subsystem", &SshServer::getSubsystem, py::arg("name"), + "Gets the command associated with a subsystem by name."); + + py::class_(m, "TcpClient") + .def(py::init<>()) + .def("connect", &TcpClient::connect, py::arg("host"), py::arg("port"), + py::arg("timeout") = std::chrono::milliseconds::zero(), + "Connects to a TCP server.") + .def("disconnect", &TcpClient::disconnect, + "Disconnects from the server.") + .def("send", &TcpClient::send, py::arg("data"), + "Sends data to the server.") + .def("receive", &TcpClient::receive, py::arg("size"), + py::arg("timeout") = std::chrono::milliseconds::zero(), + "Receives data from the server.") + .def("is_connected", &TcpClient::isConnected, + "Checks if the client is connected to the server.") + .def("get_error_message", &TcpClient::getErrorMessage, + "Gets the error message in case of any error.") + .def("set_on_connected_callback", &TcpClient::setOnConnectedCallback, + py::arg("callback"), + "Sets the callback function to be called when connected to the " + "server.") + .def("set_on_disconnected_callback", + &TcpClient::setOnDisconnectedCallback, py::arg("callback"), + "Sets the callback function to be called when disconnected from " + "the server.") + .def("set_on_data_received_callback", + &TcpClient::setOnDataReceivedCallback, py::arg("callback"), + "Sets the callback function to be called when data is received " + "from the server.") + .def("set_on_error_callback", &TcpClient::setOnErrorCallback, + py::arg("callback"), + "Sets the callback function to be called when an error occurs.") + .def("start_receiving", &TcpClient::startReceiving, + py::arg("buffer_size"), "Starts receiving data from the server.") + .def("stop_receiving", &TcpClient::stopReceiving, + "Stops receiving data from the server."); + + py::class_(m, "TTYBase") + .def(py::init(), py::arg("driver_name")) + .def("read", &TTYBase::read, py::arg("buffer"), py::arg("nbytes"), + py::arg("timeout"), py::arg("nbytes_read"), + "Reads data from the TTY device.") + .def("read_section", &TTYBase::readSection, py::arg("buffer"), + py::arg("nsize"), py::arg("stop_byte"), py::arg("timeout"), + py::arg("nbytes_read"), + "Reads a section of data from the TTY until a stop byte is " + "encountered.") + .def("write", &TTYBase::write, py::arg("buffer"), py::arg("nbytes"), + py::arg("nbytes_written"), "Writes data to the TTY device.") + .def("write_string", &TTYBase::writeString, py::arg("string"), + py::arg("nbytes_written"), "Writes a string to the TTY device.") + .def("connect", &TTYBase::connect, py::arg("device"), + py::arg("bit_rate"), py::arg("word_size"), py::arg("parity"), + py::arg("stop_bits"), "Connects to the specified TTY device.") + .def("disconnect", &TTYBase::disconnect, + "Disconnects from the TTY device.") + .def("set_debug", &TTYBase::setDebug, py::arg("enabled"), + "Enables or disables debugging information.") + .def("get_error_message", &TTYBase::getErrorMessage, py::arg("code"), + "Retrieves an error message corresponding to a given TTYResponse " + "code.") + .def("get_port_fd", &TTYBase::getPortFD, + "Gets the file descriptor for the TTY port."); + + py::enum_(m, "TTYResponse") + .value("OK", TTYBase::TTYResponse::OK) + .value("ReadError", TTYBase::TTYResponse::ReadError) + .value("WriteError", TTYBase::TTYResponse::WriteError) + .value("SelectError", TTYBase::TTYResponse::SelectError) + .value("Timeout", TTYBase::TTYResponse::Timeout) + .value("PortFailure", TTYBase::TTYResponse::PortFailure) + .value("ParamError", TTYBase::TTYResponse::ParamError) + .value("Errno", TTYBase::TTYResponse::Errno) + .value("Overflow", TTYBase::TTYResponse::Overflow); + + py::class_(m, "UdpClient") + .def(py::init<>()) + .def("bind", &UdpClient::bind, py::arg("port"), + "Binds the client to a specific port for receiving data.") + .def("send", &UdpClient::send, py::arg("host"), py::arg("port"), + py::arg("data"), "Sends data to a specified host and port.") + .def("receive", &UdpClient::receive, py::arg("size"), + py::arg("remote_host"), py::arg("remote_port"), + py::arg("timeout") = std::chrono::milliseconds::zero(), + "Receives data from a remote host.") + .def("set_on_data_received_callback", + &UdpClient::setOnDataReceivedCallback, py::arg("callback"), + "Sets the callback function to be called when data is received.") + .def("set_on_error_callback", &UdpClient::setOnErrorCallback, + py::arg("callback"), + "Sets the callback function to be called when an error occurs.") + .def("start_receiving", &UdpClient::startReceiving, + py::arg("buffer_size"), "Starts receiving data asynchronously.") + .def("stop_receiving", &UdpClient::stopReceiving, + "Stops receiving data."); + + py::class_(m, "UdpSocketHub") + .def(py::init<>()) + .def("start", &UdpSocketHub::start, py::arg("port"), + "Starts the UDP socket hub and binds it to the specified port.") + .def("stop", &UdpSocketHub::stop, "Stops the UDP socket hub.") + .def("is_running", &UdpSocketHub::isRunning, + "Checks if the UDP socket hub is currently running.") + .def("add_message_handler", &UdpSocketHub::addMessageHandler, + py::arg("handler"), + "Adds a message handler function to the UDP socket hub.") + .def("remove_message_handler", &UdpSocketHub::removeMessageHandler, + py::arg("handler"), + "Removes a message handler function from the UDP socket hub.") + .def("send_to", &UdpSocketHub::sendTo, py::arg("message"), + py::arg("ip"), py::arg("port"), + "Sends a message to the specified IP address and port."); +} \ No newline at end of file diff --git a/modules/atom.extra/pymodule.cpp b/modules/atom.extra/pymodule.cpp new file mode 100644 index 00000000..f7aba4e9 --- /dev/null +++ b/modules/atom.extra/pymodule.cpp @@ -0,0 +1,682 @@ +#include +#include +#include + +#include "atom/extra/beast/http.hpp" +#include "atom/extra/beast/ws.hpp" + +#if __has_include() +#include "atom/extra/boost/charconv.hpp" +#endif +#include "atom/extra/boost/locale.hpp" +#include "atom/extra/boost/math.hpp" +#include "atom/extra/boost/regex.hpp" +#include "atom/extra/boost/system.hpp" +#include "atom/extra/boost/uuid.hpp" + +#include "atom/extra/inicpp/inicpp.hpp" + +namespace py = pybind11; + +PYBIND11_MODULE(math, m) { + m.doc() = "Python bindings for Atom Extra Module"; + + py::class_(m, "HttpClient") + .def(py::init(), py::arg("ioc"), + "Constructs an HttpClient with the given I/O context") + .def("set_default_header", &HttpClient::setDefaultHeader, + py::arg("key"), py::arg("value"), + "Sets a default header for all requests") + .def("set_timeout", &HttpClient::setTimeout, py::arg("timeout"), + "Sets the timeout duration for the HTTP operations") + .def( + "request", &HttpClient::request, + py::arg("method"), py::arg("host"), py::arg("port"), + py::arg("target"), py::arg("version") = 11, + py::arg("content_type") = "", py::arg("body") = "", + py::arg("headers") = std::unordered_map(), + "Sends a synchronous HTTP request") + .def( + "async_request", + &HttpClient::asyncRequest< + http::string_body, + std::function)>>, + py::arg("method"), py::arg("host"), py::arg("port"), + py::arg("target"), py::arg("handler"), py::arg("version") = 11, + py::arg("content_type") = "", py::arg("body") = "", + py::arg("headers") = std::unordered_map(), + "Sends an asynchronous HTTP request") + .def( + "json_request", &HttpClient::jsonRequest, py::arg("method"), + py::arg("host"), py::arg("port"), py::arg("target"), + py::arg("json_body") = json(), + py::arg("headers") = std::unordered_map(), + "Sends a synchronous HTTP request with a JSON body and returns a " + "JSON response") + .def( + "async_json_request", + &HttpClient::asyncJsonRequest< + std::function>, + py::arg("method"), py::arg("host"), py::arg("port"), + py::arg("target"), py::arg("handler"), + py::arg("json_body") = json(), + py::arg("headers") = std::unordered_map(), + "Sends an asynchronous HTTP request with a JSON body and returns a " + "JSON response") + .def("upload_file", &HttpClient::uploadFile, py::arg("host"), + py::arg("port"), py::arg("target"), py::arg("filepath"), + py::arg("field_name") = "file", "Uploads a file to the server") + .def("download_file", &HttpClient::downloadFile, py::arg("host"), + py::arg("port"), py::arg("target"), py::arg("filepath"), + "Downloads a file from the server") + .def( + "request_with_retry", + &HttpClient::requestWithRetry, py::arg("method"), + py::arg("host"), py::arg("port"), py::arg("target"), + py::arg("retry_count") = 3, py::arg("version") = 11, + py::arg("content_type") = "", py::arg("body") = "", + py::arg("headers") = std::unordered_map(), + "Sends a synchronous HTTP request with retry logic") + .def( + "batch_request", &HttpClient::batchRequest, + py::arg("requests"), + py::arg("headers") = std::unordered_map(), + "Sends multiple synchronous HTTP requests in a batch") + .def( + "async_batch_request", + &HttpClient::asyncBatchRequest>)>>, + py::arg("requests"), py::arg("handler"), + py::arg("headers") = std::unordered_map(), + "Sends multiple asynchronous HTTP requests in a batch") + .def("run_with_thread_pool", &HttpClient::runWithThreadPool, + py::arg("num_threads"), "Runs the I/O context with a thread pool") + .def("async_download_file", + &HttpClient::asyncDownloadFile< + std::function>, + py::arg("host"), py::arg("port"), py::arg("target"), + py::arg("filepath"), py::arg("handler"), + "Asynchronously downloads a file from the server"); + + py::class_(m, "WSClient") + .def(py::init(), py::arg("ioc"), + "Constructs a WSClient with the given I/O context") + .def("set_timeout", &WSClient::setTimeout, py::arg("timeout"), + "Sets the timeout duration for the WebSocket operations") + .def("set_reconnect_options", &WSClient::setReconnectOptions, + py::arg("retries"), py::arg("interval"), + "Sets the reconnection options") + .def("set_ping_interval", &WSClient::setPingInterval, + py::arg("interval"), "Sets the interval for sending ping messages") + .def("connect", &WSClient::connect, py::arg("host"), py::arg("port"), + "Connects to the WebSocket server") + .def("send", &WSClient::send, py::arg("message"), + "Sends a message to the WebSocket server") + .def("receive", &WSClient::receive, + "Receives a message from the WebSocket server") + .def("close", &WSClient::close, "Closes the WebSocket connection") + .def("async_connect", + &WSClient::asyncConnect>, + py::arg("host"), py::arg("port"), py::arg("handler"), + "Asynchronously connects to the WebSocket server") + .def("async_send", + &WSClient::asyncSend< + std::function>, + py::arg("message"), py::arg("handler"), + "Asynchronously sends a message to the WebSocket server") + .def("async_receive", + &WSClient::asyncReceive< + std::function>, + py::arg("handler"), + "Asynchronously receives a message from the WebSocket server") + .def("async_close", + &WSClient::asyncClose>, + py::arg("handler"), + "Asynchronously closes the WebSocket connection") + .def("async_send_json", &WSClient::asyncSendJson, py::arg("jdata"), + py::arg("handler"), + "Asynchronously sends a JSON object to the WebSocket server") + .def("async_receive_json", + &WSClient::asyncReceiveJson< + std::function>, + py::arg("handler"), + "Asynchronously receives a JSON object from the WebSocket server"); + +#if __has_include() + py::enum_(m, "NumberFormat") + .value("GENERAL", atom::extra::boost::NumberFormat::GENERAL) + .value("SCIENTIFIC", atom::extra::boost::NumberFormat::SCIENTIFIC) + .value("FIXED", atom::extra::boost::NumberFormat::FIXED) + .value("HEX", atom::extra::boost::NumberFormat::HEX); + + py::class_(m, "FormatOptions") + .def(py::init<>()) + .def_readwrite("format", &atom::extra::boost::FormatOptions::format) + .def_readwrite("precision", + &atom::extra::boost::FormatOptions::precision) + .def_readwrite("uppercase", + &atom::extra::boost::FormatOptions::uppercase) + .def_readwrite("thousands_separator", + &atom::extra::boost::FormatOptions::thousandsSeparator); + + py::class_(m, "BoostCharConv") + .def_static("int_to_string", + &atom::extra::boost::BoostCharConv::intToString, + "Convert an integer to a string", py::arg("value"), + py::arg("base") = atom::extra::boost::DEFAULT_BASE, + py::arg("options") = atom::extra::boost::FormatOptions()) + .def_static("float_to_string", + &atom::extra::boost::BoostCharConv::floatToString, + "Convert a floating-point number to a string", + py::arg("value"), + py::arg("options") = atom::extra::boost::FormatOptions()) + .def_static("string_to_int", + &atom::extra::boost::BoostCharConv::stringToInt, + "Convert a string to an integer", py::arg("str"), + py::arg("base") = atom::extra::boost::DEFAULT_BASE) + .def_static("string_to_float", + &atom::extra::boost::BoostCharConv::stringToFloat, + "Convert a string to a floating-point number", + py::arg("str")) + .def_static("to_string", + &atom::extra::boost::BoostCharConv::toString, + "Convert a value to a string", py::arg("value"), + py::arg("options") = atom::extra::boost::FormatOptions()) + .def_static("from_string", + &atom::extra::boost::BoostCharConv::fromString, + "Convert a string to a value", py::arg("str"), + py::arg("base") = atom::extra::boost::DEFAULT_BASE) + .def_static( + "special_value_to_string", + &atom::extra::boost::BoostCharConv::specialValueToString, + "Convert special floating-point values (NaN, Inf) to strings", + py::arg("value")); +#endif + + py::class_(m, "LocaleWrapper") + .def(py::init(), py::arg("locale_name") = "", + "Constructs a LocaleWrapper object with the specified locale") + .def_static("to_utf8", &atom::extra::boost::LocaleWrapper::toUtf8, + py::arg("str"), py::arg("from_charset"), + "Converts a string to UTF-8 encoding") + .def_static("from_utf8", &atom::extra::boost::LocaleWrapper::fromUtf8, + py::arg("str"), py::arg("to_charset"), + "Converts a UTF-8 encoded string to another character set") + .def_static("normalize", &atom::extra::boost::LocaleWrapper::normalize, + py::arg("str"), + py::arg("norm") = ::boost::locale::norm_default, + "Normalizes a Unicode string") + .def_static("tokenize", &atom::extra::boost::LocaleWrapper::tokenize, + py::arg("str"), py::arg("locale_name") = "", + "Tokenizes a string into words") + .def_static("translate", &atom::extra::boost::LocaleWrapper::translate, + py::arg("str"), py::arg("domain"), + py::arg("locale_name") = "", + "Translates a string to the specified locale") + .def("to_upper", &atom::extra::boost::LocaleWrapper::toUpper, + py::arg("str"), "Converts a string to uppercase") + .def("to_lower", &atom::extra::boost::LocaleWrapper::toLower, + py::arg("str"), "Converts a string to lowercase") + .def("to_title", &atom::extra::boost::LocaleWrapper::toTitle, + py::arg("str"), "Converts a string to title case") + .def("compare", &atom::extra::boost::LocaleWrapper::compare, + py::arg("str1"), py::arg("str2"), + "Compares two strings using locale-specific collation rules") + .def_static("format_date", + &atom::extra::boost::LocaleWrapper::formatDate, + py::arg("date_time"), py::arg("format"), + "Formats a date and time according to the specified format") + .def_static("format_number", + &atom::extra::boost::LocaleWrapper::formatNumber, + py::arg("number"), py::arg("precision") = 2, + "Formats a number with the specified precision") + .def_static("format_currency", + &atom::extra::boost::LocaleWrapper::formatCurrency, + py::arg("amount"), py::arg("currency"), + "Formats a currency amount") + .def_static("regex_replace", + &atom::extra::boost::LocaleWrapper::regexReplace, + py::arg("str"), py::arg("regex"), py::arg("format"), + "Replaces occurrences of a regex pattern in a string with " + "a format string") + .def("format", &atom::extra::boost::LocaleWrapper::format, + py::arg("format_string"), py::kwargs(), + "Formats a string with named arguments"); + + py::class_>(m, + "SpecialFunctions") + .def_static("beta", &atom::extra::boost::SpecialFunctions::beta, + "Compute the beta function") + .def_static("gamma", + &atom::extra::boost::SpecialFunctions::gamma, + "Compute the gamma function") + .def_static("digamma", + &atom::extra::boost::SpecialFunctions::digamma, + "Compute the digamma function") + .def_static("erf", &atom::extra::boost::SpecialFunctions::erf, + "Compute the error function") + .def_static("bessel_j", + &atom::extra::boost::SpecialFunctions::besselJ, + "Compute the Bessel function of the first kind") + .def_static("legendre_p", + &atom::extra::boost::SpecialFunctions::legendreP, + "Compute the Legendre polynomial"); + + py::class_>(m, "Statistics") + .def_static("mean", &atom::extra::boost::Statistics::mean, + "Compute the mean of a dataset") + .def_static("variance", + &atom::extra::boost::Statistics::variance, + "Compute the variance of a dataset") + .def_static("skewness", + &atom::extra::boost::Statistics::skewness, + "Compute the skewness of a dataset") + .def_static("kurtosis", + &atom::extra::boost::Statistics::kurtosis, + "Compute the kurtosis of a dataset"); + + py::class_::NormalDistribution>( + m, "NormalDistribution") + .def(py::init(), py::arg("mean"), py::arg("stddev")) + .def( + "pdf", + &atom::extra::boost::Distributions::NormalDistribution::pdf, + "Compute the probability density function (PDF)") + .def( + "cdf", + &atom::extra::boost::Distributions::NormalDistribution::cdf, + "Compute the cumulative distribution function (CDF)") + .def("quantile", + &atom::extra::boost::Distributions< + double>::NormalDistribution::quantile, + "Compute the quantile (inverse CDF)"); + + py::class_::StudentTDistribution>( + m, "StudentTDistribution") + .def(py::init(), py::arg("degrees_of_freedom")) + .def("pdf", + &atom::extra::boost::Distributions< + double>::StudentTDistribution::pdf, + "Compute the probability density function (PDF)") + .def("cdf", + &atom::extra::boost::Distributions< + double>::StudentTDistribution::cdf, + "Compute the cumulative distribution function (CDF)") + .def("quantile", + &atom::extra::boost::Distributions< + double>::StudentTDistribution::quantile, + "Compute the quantile (inverse CDF)"); + + py::class_::PoissonDistribution>( + m, "PoissonDistribution") + .def(py::init(), py::arg("mean")) + .def("pdf", + &atom::extra::boost::Distributions< + double>::PoissonDistribution::pdf, + "Compute the probability density function (PDF)") + .def("cdf", + &atom::extra::boost::Distributions< + double>::PoissonDistribution::cdf, + "Compute the cumulative distribution function (CDF)"); + + py::class_< + atom::extra::boost::Distributions::ExponentialDistribution>( + m, "ExponentialDistribution") + .def(py::init(), py::arg("lambda")) + .def("pdf", + &atom::extra::boost::Distributions< + double>::ExponentialDistribution::pdf, + "Compute the probability density function (PDF)") + .def("cdf", + &atom::extra::boost::Distributions< + double>::ExponentialDistribution::cdf, + "Compute the cumulative distribution function (CDF)"); + + py::class_>( + m, "NumericalIntegration") + .def_static( + "trapezoidal", + &atom::extra::boost::NumericalIntegration::trapezoidal, + "Compute the integral of a function using the trapezoidal rule"); + + m.def("factorial", &atom::extra::boost::factorial, + "Compute the factorial of a number"); + + py::class_>(m, "Optimization") + .def_static( + "golden_section_search", + &atom::extra::boost::Optimization::goldenSectionSearch, + "Perform one-dimensional golden section search to find the minimum " + "of a function") + .def_static( + "newton_raphson", + &atom::extra::boost::Optimization::newtonRaphson, + "Perform Newton-Raphson method to find the root of a function"); + + py::class_>(m, "LinearAlgebra") + .def_static( + "solve_linear_system", + &atom::extra::boost::LinearAlgebra::solveLinearSystem, + "Solve a linear system of equations Ax = b") + .def_static("determinant", + &atom::extra::boost::LinearAlgebra::determinant, + "Compute the determinant of a matrix") + .def_static("multiply", + &atom::extra::boost::LinearAlgebra::multiply, + "Multiply two matrices") + .def_static("transpose", + &atom::extra::boost::LinearAlgebra::transpose, + "Compute the transpose of a matrix"); + + py::class_>(m, "ODESolver") + .def_static("runge_kutta4", + &atom::extra::boost::ODESolver::rungeKutta4, + "Solve an ODE using the 4th order Runge-Kutta method"); + + py::class_>(m, "FinancialMath") + .def_static( + "black_scholes_call", + &atom::extra::boost::FinancialMath::blackScholesCall, + "Compute the price of a European call option using the " + "Black-Scholes formula") + .def_static( + "modified_duration", + &atom::extra::boost::FinancialMath::modifiedDuration, + "Compute the modified duration of a bond") + .def_static("bond_price", + &atom::extra::boost::FinancialMath::bondPrice, + "Compute the price of a bond") + .def_static( + "implied_volatility", + &atom::extra::boost::FinancialMath::impliedVolatility, + "Compute the implied volatility of an option"); + + py::class_(m, "RegexWrapper") + .def(py::init(), + py::arg("pattern"), + py::arg("flags") = ::boost::regex_constants::normal) + .def("match", &atom::extra::boost::RegexWrapper::match, + "Match the given string against the regex pattern", py::arg("str")) + .def("search", &atom::extra::boost::RegexWrapper::search, + "Search the given string for the first match of the regex pattern", + py::arg("str")) + .def("search_all", + &atom::extra::boost::RegexWrapper::searchAll, + "Search the given string for all matches of the regex pattern", + py::arg("str")) + .def("replace", + &atom::extra::boost::RegexWrapper::replace, + "Replace all matches of the regex pattern in the given string " + "with the replacement string", + py::arg("str"), py::arg("replacement")) + .def("split", &atom::extra::boost::RegexWrapper::split, + "Split the given string by the regex pattern", py::arg("str")) + .def("match_groups", + &atom::extra::boost::RegexWrapper::matchGroups, + "Match the given string and return the groups of each match", + py::arg("str")) + .def("for_each_match", + &atom::extra::boost::RegexWrapper::forEachMatch< + std::string, std::function>, + "Apply a function to each match of the regex pattern in the given " + "string", + py::arg("str"), py::arg("func")) + .def("get_pattern", &atom::extra::boost::RegexWrapper::getPattern, + "Get the regex pattern as a string") + .def("set_pattern", &atom::extra::boost::RegexWrapper::setPattern, + "Set a new regex pattern with optional flags", py::arg("pattern"), + py::arg("flags") = ::boost::regex_constants::normal) + .def("named_captures", + &atom::extra::boost::RegexWrapper::namedCaptures, + "Match the given string and return the named captures", + py::arg("str")) + .def("is_valid", + &atom::extra::boost::RegexWrapper::isValid, + "Check if the given string is a valid match for the regex pattern", + py::arg("str")) + .def("replace_callback", + &atom::extra::boost::RegexWrapper::replaceCallback, + "Replace all matches of the regex pattern in the given string " + "using a callback function", + py::arg("str"), py::arg("callback")) + .def_static("escape_string", + &atom::extra::boost::RegexWrapper::escapeString, + "Escape special characters in the given string for use in " + "a regex pattern", + py::arg("str")) + .def("benchmark_match", + &atom::extra::boost::RegexWrapper::benchmarkMatch, + "Benchmark the match operation for the given string over a number " + "of iterations", + py::arg("str"), py::arg("iterations") = 1000) + .def_static( + "is_valid_regex", &atom::extra::boost::RegexWrapper::isValidRegex, + "Check if the given regex pattern is valid", py::arg("pattern")); + + py::class_(m, "Error") + .def(py::init<>(), "Default constructor") + .def(py::init(), + py::arg("error_code"), + "Constructs an Error from a Boost.System error code") + .def(py::init(), + py::arg("error_value"), py::arg("error_category"), + "Constructs an Error from an error value and category") + .def("value", &atom::extra::boost::Error::value, "Gets the error value") + .def("category", &atom::extra::boost::Error::category, + "Gets the error category") + .def("message", &atom::extra::boost::Error::message, + "Gets the error message") + .def("__bool__", &atom::extra::boost::Error::operator bool, + "Checks if the error code is valid") + .def("to_boost_error_code", + &atom::extra::boost::Error::toBoostErrorCode, + "Converts to a Boost.System error code") + .def("__eq__", &atom::extra::boost::Error::operator==, + "Equality operator") + .def("__ne__", &atom::extra::boost::Error::operator!=, + "Inequality operator"); + + py::class_(m, "Exception") + .def(py::init(), py::arg("error"), + "Constructs an Exception from an Error") + .def("error", &atom::extra::boost::Exception::error, + "Gets the associated Error"); + + /* + py::class_>(m, "ResultVoid") + .def(py::init<>(), "Default constructor") + .def(py::init(), py::arg("error"), + "Constructs a Result with an Error") .def("has_value", + &atom::extra::boost::Result::hasValue, "Checks if the Result has a + value") .def("error", + py::overload_cast<>(&atom::extra::boost::Result::error, + py::const_), "Gets the associated Error") .def("__bool__", + &atom::extra::boost::Result::operator bool, "Checks if the Result + has a value"); + + py::class_>(m, "ResultString") + .def(py::init(), py::arg("value"), "Constructs a Result + with a value") .def(py::init(), + py::arg("error"), "Constructs a Result with an Error") .def("has_value", + &atom::extra::boost::Result::hasValue, "Checks if the Result + has a value") .def("value", + py::overload_cast<>(&atom::extra::boost::Result::value, + py::const_), "Gets the result value") .def("error", + py::overload_cast<>(&atom::extra::boost::Result::error, + py::const_), "Gets the associated Error") .def("__bool__", + &atom::extra::boost::Result::operator bool, "Checks if the + Result has a value"); + + m.def("make_result", [](const std::function& func) { + return atom::extra::boost::makeResult(func); + }, "Creates a Result from a function"); + */ + + py::class_(m, "UUID") + .def(py::init<>(), + "Default constructor that generates a random UUID (v4)") + .def(py::init(), py::arg("str"), + "Constructs a UUID from a string representation") + .def(py::init(), py::arg("uuid"), + "Constructs a UUID from a Boost.UUID object") + .def("to_string", &atom::extra::boost::UUID::toString, + "Converts the UUID to a string representation") + .def("is_nil", &atom::extra::boost::UUID::isNil, + "Checks if the UUID is nil (all zeros)") + .def("__eq__", &atom::extra::boost::UUID::operator==, + "Checks if this UUID is equal to another UUID") + .def( + "__lt__", + [](const atom::extra::boost::UUID &self, + const atom::extra::boost::UUID &other) { return self < other; }, + "Less than comparison for UUIDs") + .def( + "__le__", + [](const atom::extra::boost::UUID &self, + const atom::extra::boost::UUID &other) { return self <= other; }, + "Less than or equal comparison for UUIDs") + .def( + "__gt__", + [](const atom::extra::boost::UUID &self, + const atom::extra::boost::UUID &other) { return self > other; }, + "Greater than comparison for UUIDs") + .def( + "__ge__", + [](const atom::extra::boost::UUID &self, + const atom::extra::boost::UUID &other) { return self >= other; }, + "Greater than or equal comparison for UUIDs") + .def("format", &atom::extra::boost::UUID::format, + "Formats the UUID as a string enclosed in curly braces") + .def("to_bytes", &atom::extra::boost::UUID::toBytes, + "Converts the UUID to a vector of bytes") + .def_static("from_bytes", &atom::extra::boost::UUID::fromBytes, + py::arg("bytes"), "Constructs a UUID from a span of bytes") + .def("to_uint64", &atom::extra::boost::UUID::toUint64, + "Converts the UUID to a 64-bit unsigned integer") + .def_static("namespace_dns", &atom::extra::boost::UUID::namespaceDNS, + "Gets the DNS namespace UUID") + .def_static("namespace_url", &atom::extra::boost::UUID::namespaceURL, + "Gets the URL namespace UUID") + .def_static("namespace_oid", &atom::extra::boost::UUID::namespaceOID, + "Gets the OID namespace UUID") + .def_static("v3", &atom::extra::boost::UUID::v3, + py::arg("namespace_uuid"), py::arg("name"), + "Generates a version 3 (MD5) UUID based on a namespace " + "UUID and a name") + .def_static("v5", &atom::extra::boost::UUID::v5, + py::arg("namespace_uuid"), py::arg("name"), + "Generates a version 5 (SHA-1) UUID based on a namespace " + "UUID and a name") + .def("version", &atom::extra::boost::UUID::version, + "Gets the version of the UUID") + .def("variant", &atom::extra::boost::UUID::variant, + "Gets the variant of the UUID") + .def_static("v1", &atom::extra::boost::UUID::v1, + "Generates a version 1 (timestamp-based) UUID") + .def_static("v4", &atom::extra::boost::UUID::v4, + "Generates a version 4 (random) UUID") + .def("to_base64", &atom::extra::boost::UUID::toBase64, + "Converts the UUID to a Base64 string representation") + .def("get_timestamp", &atom::extra::boost::UUID::getTimestamp, + "Gets the timestamp from a version 1 UUID") + .def( + "__hash__", + [](const atom::extra::boost::UUID &self) { + return std::hash()(self); + }, + "Hash function for UUIDs"); + + py::class_>>(m, "IniFile") + .def(py::init<>(), "Default constructor") + .def(py::init(), py::arg("filename"), + "Constructs an IniFileBase from a file") + .def(py::init(), py::arg("iss"), + "Constructs an IniFileBase from an input stream") + .def("set_field_sep", &inicpp::IniFileBase>::setFieldSep, + py::arg("sep"), "Sets the field separator character") + .def("set_comment_prefixes", + &inicpp::IniFileBase>::setCommentPrefixes, + py::arg("comment_prefixes"), "Sets the comment prefixes") + .def("set_escape_char", + &inicpp::IniFileBase>::setEscapeChar, py::arg("esc"), + "Sets the escape character") + .def("set_multi_line_values", + &inicpp::IniFileBase>::setMultiLineValues, + py::arg("enable"), "Enables or disables multi-line values") + .def("allow_overwrite_duplicate_fields", + &inicpp::IniFileBase>::allowOverwriteDuplicateFields, + py::arg("allowed"), + "Allows or disallows overwriting duplicate fields") + .def("decode", + py::overload_cast( + &inicpp::IniFileBase>::decode), + py::arg("iss"), "Decodes an INI file from an input stream") + .def("decode", + py::overload_cast( + &inicpp::IniFileBase>::decode), + py::arg("content"), "Decodes an INI file from a string") + .def("load", &inicpp::IniFileBase>::load, + py::arg("file_name"), + "Loads and decodes an INI file from a file path") + // .def("encode", py::overload_cast(&inicpp::IniFileBase>::encode, py::const_), + // py::arg("oss"), "Encodes the INI file to an output stream") + .def("encode", + py::overload_cast<>(&inicpp::IniFileBase>::encode, + py::const_), + "Encodes the INI file to a string and returns it") + .def("save", &inicpp::IniFileBase>::save, + py::arg("file_name"), "Saves the INI file to a given file path"); + + py::class_>( + m, "IniFileCaseInsensitive") + .def(py::init<>(), "Default constructor") + .def(py::init(), py::arg("filename"), + "Constructs an IniFileBase from a file") + .def(py::init(), py::arg("iss"), + "Constructs an IniFileBase from an input stream") + .def("set_field_sep", + &inicpp::IniFileBase::setFieldSep, + py::arg("sep"), "Sets the field separator character") + .def("set_comment_prefixes", + &inicpp::IniFileBase< + inicpp::StringInsensitiveLess>::setCommentPrefixes, + py::arg("comment_prefixes"), "Sets the comment prefixes") + .def("set_escape_char", + &inicpp::IniFileBase::setEscapeChar, + py::arg("esc"), "Sets the escape character") + .def("set_multi_line_values", + &inicpp::IniFileBase< + inicpp::StringInsensitiveLess>::setMultiLineValues, + py::arg("enable"), "Enables or disables multi-line values") + .def("allow_overwrite_duplicate_fields", + &inicpp::IniFileBase< + inicpp::StringInsensitiveLess>::allowOverwriteDuplicateFields, + py::arg("allowed"), + "Allows or disallows overwriting duplicate fields") + .def("decode", + py::overload_cast( + &inicpp::IniFileBase::decode), + py::arg("iss"), "Decodes an INI file from an input stream") + .def("decode", + py::overload_cast( + &inicpp::IniFileBase::decode), + py::arg("content"), "Decodes an INI file from a string") + .def("load", &inicpp::IniFileBase::load, + py::arg("file_name"), + "Loads and decodes an INI file from a file path") + // .def("encode", py::overload_cast(&inicpp::IniFileBase::encode, + // py::const_), py::arg("oss"), "Encodes the INI file to an output + // stream") + .def("encode", + py::overload_cast<>( + &inicpp::IniFileBase::encode, + py::const_), + "Encodes the INI file to a string and returns it") + .def("save", &inicpp::IniFileBase::save, + py::arg("file_name"), "Saves the INI file to a given file path"); +} \ No newline at end of file diff --git a/modules/atom.io/CMakeLists.txt b/modules/atom.io/CMakeLists.txt index 3ef8ce76..721e0c83 100644 --- a/modules/atom.io/CMakeLists.txt +++ b/modules/atom.io/CMakeLists.txt @@ -56,3 +56,8 @@ set_target_properties(${PROJECT_NAME} PROPERTIES install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) + +find_package(Python COMPONENTS Interpreter Development) +find_package(pybind11 CONFIG) + +pybind11_add_module(${PROJECT_NAME}_py pymodule.cpp) diff --git a/modules/atom.io/component.cpp b/modules/atom.io/component.cpp index d87685b2..7781d9b5 100644 --- a/modules/atom.io/component.cpp +++ b/modules/atom.io/component.cpp @@ -14,53 +14,71 @@ using namespace atom::io; ATOM_MODULE(atom_io, [](Component &component) { DLOG_F(INFO, "Loading module {}", component.getName()); - component.def("compress", &compressFile, "Compress a file"); - component.def("decompress", &decompressFile, "Decompress a file"); - component.def("create_zip", &createZip, "Create a zip file"); - component.def("extract_zip", &extractZip, "Extract a zip file"); - component.def("compress_folder", &compressFolder, "Compress a folder"); + component.def("compress", &compressFile, "compression", "Compress a file"); + component.def("decompress", &decompressFile, "compression", + "Decompress a file"); + component.def("create_zip", &createZip, "compression", "Create a zip file"); + component.def("extract_zip", &extractZip, "compression", + "Extract a zip file"); + component.def("compress_folder", &compressFolder, "compression", + "Compress a folder"); - component.def("translate", &translate, "Translate a pattern"); - component.def("compile_pattern", &compilePattern, "Compile a pattern"); - component.def("fnmatch", &fnmatch, "Check if a name matches a pattern"); - component.def("filter", &filter, "Filter a list of names"); - component.def("expand_tilde", &expandTilde, "Expand a tilde"); - component.def("has_magic", &hasMagic, "Check if a pattern has magic"); - component.def("is_hidden", &isHidden, "Check if a path is hidden"); - component.def("is_recursive", &isRecursive, + component.def("translate", &translate, "pattern_matching", + "Translate a pattern"); + component.def("compile_pattern", &compilePattern, "pattern_matching", + "Compile a pattern"); + component.def("fnmatch", &fnmatch, "pattern_matching", + "Check if a name matches a pattern"); + component.def("filter", &filter, "pattern_matching", + "Filter a list of names"); + component.def("expand_tilde", &expandTilde, "path_operations", + "Expand a tilde"); + component.def("has_magic", &hasMagic, "pattern_matching", + "Check if a pattern has magic"); + component.def("is_hidden", &isHidden, "path_operations", + "Check if a path is hidden"); + component.def("is_recursive", &isRecursive, "pattern_matching", "Check if a pattern is recursive"); - component.def("iter_dir", &iterDirectory, "Iterate a directory"); - component.def("rlistdir", &rlistdir, "Recursively list a directory"); + component.def("iter_dir", &iterDirectory, "directory_operations", + "Iterate a directory"); + component.def("rlistdir", &rlistdir, "directory_operations", + "Recursively list a directory"); component.def("glob_s", atom::meta::overload_cast(glob), - "Glob a list of files"); + "pattern_matching", "Glob a list of files"); component.def( "glob_v", atom::meta::overload_cast &>(glob), - "Glob a list of files"); - component.def("rglob", &rglob, + "pattern_matching", "Glob a list of files"); + component.def("rglob", &rglob, "pattern_matching", "Recursively glob a list of files"); - component.def("glob0", &glob0, "Glob0 a list of files"); - component.def("glob1", &glob1, "Glob1 a list of files"); - component.def("glob2", &glob2, "Glob2 a list of files"); + component.def("glob0", &glob0, "pattern_matching", "Glob0 a list of files"); + component.def("glob1", &glob1, "pattern_matching", "Glob1 a list of files"); + component.def("glob2", &glob2, "pattern_matching", "Glob2 a list of files"); component.def( "mkdir", [](const std::string &path) -> bool { return createDirectory(path); }, - "Create a directory"); + "directory_operations", "Create a directory"); component.def("mkdir_r", &createDirectoriesRecursive, - "Create a directory recursively"); - component.def("rmdir", &removeDirectory, "Remove a directory"); + "directory_operations", "Create a directory recursively"); + component.def("rmdir", &removeDirectory, "directory_operations", + "Remove a directory"); component.def("rmdir_r", &removeDirectoriesRecursive, - "Remove a directory recursively"); - component.def("move", &moveDirectory, "Move a directory"); - component.def("rename", &renameDirectory, "Rename a directory"); - component.def("copy", ©File, "Copy a file"); - component.def("move_file", &moveFile, "Move a file"); - component.def("rename_file", &renameFile, "Rename a file"); - component.def("remove", &removeFile, "Remove a file"); - component.def("mksymlink", &createSymlink, "Create a symbolic link"); - component.def("rmsymlink", &removeSymlink, "Remove a symbolic link"); + "directory_operations", "Remove a directory recursively"); + component.def("move", &moveDirectory, "directory_operations", + "Move a directory"); + component.def("rename", &renameDirectory, "directory_operations", + "Rename a directory"); + component.def("copy", ©File, "file_operations", "Copy a file"); + component.def("move_file", &moveFile, "file_operations", "Move a file"); + component.def("rename_file", &renameFile, "file_operations", + "Rename a file"); + component.def("remove", &removeFile, "file_operations", "Remove a file"); + component.def("mksymlink", &createSymlink, "file_operations", + "Create a symbolic link"); + component.def("rmsymlink", &removeSymlink, "file_operations", + "Remove a symbolic link"); DLOG_F(INFO, "Loaded module {}", component.getName()); -}); +}); \ No newline at end of file diff --git a/modules/atom.io/package.json b/modules/atom.io/package.json index fc0abc8d..324e9aeb 100644 --- a/modules/atom.io/package.json +++ b/modules/atom.io/package.json @@ -1,7 +1,6 @@ { "name": "atom.io", "version": "1.0.0", - "type": "shared", "description": "Atom IO Module", "license": "GPL-3.0-or-later", "author": "Max Qian", @@ -10,19 +9,15 @@ "url": "https://github.com/ElementAstro/Lithium" }, "bugs": { - "type": "git", "url": "https://github.com/ElementAstro/Lithium/issues" }, - "homepage": { - "type": "git", - "url": "https://github.com/ElementAstro/Lithium" - }, + "homepage": "https://github.com/ElementAstro/Lithium", "keywords": [ "lithium", "config" ], "scripts": { - "build": "cmake --build-type=Release -- -j 4", + "build": "cmake --build . --config Release -- -j 4", "lint": "clang-format -i src/*.cpp src/*.h" }, "modules": [ @@ -31,4 +26,4 @@ "entry": "getInstance" } ] -} +} \ No newline at end of file diff --git a/modules/atom.io/pymodule.cpp b/modules/atom.io/pymodule.cpp index e4dd4421..8be6accc 100644 --- a/modules/atom.io/pymodule.cpp +++ b/modules/atom.io/pymodule.cpp @@ -1,54 +1,460 @@ #include +#include +#include "atom/io/async_compress.hpp" +#include "atom/io/async_glob.hpp" +#include "atom/io/async_io.hpp" #include "atom/io/compress.hpp" #include "atom/io/glob.hpp" #include "atom/io/io.hpp" +#include "atom/io/pushd.hpp" namespace py = pybind11; -using namespace atom::io; - -PYBIND11_MODULE(atom_io, m) { - m.def("compress", &compressFile, "Compress a file"); - m.def("decompress", &decompressFile, "Decompress a file"); - m.def("create_zip", &createZip, "Create a zip file"); - m.def("extract_zip", &extractZip, "Extract a zip file"); - m.def("compress_folder", &compressFolder, "Compress a folder"); - - m.def("translate", &translate, "Translate a pattern"); - m.def("compile_pattern", &compilePattern, "Compile a pattern"); - m.def("fnmatch", &fnmatch, "Check if a name matches a pattern"); - m.def("filter", &filter, "Filter a list of names"); - m.def("expand_tilde", &expandTilde, "Expand a tilde"); - m.def("has_magic", &hasMagic, "Check if a pattern has magic"); - m.def("is_hidden", &isHidden, "Check if a path is hidden"); - m.def("is_recursive", &isRecursive, "Check if a pattern is recursive"); - m.def("iter_dir", &iterDirectory, "Iterate a directory"); - m.def("rlistdir", &rlistdir, "Recursively list a directory"); - m.def("glob_s", atom::meta::overload_cast(glob), - "Glob a list of files"); - m.def("glob_v", - atom::meta::overload_cast &>(glob), - "Glob a list of files"); - m.def("rglob", &rglob, "Recursively glob a list of files"); - m.def("glob0", &glob0, "Glob0 a list of files"); - m.def("glob1", &glob1, "Glob1 a list of files"); - m.def("glob2", &glob2, "Glob2 a list of files"); - - m.def( - "mkdir", - [](const std::string &path) -> bool { return createDirectory(path); }, - "Create a directory"); - m.def("mkdir_r", &createDirectoriesRecursive, - "Create a directory recursively"); - m.def("rmdir", &removeDirectory, "Remove a directory"); - m.def("rmdir_r", &removeDirectoriesRecursive, - "Remove a directory recursively"); - m.def("move", &moveDirectory, "Move a directory"); - m.def("rename", &renameDirectory, "Rename a directory"); - m.def("copy", ©File, "Copy a file"); - m.def("move_file", &moveFile, "Move a file"); - m.def("rename_file", &renameFile, "Rename a file"); - m.def("remove", &removeFile, "Remove a file"); - m.def("mksymlink", &createSymlink, "Create a symbolic link"); - m.def("rmsymlink", &removeSymlink, "Remove a symbolic link"); -} + +PYBIND11_MODULE(io, m) { + m.doc() = "Python bindings for Atom IO Module"; + + py::enum_(m, "path_type") + .value("NOT_EXISTS", atom::io::PathType::NOT_EXISTS) + .value("REGULAR_FILE", atom::io::PathType::REGULAR_FILE) + .value("DIRECTORY", atom::io::PathType::DIRECTORY) + .value("SYMLINK", atom::io::PathType::SYMLINK) + .value("OTHER", atom::io::PathType::OTHER); + + py::class_(m, + "create_directories_options") + .def(py::init<>()) + .def_readwrite("verbose", &atom::io::CreateDirectoriesOptions::verbose) + .def_readwrite("dry_run", &atom::io::CreateDirectoriesOptions::dryRun) + .def_readwrite("delay", &atom::io::CreateDirectoriesOptions::delay) + .def_readwrite("filter", &atom::io::CreateDirectoriesOptions::filter) + .def_readwrite("on_create", + &atom::io::CreateDirectoriesOptions::onCreate) + .def_readwrite("on_delete", + &atom::io::CreateDirectoriesOptions::onDelete); + + m.def("create_directory", + py::overload_cast(&atom::io::createDirectory), + "Create a directory", py::arg("path"), py::arg("root_dir") = ""); + + m.def("create_directories_recursive", &atom::io::createDirectoriesRecursive, + "Create directories recursively", py::arg("base_path"), + py::arg("subdirs"), py::arg("options")); + + m.def("remove_directory", &atom::io::removeDirectory, "Remove a directory", + py::arg("path")); + + m.def("remove_directories_recursive", &atom::io::removeDirectoriesRecursive, + "Remove directories recursively", py::arg("base_path"), + py::arg("subdirs"), + py::arg("options") = atom::io::CreateDirectoriesOptions()); + + m.def("rename_directory", &atom::io::renameDirectory, "Rename a directory", + py::arg("old_path"), py::arg("new_path")); + + m.def("move_directory", &atom::io::moveDirectory, "Move a directory", + py::arg("old_path"), py::arg("new_path")); + + m.def("copy_file", &atom::io::copyFile, "Copy a file", py::arg("src_path"), + py::arg("dst_path")); + + m.def("move_file", &atom::io::moveFile, "Move a file", py::arg("src_path"), + py::arg("dst_path")); + + m.def("rename_file", &atom::io::renameFile, "Rename a file", + py::arg("old_path"), py::arg("new_path")); + + m.def("remove_file", &atom::io::removeFile, "Remove a file", + py::arg("path")); + + m.def("create_symlink", &atom::io::createSymlink, "Create a symbolic link", + py::arg("target_path"), py::arg("symlink_path")); + + m.def("remove_symlink", &atom::io::removeSymlink, "Remove a symbolic link", + py::arg("path")); + + m.def("file_size", &atom::io::fileSize, "Get the size of a file", + py::arg("path")); + + m.def("truncate_file", &atom::io::truncateFile, "Truncate a file", + py::arg("path"), py::arg("size")); + + m.def("jwalk", &atom::io::jwalk, "Recursively walk through a directory", + py::arg("root")); + + m.def("fwalk", &atom::io::fwalk, "Recursively walk through a directory", + py::arg("root"), py::arg("callback")); + + m.def("convert_to_linux_path", &atom::io::convertToLinuxPath, + "Convert Windows path to Linux path", py::arg("windows_path")); + + m.def("convert_to_windows_path", &atom::io::convertToWindowsPath, + "Convert Linux path to Windows path", py::arg("linux_path")); + + m.def("norm_path", &atom::io::normPath, "Normalize a path", + py::arg("raw_path")); + + m.def("is_folder_name_valid", &atom::io::isFolderNameValid, + "Check if the folder name is valid", py::arg("folder_name")); + + m.def("is_file_name_valid", &atom::io::isFileNameValid, + "Check if the file name is valid", py::arg("file_name")); + + m.def("is_folder_exists", &atom::io::isFolderExists, + "Check if the folder exists", py::arg("folder_name")); + + m.def("is_file_exists", &atom::io::isFileExists, "Check if the file exists", + py::arg("file_name")); + + m.def("is_folder_empty", &atom::io::isFolderEmpty, + "Check if the folder is empty", py::arg("folder_name")); + + m.def("is_absolute_path", &atom::io::isAbsolutePath, + "Check if the path is an absolute path", py::arg("path")); + + m.def("change_working_directory", &atom::io::changeWorkingDirectory, + "Change the working directory", py::arg("directory_path")); + + m.def("get_file_times", &atom::io::getFileTimes, "Get the file times", + py::arg("file_path")); + + py::enum_(m, "file_option") + .value("PATH", atom::io::FileOption::PATH) + .value("NAME", atom::io::FileOption::NAME); + + m.def("check_file_type_in_folder", &atom::io::checkFileTypeInFolder, + "Check the file type in the folder", py::arg("folder_path"), + py::arg("file_types"), py::arg("file_option")); + + m.def("is_executable_file", &atom::io::isExecutableFile, + "Check whether the specified file exists", py::arg("file_name"), + py::arg("file_ext")); + + m.def("get_file_size", &atom::io::getFileSize, "Get the file size", + py::arg("file_path")); + + m.def("calculate_chunk_size", &atom::io::calculateChunkSize, + "Calculate the chunk size", py::arg("file_size"), + py::arg("num_chunks")); + + m.def("split_file", &atom::io::splitFile, + "Split a file into multiple parts", py::arg("file_path"), + py::arg("chunk_size"), py::arg("output_pattern") = ""); + + m.def("merge_files", &atom::io::mergeFiles, + "Merge multiple parts into a single file", + py::arg("output_file_path"), py::arg("part_files")); + + m.def("quick_split", &atom::io::quickSplit, + "Quickly split a file into multiple parts", py::arg("file_path"), + py::arg("num_chunks"), py::arg("output_pattern") = ""); + + m.def("quick_merge", &atom::io::quickMerge, + "Quickly merge multiple parts into a single file", + py::arg("output_file_path"), py::arg("part_pattern"), + py::arg("num_chunks")); + + m.def("get_executable_name_from_path", &atom::io::getExecutableNameFromPath, + "Get the executable name from the path", py::arg("path")); + + m.def("check_path_type", &atom::io::checkPathType, "Get the file type", + py::arg("path")); + + m.def("count_lines_in_file", &atom::io::countLinesInFile, + "Count lines in a file", py::arg("file_path")); + + m.def("search_executable_files", &atom::io::searchExecutableFiles, + "Search executable files", py::arg("dir"), py::arg("search_str")); + + m.def("compress_file", &atom::io::compressFile, "Compress a single file", + py::arg("file_name"), py::arg("output_folder")); + + m.def("decompress_file", &atom::io::decompressFile, + "Decompress a single file", py::arg("file_name"), + py::arg("output_folder")); + + m.def("compress_folder", &atom::io::compressFolder, + "Compress all files in a specified directory", + py::arg("folder_name")); + + m.def("extract_zip", &atom::io::extractZip, "Extract a single ZIP file", + py::arg("zip_file"), py::arg("destination_folder")); + + m.def("create_zip", &atom::io::createZip, "Create a ZIP file", + py::arg("source_folder"), py::arg("zip_file"), + py::arg("compression_level") = -1); + + m.def("list_files_in_zip", &atom::io::listFilesInZip, + "List files in a ZIP file", py::arg("zip_file")); + + m.def("file_exists_in_zip", &atom::io::fileExistsInZip, + "Check if a specified file exists in a ZIP file", py::arg("zip_file"), + py::arg("file_name")); + + m.def("remove_file_from_zip", &atom::io::removeFileFromZip, + "Remove a specified file from a ZIP file", py::arg("zip_file"), + py::arg("file_name")); + + m.def("get_zip_file_size", &atom::io::getZipFileSize, + "Get the size of a file in a ZIP file", py::arg("zip_file")); + + py::class_(m, "DirectoryStack") + .def(py::init(), py::arg("io_context")) + .def("async_pushd", &atom::io::DirectoryStack::asyncPushd, + "Push the current directory onto the stack and change to the " + "specified directory asynchronously", + py::arg("new_dir"), py::arg("handler")) + .def("async_popd", &atom::io::DirectoryStack::asyncPopd, + "Pop the directory from the stack and change back to it " + "asynchronously", + py::arg("handler")) + .def("peek", &atom::io::DirectoryStack::peek, + "View the top directory in the stack without changing to it") + .def("dirs", &atom::io::DirectoryStack::dirs, + "Display the current stack of directories") + .def("clear", &atom::io::DirectoryStack::clear, + "Clear the directory stack") + .def("swap", &atom::io::DirectoryStack::swap, + "Swap two directories in the stack given their indices", + py::arg("index1"), py::arg("index2")) + .def("remove", &atom::io::DirectoryStack::remove, + "Remove a directory from the stack at the specified index", + py::arg("index")) + .def("async_goto_index", &atom::io::DirectoryStack::asyncGotoIndex, + "Change to the directory at the specified index in the stack " + "asynchronously", + py::arg("index"), py::arg("handler")) + .def("async_save_stack_to_file", + &atom::io::DirectoryStack::asyncSaveStackToFile, + "Save the directory stack to a file asynchronously", + py::arg("filename"), py::arg("handler")) + .def("async_load_stack_from_file", + &atom::io::DirectoryStack::asyncLoadStackFromFile, + "Load the directory stack from a file asynchronously", + py::arg("filename"), py::arg("handler")) + .def("size", &atom::io::DirectoryStack::size, + "Get the size of the directory stack") + .def("is_empty", &atom::io::DirectoryStack::isEmpty, + "Check if the directory stack is empty") + .def("async_get_current_directory", + &atom::io::DirectoryStack::asyncGetCurrentDirectory, + "Get the current directory path asynchronously", + py::arg("handler")); + + m.def("string_replace", &atom::io::stringReplace, + "Replace a substring in a string", py::arg("str"), py::arg("from"), + py::arg("to_str")); + + m.def("translate", &atom::io::translate, + "Translate a pattern to a regex string", py::arg("pattern")); + + m.def("compile_pattern", &atom::io::compilePattern, + "Compile a pattern to a regex", py::arg("pattern")); + + m.def("fnmatch", &atom::io::fnmatch, "Match a filename against a pattern", + py::arg("name"), py::arg("pattern")); + + m.def("filter", &atom::io::filter, + "Filter a list of names against a pattern", py::arg("names"), + py::arg("pattern")); + + m.def("expand_tilde", &atom::io::expandTilde, "Expand tilde in a path", + py::arg("path")); + + m.def("has_magic", &atom::io::hasMagic, + "Check if a pathname contains any magic characters", + py::arg("pathname")); + + m.def("is_hidden", &atom::io::isHidden, "Check if a pathname is hidden", + py::arg("pathname")); + + m.def("is_recursive", &atom::io::isRecursive, + "Check if a pattern is recursive", py::arg("pattern")); + + m.def("iter_directory", &atom::io::iterDirectory, + "Iterate over a directory", py::arg("dirname"), py::arg("dironly")); + + m.def("rlistdir", &atom::io::rlistdir, "Recursively list a directory", + py::arg("dirname"), py::arg("dironly")); + + m.def("glob2", &atom::io::glob2, "Recursive glob", py::arg("dirname"), + py::arg("pattern"), py::arg("dironly")); + + m.def("glob1", &atom::io::glob1, "Non-recursive glob", py::arg("dirname"), + py::arg("pattern"), py::arg("dironly")); + + m.def("glob0", &atom::io::glob0, "Glob with no magic", py::arg("dirname"), + py::arg("basename"), py::arg("dironly")); + + m.def("glob", + py::overload_cast(&atom::io::glob), + "Glob with pathname", py::arg("pathname"), + py::arg("recursive") = false, py::arg("dironly") = false); + + m.def("glob", + py::overload_cast &>(&atom::io::glob), + "Glob with pathnames", py::arg("pathnames")); + + m.def("rglob", py::overload_cast(&atom::io::rglob), + "Recursive glob with pathname", py::arg("pathname")); + + m.def("rglob", + py::overload_cast &>(&atom::io::rglob), + "Recursive glob with pathnames", py::arg("pathnames")); + + m.def("glob", + py::overload_cast &>( + &atom::io::glob), + "Glob with initializer list", py::arg("pathnames")); + + m.def("rglob", + py::overload_cast &>( + &atom::io::rglob), + "Recursive glob with initializer list", py::arg("pathnames")); + + py::class_(m, "BaseCompressor") + .def("start", &atom::async::io::BaseCompressor::start, + "Start the compression process"); + + py::class_(m, "SingleFileCompressor") + .def(py::init(), + py::arg("io_context"), py::arg("input_file"), + py::arg("output_file")) + .def("start", &atom::async::io::SingleFileCompressor::start, + "Start the compression process"); + + py::class_(m, "DirectoryCompressor") + .def(py::init(), + py::arg("io_context"), py::arg("input_dir"), + py::arg("output_file")) + .def("start", &atom::async::io::DirectoryCompressor::start, + "Start the compression process"); + + py::class_(m, "BaseDecompressor") + .def("start", &atom::async::io::BaseDecompressor::start, + "Start the decompression process"); + + py::class_(m, "SingleFileDecompressor") + .def(py::init(), + py::arg("io_context"), py::arg("input_file"), + py::arg("output_folder")) + .def("start", &atom::async::io::SingleFileDecompressor::start, + "Start the decompression process"); + + py::class_(m, "DirectoryDecompressor") + .def(py::init(), + py::arg("io_context"), py::arg("input_dir"), + py::arg("output_folder")) + .def("start", &atom::async::io::DirectoryDecompressor::start, + "Start the decompression process"); + + py::class_(m, "ZipOperation") + .def("start", &atom::async::io::ZipOperation::start, + "Start the ZIP operation"); + + py::class_( + m, "ListFilesInZip") + .def(py::init(), + py::arg("io_context"), py::arg("zip_file")) + .def("start", &atom::async::io::ListFilesInZip::start, + "Start the ZIP operation") + .def("get_file_list", &atom::async::io::ListFilesInZip::getFileList, + "Get the list of files in the ZIP archive"); + + py::class_( + m, "FileExistsInZip") + .def(py::init(), + py::arg("io_context"), py::arg("zip_file"), py::arg("file_name")) + .def("start", &atom::async::io::FileExistsInZip::start, + "Start the ZIP operation") + .def("found", &atom::async::io::FileExistsInZip::found, + "Check if the file was found in the ZIP archive"); + + py::class_(m, "RemoveFileFromZip") + .def(py::init(), + py::arg("io_context"), py::arg("zip_file"), py::arg("file_name")) + .def("start", &atom::async::io::RemoveFileFromZip::start, + "Start the ZIP operation") + .def("is_successful", &atom::async::io::RemoveFileFromZip::isSuccessful, + "Check if the file removal was successful"); + + py::class_( + m, "GetZipFileSize") + .def(py::init(), + py::arg("io_context"), py::arg("zip_file")) + .def("start", &atom::async::io::GetZipFileSize::start, + "Start the ZIP operation") + .def("get_size_value", &atom::async::io::GetZipFileSize::getSizeValue, + "Get the size of the ZIP file"); + + py::class_(m, "AsyncGlob") + .def(py::init(), py::arg("io_context")) + .def("glob", &atom::io::AsyncGlob::glob, + "Perform a glob operation to match files", py::arg("pathname"), + py::arg("callback"), py::arg("recursive") = false, + py::arg("dironly") = false); + + py::class_(m, "AsyncFile") + .def(py::init(), py::arg("io_context")) + .def("async_read", &atom::async::io::AsyncFile::asyncRead, + "Asynchronously read the content of a file", py::arg("filename"), + py::arg("callback")) + .def("async_write", &atom::async::io::AsyncFile::asyncWrite, + "Asynchronously write content to a file", py::arg("filename"), + py::arg("content"), py::arg("callback")) + .def("async_delete", &atom::async::io::AsyncFile::asyncDelete, + "Asynchronously delete a file", py::arg("filename"), + py::arg("callback")) + .def("async_copy", &atom::async::io::AsyncFile::asyncCopy, + "Asynchronously copy a file", py::arg("src"), py::arg("dest"), + py::arg("callback")) + .def("async_read_with_timeout", + &atom::async::io::AsyncFile::asyncReadWithTimeout, + "Asynchronously read the content of a file with a timeout", + py::arg("filename"), py::arg("timeoutMs"), py::arg("callback")) + .def("async_batch_read", &atom::async::io::AsyncFile::asyncBatchRead, + "Asynchronously read the content of multiple files", + py::arg("files"), py::arg("callback")) + .def("async_stat", &atom::async::io::AsyncFile::asyncStat, + "Asynchronously retrieve the status of a file", + py::arg("filename"), py::arg("callback")) + .def("async_move", &atom::async::io::AsyncFile::asyncMove, + "Asynchronously move a file", py::arg("src"), py::arg("dest"), + py::arg("callback")) + .def("async_change_permissions", + &atom::async::io::AsyncFile::asyncChangePermissions, + "Asynchronously change the permissions of a file", + py::arg("filename"), py::arg("perms"), py::arg("callback")) + .def("async_create_directory", + &atom::async::io::AsyncFile::asyncCreateDirectory, + "Asynchronously create a directory", py::arg("path"), + py::arg("callback")) + .def("async_exists", &atom::async::io::AsyncFile::asyncExists, + "Asynchronously check if a file exists", py::arg("filename"), + py::arg("callback")); + + py::class_(m, "AsyncDirectory") + .def(py::init(), py::arg("io_context")) + .def("async_create", &atom::async::io::AsyncDirectory::asyncCreate, + "Asynchronously create a directory", py::arg("path"), + py::arg("callback")) + .def("async_remove", &atom::async::io::AsyncDirectory::asyncRemove, + "Asynchronously remove a directory", py::arg("path"), + py::arg("callback")) + .def("async_list_contents", + &atom::async::io::AsyncDirectory::asyncListContents, + "Asynchronously list the contents of a directory", py::arg("path"), + py::arg("callback")) + .def("async_exists", &atom::async::io::AsyncDirectory::asyncExists, + "Asynchronously check if a directory exists", py::arg("path"), + py::arg("callback")); +} \ No newline at end of file diff --git a/modules/atom.search/pymodule.cpp b/modules/atom.search/pymodule.cpp new file mode 100644 index 00000000..78813854 --- /dev/null +++ b/modules/atom.search/pymodule.cpp @@ -0,0 +1,177 @@ +#include +#include + +#include "atom/search/cache.hpp" +#include "atom/search/lru.hpp" +#include "atom/search/search.hpp" + +namespace py = pybind11; +using namespace atom::search; + +template +void bind_resource_cache(py::module &m, const std::string &name) { + py::class_>(m, name.c_str()) + .def(py::init(), "Constructor", py::arg("max_size")) + .def("insert", &ResourceCache::insert, + "Insert a resource into the cache with an expiration time", + py::arg("key"), py::arg("value"), py::arg("expiration_time")) + .def("contains", &ResourceCache::contains, + "Check if the cache contains a resource with the specified key", + py::arg("key")) + .def("get", &ResourceCache::get, + "Retrieve a resource from the cache", py::arg("key")) + .def("remove", &ResourceCache::remove, + "Remove a resource from the cache", py::arg("key")) + .def("async_get", &ResourceCache::asyncGet, + "Asynchronously retrieve a resource from the cache", + py::arg("key")) + .def("async_insert", &ResourceCache::asyncInsert, + "Asynchronously insert a resource into the cache with an " + "expiration time", + py::arg("key"), py::arg("value"), py::arg("expiration_time")) + .def("clear", &ResourceCache::clear, + "Clear all resources from the cache") + .def("size", &ResourceCache::size, + "Get the number of resources in the cache") + .def("empty", &ResourceCache::empty, "Check if the cache is empty") + .def("evict_oldest", &ResourceCache::evictOldest, + "Evict the oldest resource from the cache") + .def("is_expired", &ResourceCache::isExpired, + "Check if a resource with the specified key is expired", + py::arg("key")) + .def("async_load", &ResourceCache::asyncLoad, + "Asynchronously load a resource into the cache using a provided " + "function", + py::arg("key"), py::arg("load_data_function")) + .def("set_max_size", &ResourceCache::setMaxSize, + "Set the maximum size of the cache", py::arg("max_size")) + .def("set_expiration_time", &ResourceCache::setExpirationTime, + "Set the expiration time for a resource in the cache", + py::arg("key"), py::arg("expiration_time")) + .def("read_from_file", &ResourceCache::readFromFile, + "Read resources from a file and insert them into the cache", + py::arg("file_path"), py::arg("deserializer")) + .def("write_to_file", &ResourceCache::writeToFile, + "Write the resources in the cache to a file", py::arg("file_path"), + py::arg("serializer")) + .def("remove_expired", &ResourceCache::removeExpired, + "Remove expired resources from the cache") + .def("read_from_json_file", &ResourceCache::readFromJsonFile, + "Read resources from a JSON file and insert them into the cache", + py::arg("file_path"), py::arg("from_json")) + .def("write_to_json_file", &ResourceCache::writeToJsonFile, + "Write the resources in the cache to a JSON file", + py::arg("file_path"), py::arg("to_json")) + .def("insert_batch", &ResourceCache::insertBatch, + "Insert multiple resources into the cache with an expiration time", + py::arg("items"), py::arg("expiration_time")) + .def("remove_batch", &ResourceCache::removeBatch, + "Remove multiple resources from the cache", py::arg("keys")) + .def("on_insert", &ResourceCache::onInsert, + "Register a callback to be called on insertion", + py::arg("callback")) + .def("on_remove", &ResourceCache::onRemove, + "Register a callback to be called on removal", py::arg("callback")) + .def("get_statistics", &ResourceCache::getStatistics, + "Retrieve cache statistics"); +} + +template +void bind_thread_safe_lru_cache(py::module &m, const std::string &name) { + py::class_>(m, name.c_str()) + .def(py::init(), "Constructor", py::arg("max_size")) + .def("get", &ThreadSafeLRUCache::get, + "Retrieve a value from the cache", py::arg("key")) + .def("put", &ThreadSafeLRUCache::put, + "Insert or update a value in the cache", py::arg("key"), + py::arg("value"), py::arg("ttl") = std::nullopt) + .def("erase", &ThreadSafeLRUCache::erase, + "Erase an item from the cache", py::arg("key")) + .def("clear", &ThreadSafeLRUCache::clear, + "Clear all items from the cache") + .def("keys", &ThreadSafeLRUCache::keys, + "Retrieve all keys in the cache") + .def("pop_lru", &ThreadSafeLRUCache::popLru, + "Remove and return the least recently used item") + .def("resize", &ThreadSafeLRUCache::resize, + "Resize the cache to a new maximum size", py::arg("new_max_size")) + .def("size", &ThreadSafeLRUCache::size, + "Get the current size of the cache") + .def("load_factor", &ThreadSafeLRUCache::loadFactor, + "Get the current load factor of the cache") + .def("set_insert_callback", + &ThreadSafeLRUCache::setInsertCallback, + "Set the callback function to be called when a new item is " + "inserted", + py::arg("callback")) + .def("set_erase_callback", + &ThreadSafeLRUCache::setEraseCallback, + "Set the callback function to be called when an item is erased", + py::arg("callback")) + .def("set_clear_callback", + &ThreadSafeLRUCache::setClearCallback, + "Set the callback function to be called when the cache is cleared", + py::arg("callback")) + .def("hit_rate", &ThreadSafeLRUCache::hitRate, + "Get the hit rate of the cache") + .def("save_to_file", &ThreadSafeLRUCache::saveToFile, + "Save the cache contents to a file", py::arg("filename")) + .def("load_from_file", &ThreadSafeLRUCache::loadFromFile, + "Load cache contents from a file", py::arg("filename")); +} + +PYBIND11_MODULE(search, m) { + m.doc() = "Search engine module"; + + bind_resource_cache(m, "StringResourceCache"); + bind_resource_cache(m, "IntResourceCache"); + bind_resource_cache(m, "DoubleResourceCache"); + + bind_thread_safe_lru_cache(m, "StringLRUCache"); + bind_thread_safe_lru_cache(m, "IntLRUCache"); + bind_thread_safe_lru_cache(m, "IntDoubleLRUCache"); + bind_thread_safe_lru_cache(m, "IntStringLRUCache"); + bind_thread_safe_lru_cache(m, "StringIntLRUCache"); + bind_thread_safe_lru_cache(m, "StringDoubleLRUCache"); + + py::register_exception( + m, "DocumentNotFoundException"); + + py::class_(m, "Document") + .def(py::init>(), + py::arg("id"), py::arg("content"), py::arg("tags")) + .def_readwrite("id", &Document::id) + .def_readwrite("content", &Document::content) + .def_readwrite("tags", &Document::tags) + .def_readwrite("click_count", &Document::clickCount); + + py::class_(m, "SearchEngine") + .def(py::init<>()) + .def("add_document", &SearchEngine::addDocument, + "Add a document to the search engine", py::arg("doc")) + .def("remove_document", &SearchEngine::removeDocument, + "Remove a document from the search engine", py::arg("doc_id")) + .def("update_document", &SearchEngine::updateDocument, + "Update an existing document in the search engine", py::arg("doc")) + .def("search_by_tag", &SearchEngine::searchByTag, + "Search for documents by a specific tag", py::arg("tag")) + .def("fuzzy_search_by_tag", &SearchEngine::fuzzySearchByTag, + "Perform a fuzzy search for documents by a tag with a specified " + "tolerance", + py::arg("tag"), py::arg("tolerance")) + .def("search_by_tags", &SearchEngine::searchByTags, + "Search for documents by multiple tags", py::arg("tags")) + .def("search_by_content", &SearchEngine::searchByContent, + "Search for documents by content", py::arg("query")) + .def("boolean_search", &SearchEngine::booleanSearch, + "Perform a boolean search for documents by a query", + py::arg("query")) + .def("auto_complete", &SearchEngine::autoComplete, + "Provide autocomplete suggestions for a given prefix", + py::arg("prefix")) + .def("save_index", &SearchEngine::saveIndex, + "Save the current index to a file", py::arg("filename")) + .def("load_index", &SearchEngine::loadIndex, + "Load the index from a file", py::arg("filename")); +} \ No newline at end of file diff --git a/modules/atom.sysinfo/pymodule.cpp b/modules/atom.sysinfo/pymodule.cpp index 66eeafbc..667ab960 100644 --- a/modules/atom.sysinfo/pymodule.cpp +++ b/modules/atom.sysinfo/pymodule.cpp @@ -1,13 +1,16 @@ #include #include "atom/sysinfo/battery.hpp" +#include "atom/sysinfo/bios.hpp" #include "atom/sysinfo/cpu.hpp" #include "atom/sysinfo/disk.hpp" #include "atom/sysinfo/gpu.hpp" +#include "atom/sysinfo/locale.hpp" #include "atom/sysinfo/memory.hpp" #include "atom/sysinfo/os.hpp" #include "atom/sysinfo/sn.hpp" #include "atom/sysinfo/wifi.hpp" +#include "atom/sysinfo/wm.hpp" namespace py = pybind11; using namespace atom::system; @@ -15,25 +18,35 @@ using namespace atom::system; PYBIND11_MODULE(atom_io, m) { // CPU m.def("cpu_usage", &getCurrentCpuUsage, "Get current CPU usage percentage"); - m.def("cpu_temperature", &getCurrentCpuTemperature, "Get current CPU temperature"); + m.def("cpu_temperature", &getCurrentCpuTemperature, + "Get current CPU temperature"); m.def("cpu_model", &getCPUModel, "Get CPU model name"); m.def("cpu_identifier", &getProcessorIdentifier, "Get CPU identifier"); m.def("cpu_frequency", &getProcessorFrequency, "Get current CPU frequency"); - m.def("physical_packages", &getNumberOfPhysicalPackages, "Get number of physical CPU packages"); - m.def("logical_cpus", &getNumberOfPhysicalCPUs, "Get number of logical CPUs"); + m.def("physical_packages", &getNumberOfPhysicalPackages, + "Get number of physical CPU packages"); + m.def("logical_cpus", &getNumberOfPhysicalCPUs, + "Get number of logical CPUs"); m.def("cache_sizes", &getCacheSizes, "Get CPU cache sizes"); // Memory - m.def("memory_usage", &getMemoryUsage, "Get current memory usage percentage"); + m.def("memory_usage", &getMemoryUsage, + "Get current memory usage percentage"); m.def("total_memory", &getTotalMemorySize, "Get total memory size"); - m.def("available_memory", &getAvailableMemorySize, "Get available memory size"); - m.def("physical_memory_info", &getPhysicalMemoryInfo, "Get physical memory slot info"); - m.def("virtual_memory_max", &getVirtualMemoryMax, "Get virtual memory max size"); - m.def("virtual_memory_used", &getVirtualMemoryUsed, "Get virtual memory used size"); - m.def("swap_memory_total", &getSwapMemoryTotal, "Get swap memory total size"); + m.def("available_memory", &getAvailableMemorySize, + "Get available memory size"); + m.def("physical_memory_info", &getPhysicalMemoryInfo, + "Get physical memory slot info"); + m.def("virtual_memory_max", &getVirtualMemoryMax, + "Get virtual memory max size"); + m.def("virtual_memory_used", &getVirtualMemoryUsed, + "Get virtual memory used size"); + m.def("swap_memory_total", &getSwapMemoryTotal, + "Get swap memory total size"); m.def("swap_memory_used", &getSwapMemoryUsed, "Get swap memory used size"); m.def("committed_memory", &getCommittedMemory, "Get committed memory"); - m.def("uncommitted_memory", &getUncommittedMemory, "Get uncommitted memory"); + m.def("uncommitted_memory", &getUncommittedMemory, + "Get uncommitted memory"); py::class_(m, "MemoryInfo"); py::class_(m, "MemorySlot") @@ -48,25 +61,35 @@ PYBIND11_MODULE(atom_io, m) { // Disk m.def("disk_usage", &getDiskUsage, "Get current disk usage percentage"); m.def("get_drive_model", &getDriveModel, "Get drive model"); - m.def("storage_device_models", &getStorageDeviceModels, "Get storage device models"); + m.def("storage_device_models", &getStorageDeviceModels, + "Get storage device models"); m.def("available_drives", &getAvailableDrives, "Get available drives"); - m.def("calculate_disk_usage_percentage", &calculateDiskUsagePercentage, "Calculate disk usage percentage"); + m.def("calculate_disk_usage_percentage", &calculateDiskUsagePercentage, + "Calculate disk usage percentage"); m.def("file_system_type", &getFileSystemType, "Get file system type"); // OS - m.def("get_os_info", &getOperatingSystemInfo, "Get operating system information"); + m.def("get_os_info", &getOperatingSystemInfo, + "Get operating system information"); m.def("is_wsl", &isWsl, "Check if running in WSL"); py::class_(m, "OperatingSystemInfo"); // SN - m.def("get_bios_serial_number", &HardwareInfo::getBiosSerialNumber, "Get bios serial number"); - m.def("get_motherboard_serial_number", &HardwareInfo::getMotherboardSerialNumber, "Get motherboard serial number"); - m.def("get_cpu_serial_number", &HardwareInfo::getCpuSerialNumber, "Get cpu serial number"); - m.def("get_disk_serial_numbers", &HardwareInfo::getDiskSerialNumbers, "Get disk serial numbers"); + m.def("get_bios_serial_number", &HardwareInfo::getBiosSerialNumber, + "Get bios serial number"); + m.def("get_motherboard_serial_number", + &HardwareInfo::getMotherboardSerialNumber, + "Get motherboard serial number"); + m.def("get_cpu_serial_number", &HardwareInfo::getCpuSerialNumber, + "Get cpu serial number"); + m.def("get_disk_serial_numbers", &HardwareInfo::getDiskSerialNumbers, + "Get disk serial numbers"); // Wifi - m.def("is_hotspot_connected", &isHotspotConnected, "Check if the hotspot is connected"); - m.def("wired_network", &getCurrentWiredNetwork, "Get current wired network"); + m.def("is_hotspot_connected", &isHotspotConnected, + "Check if the hotspot is connected"); + m.def("wired_network", &getCurrentWiredNetwork, + "Get current wired network"); m.def("wifi_name", &getCurrentWifi, "Get current wifi name"); m.def("current_ip", &getHostIPs, "Get current IP address"); m.def("ipv4_addresses", &getIPv4Addresses, "Get IPv4 addresses"); @@ -74,5 +97,55 @@ PYBIND11_MODULE(atom_io, m) { m.def("interface_names", &getInterfaceNames, "Get interface names"); // GPU - m.def("gpu_info", &getGPUInfo, "Get GPU info"); + m.def("get_gpu_info", &getGPUInfo, "Get GPU information"); + + py::class_(m, "MonitorInfo") + .def(py::init<>()) + .def_readwrite("model", &MonitorInfo::model) + .def_readwrite("identifier", &MonitorInfo::identifier) + .def_readwrite("width", &MonitorInfo::width) + .def_readwrite("height", &MonitorInfo::height) + .def_readwrite("refresh_rate", &MonitorInfo::refreshRate); + + m.def("get_all_monitors_info", &getAllMonitorsInfo, + "Get all monitors information"); + + py::class_(m, "SystemInfo") + .def(py::init<>()) + .def_readwrite("desktop_environment", &SystemInfo::desktopEnvironment) + .def_readwrite("window_manager", &SystemInfo::windowManager) + .def_readwrite("wm_theme", &SystemInfo::wmTheme) + .def_readwrite("icons", &SystemInfo::icons) + .def_readwrite("font", &SystemInfo::font) + .def_readwrite("cursor", &SystemInfo::cursor); + + m.def("get_system_info", &getSystemInfo, "Get system information"); + + py::class_(m, "BiosInfoData") + .def(py::init<>()) + .def_readwrite("version", &BiosInfoData::version) + .def_readwrite("manufacturer", &BiosInfoData::manufacturer) + .def_readwrite("release_date", &BiosInfoData::releaseDate); + + m.def("get_bios_info", &getBiosInfo, "Get BIOS information"); + + py::class_(m, "LocaleInfo") + .def(py::init<>()) + .def_readwrite("language_code", &LocaleInfo::languageCode) + .def_readwrite("country_code", &LocaleInfo::countryCode) + .def_readwrite("locale_name", &LocaleInfo::localeName) + .def_readwrite("language_display_name", + &LocaleInfo::languageDisplayName) + .def_readwrite("country_display_name", &LocaleInfo::countryDisplayName) + .def_readwrite("currency_symbol", &LocaleInfo::currencySymbol) + .def_readwrite("decimal_symbol", &LocaleInfo::decimalSymbol) + .def_readwrite("thousand_separator", &LocaleInfo::thousandSeparator) + .def_readwrite("date_format", &LocaleInfo::dateFormat) + .def_readwrite("time_format", &LocaleInfo::timeFormat) + .def_readwrite("character_encoding", &LocaleInfo::characterEncoding); + + m.def("get_system_language_info", &getSystemLanguageInfo, + "Get system language information"); + m.def("print_locale_info", &printLocaleInfo, "Print locale information", + py::arg("info")); } diff --git a/modules/atom.web/pymodule.cpp b/modules/atom.web/pymodule.cpp new file mode 100644 index 00000000..3ba961a4 --- /dev/null +++ b/modules/atom.web/pymodule.cpp @@ -0,0 +1,249 @@ +#include +#include + +#include "atom/web/address.hpp" +#include "atom/web/curl.hpp" +#include "atom/web/downloader.hpp" +#include "atom/web/httpparser.hpp" +#include "atom/web/time.hpp" +#include "atom/web/utils.hpp" + +namespace py = pybind11; +using namespace atom::web; + +PYBIND11_MODULE(web, m) { + py::class_>(m, "Address") + .def("parse", &Address::parse, "Parse address string", + py::arg("address")) + .def("print_address_type", &Address::printAddressType, + "Print address type") + .def("is_in_range", &Address::isInRange, "Check if address is in range", + py::arg("start"), py::arg("end")) + .def("to_binary", &Address::toBinary, + "Convert address to binary representation") + .def("get_address", &Address::getAddress, "Get address string") + .def("is_equal", &Address::isEqual, "Check if two addresses are equal", + py::arg("other")) + .def("get_type", &Address::getType, "Get address type") + .def("get_network_address", &Address::getNetworkAddress, + "Get network address", py::arg("mask")) + .def("get_broadcast_address", &Address::getBroadcastAddress, + "Get broadcast address", py::arg("mask")) + .def("is_same_subnet", &Address::isSameSubnet, + "Check if two addresses are in the same subnet", py::arg("other"), + py::arg("mask")) + .def("to_hex", &Address::toHex, + "Convert address to hexadecimal representation"); + + py::class_>(m, "IPv4") + .def(py::init<>()) + .def(py::init(), py::arg("address")) + .def("parse", &IPv4::parse, "Parse IPv4 address", py::arg("address")) + .def("print_address_type", &IPv4::printAddressType, + "Print IPv4 address type") + .def("is_in_range", &IPv4::isInRange, + "Check if IPv4 address is in range", py::arg("start"), + py::arg("end")) + .def("to_binary", &IPv4::toBinary, + "Convert IPv4 address to binary representation") + .def("is_equal", &IPv4::isEqual, + "Check if two IPv4 addresses are equal", py::arg("other")) + .def("get_type", &IPv4::getType, "Get IPv4 address type") + .def("get_network_address", &IPv4::getNetworkAddress, + "Get IPv4 network address", py::arg("mask")) + .def("get_broadcast_address", &IPv4::getBroadcastAddress, + "Get IPv4 broadcast address", py::arg("mask")) + .def("is_same_subnet", &IPv4::isSameSubnet, + "Check if two IPv4 addresses are in the same subnet", + py::arg("other"), py::arg("mask")) + .def("to_hex", &IPv4::toHex, + "Convert IPv4 address to hexadecimal representation") + .def("parse_cidr", &IPv4::parseCIDR, + "Parse CIDR formatted IPv4 address", py::arg("cidr")); + + py::class_>(m, "IPv6") + .def(py::init<>()) + .def(py::init(), py::arg("address")) + .def("parse", &IPv6::parse, "Parse IPv6 address", py::arg("address")) + .def("print_address_type", &IPv6::printAddressType, + "Print IPv6 address type") + .def("is_in_range", &IPv6::isInRange, + "Check if IPv6 address is in range", py::arg("start"), + py::arg("end")) + .def("to_binary", &IPv6::toBinary, + "Convert IPv6 address to binary representation") + .def("is_equal", &IPv6::isEqual, + "Check if two IPv6 addresses are equal", py::arg("other")) + .def("get_type", &IPv6::getType, "Get IPv6 address type") + .def("get_network_address", &IPv6::getNetworkAddress, + "Get IPv6 network address", py::arg("mask")) + .def("get_broadcast_address", &IPv6::getBroadcastAddress, + "Get IPv6 broadcast address", py::arg("mask")) + .def("is_same_subnet", &IPv6::isSameSubnet, + "Check if two IPv6 addresses are in the same subnet", + py::arg("other"), py::arg("mask")) + .def("to_hex", &IPv6::toHex, + "Convert IPv6 address to hexadecimal representation") + .def("parse_cidr", &IPv6::parseCIDR, + "Parse CIDR formatted IPv6 address", py::arg("cidr")); + + py::class_>(m, + "UnixDomain") + .def(py::init<>()) + .def(py::init(), py::arg("path")) + .def("parse", &UnixDomain::parse, "Parse Unix domain socket address", + py::arg("path")) + .def("print_address_type", &UnixDomain::printAddressType, + "Print Unix domain socket address type") + .def("is_in_range", &UnixDomain::isInRange, + "Check if Unix domain socket address is in range", + py::arg("start"), py::arg("end")) + .def("to_binary", &UnixDomain::toBinary, + "Convert Unix domain socket address to binary representation") + .def("is_equal", &UnixDomain::isEqual, + "Check if two Unix domain socket addresses are equal", + py::arg("other")) + .def("get_type", &UnixDomain::getType, + "Get Unix domain socket address type") + .def("get_network_address", &UnixDomain::getNetworkAddress, + "Get Unix domain socket network address", py::arg("mask")) + .def("get_broadcast_address", &UnixDomain::getBroadcastAddress, + "Get Unix domain socket broadcast address", py::arg("mask")) + .def("is_same_subnet", &UnixDomain::isSameSubnet, + "Check if two Unix domain socket addresses are in the same subnet", + py::arg("other"), py::arg("mask")) + .def( + "to_hex", &UnixDomain::toHex, + "Convert Unix domain socket address to hexadecimal representation"); + + py::class_(m, "CurlWrapper") + .def(py::init<>()) + .def("set_url", &CurlWrapper::setUrl, "Set the URL for the request", + py::arg("url")) + .def("set_request_method", &CurlWrapper::setRequestMethod, + "Set the HTTP request method", py::arg("method")) + .def("add_header", &CurlWrapper::addHeader, + "Add a header to the request", py::arg("key"), py::arg("value")) + .def("on_error", &CurlWrapper::onError, "Set the error callback", + py::arg("callback")) + .def("on_response", &CurlWrapper::onResponse, + "Set the response callback", py::arg("callback")) + .def("set_timeout", &CurlWrapper::setTimeout, "Set the request timeout", + py::arg("timeout")) + .def("set_follow_location", &CurlWrapper::setFollowLocation, + "Set whether to follow redirects", py::arg("follow")) + .def("set_request_body", &CurlWrapper::setRequestBody, + "Set the request body", py::arg("data")) + .def("set_upload_file", &CurlWrapper::setUploadFile, + "Set the file to upload", py::arg("file_path")) + .def("set_proxy", &CurlWrapper::setProxy, + "Set the proxy for the request", py::arg("proxy")) + .def("set_ssl_options", &CurlWrapper::setSSLOptions, "Set SSL options", + py::arg("verify_peer"), py::arg("verify_host")) + .def("perform", &CurlWrapper::perform, "Perform the HTTP request") + .def("perform_async", &CurlWrapper::performAsync, + "Perform the HTTP request asynchronously") + .def("wait_all", &CurlWrapper::waitAll, + "Wait for all asynchronous requests to complete") + .def("set_max_download_speed", &CurlWrapper::setMaxDownloadSpeed, + "Set the maximum download speed", py::arg("speed")); + + py::class_(m, "DownloadManager") + .def(py::init(), "Constructor", + py::arg("task_file")) + .def("add_task", &DownloadManager::addTask, "Add a download task", + py::arg("url"), py::arg("filepath"), py::arg("priority") = 0) + .def("remove_task", &DownloadManager::removeTask, + "Remove a download task", py::arg("index")) + .def("start", &DownloadManager::start, "Start download tasks", + py::arg("thread_count") = std::thread::hardware_concurrency(), + py::arg("download_speed") = 0) + .def("pause_task", &DownloadManager::pauseTask, "Pause a download task", + py::arg("index")) + .def("resume_task", &DownloadManager::resumeTask, + "Resume a paused download task", py::arg("index")) + .def("get_downloaded_bytes", &DownloadManager::getDownloadedBytes, + "Get the number of bytes downloaded for a task", py::arg("index")) + .def("cancel_task", &DownloadManager::cancelTask, + "Cancel a download task", py::arg("index")) + .def("set_thread_count", &DownloadManager::setThreadCount, + "Set the number of download threads", py::arg("thread_count")) + .def("set_max_retries", &DownloadManager::setMaxRetries, + "Set the maximum number of retries for download errors", + py::arg("retries")) + .def("on_download_complete", &DownloadManager::onDownloadComplete, + "Register a callback for when a download completes", + py::arg("callback")) + .def("on_progress_update", &DownloadManager::onProgressUpdate, + "Register a callback for when download progress updates", + py::arg("callback")); + + py::class_(m, "HttpHeaderParser") + .def(py::init<>()) + .def("parse_headers", &HttpHeaderParser::parseHeaders, + "Parse raw HTTP headers", py::arg("raw_headers")) + .def("set_header_value", &HttpHeaderParser::setHeaderValue, + "Set the value of a specific header field", py::arg("key"), + py::arg("value")) + .def("set_headers", &HttpHeaderParser::setHeaders, + "Set multiple header fields at once", py::arg("headers")) + .def("add_header_value", &HttpHeaderParser::addHeaderValue, + "Add a new value to an existing header field", py::arg("key"), + py::arg("value")) + .def("get_header_values", &HttpHeaderParser::getHeaderValues, + "Retrieve the values of a specific header field", py::arg("key")) + .def("remove_header", &HttpHeaderParser::removeHeader, + "Remove a specific header field", py::arg("key")) + .def("get_all_headers", &HttpHeaderParser::getAllHeaders, + "Retrieve all the parsed headers") + .def("has_header", &HttpHeaderParser::hasHeader, + "Check if a specific header field exists", py::arg("key")) + .def("clear_headers", &HttpHeaderParser::clearHeaders, + "Clear all the parsed headers"); + + py::class_(m, "TimeManager") + .def(py::init<>()) + .def("get_system_time", &TimeManager::getSystemTime, + "Get the current system time") + .def("set_system_time", &TimeManager::setSystemTime, + "Set the system time", py::arg("year"), py::arg("month"), + py::arg("day"), py::arg("hour"), py::arg("minute"), + py::arg("second")) + .def("set_system_timezone", &TimeManager::setSystemTimezone, + "Set the system timezone", py::arg("timezone")) + .def("sync_time_from_rtc", &TimeManager::syncTimeFromRTC, + "Synchronize the system time from the Real-Time Clock (RTC)") + .def("get_ntp_time", &TimeManager::getNtpTime, + "Get the Network Time Protocol (NTP) time from a specified " + "hostname", + py::arg("hostname")); + + m.def("is_port_in_use", &isPortInUse, "Check if a port is in use", + py::arg("port")); + m.def("check_and_kill_program_on_port", &checkAndKillProgramOnPort, + "Check if there is any program running on the specified port and " + "kill it if found", + py::arg("port")); + +#if defined(__linux__) || defined(__APPLE__) + m.def("dump_addr_info", &dumpAddrInfo, + "Dump address information from source to destination", py::arg("dst"), + py::arg("src")); + m.def("addr_info_to_string", &addrInfoToString, + "Convert address information to string", py::arg("addr_info"), + py::arg("json_format") = false); + m.def("get_addr_info", &getAddrInfo, + "Get address information for a given hostname and service", + py::arg("hostname"), py::arg("service")); + m.def("free_addr_info", &freeAddrInfo, "Free address information", + py::arg("addr_info")); + m.def("compare_addr_info", &compareAddrInfo, + "Compare two address information structures", py::arg("addr_info1"), + py::arg("addr_info2")); + m.def("filter_addr_info", &filterAddrInfo, + "Filter address information by family", py::arg("addr_info"), + py::arg("family")); + m.def("sort_addr_info", &sortAddrInfo, "Sort address information by family", + py::arg("addr_info")); +#endif +} \ No newline at end of file diff --git a/modules/lithium.config/pymodule.cpp b/modules/lithium.config/pymodule.cpp index ba052e36..9998f1d4 100644 --- a/modules/lithium.config/pymodule.cpp +++ b/modules/lithium.config/pymodule.cpp @@ -1,26 +1,56 @@ +#include #include #include +#include #include "config/configor.hpp" namespace py = pybind11; +using namespace lithium; -static auto mConfigManager = lithium::ConfigManager::createShared(); - -PYBIND11_MODULE(lithium_config, m) { - py::class_>( - m, "ConfigManager") - .def("getConfig", &lithium::ConfigManager::getValue) - .def("setConfig", &lithium::ConfigManager::setValue) - .def("hasConfig", &lithium::ConfigManager::hasValue) - .def("deleteConfig", &lithium::ConfigManager::deleteValue) - .def("loadConfig", &lithium::ConfigManager::loadFromFile) - .def("loadConfigs", &lithium::ConfigManager::loadFromDir) - .def("saveConfig", &lithium::ConfigManager::saveToFile) - .def("tidyConfig", &lithium::ConfigManager::tidyConfig) - .def("clearConfig", &lithium::ConfigManager::clearConfig) - .def("asyncLoadConfig", &lithium::ConfigManager::asyncLoadFromFile) - .def("asyncSaveConfig", &lithium::ConfigManager::asyncSaveToFile); - - m.attr("config_instance") = mConfigManager; -} +PYBIND11_MODULE(configor, m) { + py::class_>(m, + "ConfigManager") + .def(py::init<>()) + .def_static("create_shared", &ConfigManager::createShared, + "Creates a shared pointer instance of ConfigManager.") + .def_static("create_unique", &ConfigManager::createUnique, + "Creates a unique pointer instance of ConfigManager.") + .def("get_value", &ConfigManager::getValue, py::arg("key_path"), + "Retrieves the value associated with the given key path.") + .def("set_value", &ConfigManager::setValue, py::arg("key_path"), + py::arg("value"), "Sets the value for the specified key path.") + .def("append_value", &ConfigManager::appendValue, py::arg("key_path"), + py::arg("value"), + "Appends a value to an array at the specified key path.") + .def("delete_value", &ConfigManager::deleteValue, py::arg("key_path"), + "Deletes the value associated with the given key path.") + .def("has_value", &ConfigManager::hasValue, py::arg("key_path"), + "Checks if a value exists for the given key path.") + .def("get_keys", &ConfigManager::getKeys, + "Retrieves all keys in the configuration.") + .def("list_paths", &ConfigManager::listPaths, + "Lists all configuration files in specified directory.") + .def("load_from_file", &ConfigManager::loadFromFile, py::arg("path"), + "Loads configuration data from a file.") + .def("load_from_dir", &ConfigManager::loadFromDir, py::arg("dir_path"), + py::arg("recursive") = false, + "Loads configuration data from a directory.") + .def("save_to_file", &ConfigManager::saveToFile, py::arg("file_path"), + "Saves the current configuration to a file.") + .def("tidy_config", &ConfigManager::tidyConfig, + "Cleans up the configuration by removing unused entries or " + "optimizing data.") + .def("clear_config", &ConfigManager::clearConfig, + "Clears all configuration data.") + .def("merge_config", + py::overload_cast(&ConfigManager::mergeConfig), + py::arg("src"), + "Merges the current configuration with the provided JSON data.") + .def("async_load_from_file", &ConfigManager::asyncLoadFromFile, + py::arg("path"), py::arg("callback"), + "Asynchronously loads configuration data from a file.") + .def("async_save_to_file", &ConfigManager::asyncSaveToFile, + py::arg("file_path"), py::arg("callback"), + "Asynchronously saves the current configuration to a file."); +} \ No newline at end of file diff --git a/modules/lithium.tools/pymodule.cpp b/modules/lithium.tools/pymodule.cpp new file mode 100644 index 00000000..76703c04 --- /dev/null +++ b/modules/lithium.tools/pymodule.cpp @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include + +#include "tools/croods.hpp" +#include "tools/libastro.hpp" + +namespace py = pybind11; +using namespace lithium::tools; + +PYBIND11_MODULE(croods, m) { + m.doc() = "Croods Module"; + + py::class_(m, "CartesianCoordinates") + .def(py::init<>()) + .def_readwrite("x", &CartesianCoordinates::x) + .def_readwrite("y", &CartesianCoordinates::y) + .def_readwrite("z", &CartesianCoordinates::z); + + py::class_(m, "SphericalCoordinates") + .def(py::init<>()) + .def_readwrite("rightAscension", &SphericalCoordinates::rightAscension) + .def_readwrite("declination", &SphericalCoordinates::declination); + + py::class_(m, "MinMaxFOV") + .def(py::init<>()) + .def_readwrite("minFOV", &MinMaxFOV::minFOV) + .def_readwrite("maxFOV", &MinMaxFOV::maxFOV); + + py::class_(m, "DateTime") + .def(py::init<>()) + .def_readwrite("year", &DateTime::year) + .def_readwrite("month", &DateTime::month) + .def_readwrite("day", &DateTime::day) + .def_readwrite("hour", &DateTime::hour) + .def_readwrite("minute", &DateTime::minute) + .def_readwrite("second", &DateTime::second); + + py::class_>(m, "CelestialCoords") + .def(py::init<>()) + .def_readwrite("ra", &CelestialCoords::ra) + .def_readwrite("dec", &CelestialCoords::dec); + + py::class_>(m, "GeographicCoords") + .def(py::init<>()) + .def_readwrite("latitude", &GeographicCoords::latitude) + .def_readwrite("longitude", &GeographicCoords::longitude); + + m.def("range_to", &rangeTo, py::arg("value"), py::arg("max"), + py::arg("min"), "Clamps a value to a specified range."); + m.def("degree_to_rad", °reeToRad, py::arg("degree"), + "Converts degrees to radians."); + m.def("rad_to_degree", &radToDegree, py::arg("rad"), + "Converts radians to degrees."); + m.def("hour_to_degree", &hourToDegree, py::arg("hour"), + "Converts hours to degrees."); + m.def("hour_to_rad", &hourToRad, py::arg("hour"), + "Converts hours to radians."); + m.def("degree_to_hour", °reeToHour, py::arg("degree"), + "Converts degrees to hours."); + m.def("rad_to_hour", &radToHour, py::arg("rad"), + "Converts radians to hours."); + m.def("get_ha_degree", &getHaDegree, py::arg("RA_radian"), + py::arg("LST_Degree"), "Calculates the hour angle in degrees."); + m.def("ra_dec_to_alt_az", + py::overload_cast( + &raDecToAltAz), + py::arg("ha_radian"), py::arg("dec_radian"), py::arg("alt_radian"), + py::arg("az_radian"), py::arg("lat_radian"), + "Converts RA/Dec to Alt/Az."); + m.def("ra_dec_to_alt_az", + py::overload_cast(&raDecToAltAz), + py::arg("ha_radian"), py::arg("dec_radian"), py::arg("lat_radian"), + "Converts RA/Dec to Alt/Az and returns a vector."); + m.def("period_belongs", &periodBelongs, py::arg("value"), py::arg("min"), + py::arg("max"), py::arg("period"), py::arg("minequ"), + py::arg("maxequ"), + "Checks if a value belongs to a specified period."); + m.def("convert_equatorial_to_cartesian", &convertEquatorialToCartesian, + py::arg("ra"), py::arg("dec"), py::arg("radius"), + "Converts equatorial coordinates to Cartesian coordinates."); + m.def("calculate_vector", &calculateVector, py::arg("pointA"), + py::arg("pointB"), + "Calculates the vector between two Cartesian points."); + m.def("calculate_point_c", &calculatePointC, py::arg("pointA"), + py::arg("vectorV"), + "Calculates a new Cartesian point based on a vector."); + m.def("convert_to_spherical_coordinates", &convertToSphericalCoordinates, + py::arg("cartesianPoint"), + "Converts Cartesian coordinates to spherical coordinates."); + m.def("calculate_fov", &calculateFOV, py::arg("focalLength"), + py::arg("cameraSizeWidth"), py::arg("cameraSizeHeight"), + "Calculates the field of view based on camera parameters."); + m.def("lumen", &lumen, py::arg("wavelength"), + "Calculates the luminous efficacy for a given wavelength."); + m.def("redshift", &redshift, py::arg("observed"), py::arg("rest"), + "Calculates the redshift."); + m.def("doppler", &doppler, py::arg("redshift"), py::arg("speed"), + "Calculates the Doppler shift."); + m.def("range_ha", &rangeHA, py::arg("range"), + "Clamps a value to the range of hour angles."); + m.def("range_24", &range24, py::arg("range"), + "Clamps a value to the range of 24 hours."); + m.def("range_360", &range360, py::arg("range"), + "Clamps a value to the range of 360 degrees."); + m.def("range_dec", &rangeDec, py::arg("decDegrees"), + "Clamps a declination value to the valid range."); + m.def("get_local_hour_angle", &getLocalHourAngle, + py::arg("siderealTime"), py::arg("rightAscension"), + "Calculates the local hour angle."); + m.def("get_alt_az_coordinates", &getAltAzCoordinates, + py::arg("hourAngle"), py::arg("declination"), py::arg("latitude"), + "Calculates the altitude and azimuth coordinates."); + m.def("estimate_geocentric_elevation", &estimateGeocentricElevation, + py::arg("latitude"), py::arg("elevation"), + "Estimates the geocentric elevation."); + m.def("estimate_field_rotation_rate", &estimateFieldRotationRate, + py::arg("altitude"), py::arg("azimuth"), py::arg("latitude"), + "Estimates the field rotation rate."); + m.def("estimate_field_rotation", &estimateFieldRotation, + py::arg("hourAngle"), py::arg("rate"), + "Estimates the field rotation."); + m.def("as2rad", &as2rad, py::arg("arcSeconds"), + "Converts arcseconds to radians."); + m.def("rad2as", &rad2as, py::arg("radians"), + "Converts radians to arcseconds."); + m.def("estimate_distance", &estimateDistance, py::arg("parsecs"), + py::arg("parallaxRadius"), + "Estimates the distance based on parallax."); + m.def("m2au", &m2au, py::arg("meters"), + "Converts meters to astronomical units."); + m.def("calc_delta_magnitude", &calcDeltaMagnitude, + py::arg("magnitudeRatio"), py::arg("spectrum"), + "Calculates the delta magnitude."); + m.def("calc_star_mass", &calcStarMass, py::arg("deltaMagnitude"), + py::arg("referenceSize"), "Calculates the mass of a star."); + m.def("estimate_orbit_radius", &estimateOrbitRadius, + py::arg("observedWavelength"), py::arg("referenceWavelength"), + py::arg("period"), "Estimates the orbit radius."); + m.def("estimate_secondary_mass", &estimateSecondaryMass, + py::arg("starMass"), py::arg("starDrift"), py::arg("orbitRadius"), + "Estimates the mass of a secondary object."); + m.def("estimate_secondary_size", &estimateSecondarySize, + py::arg("starSize"), py::arg("dropoffRatio"), + "Estimates the size of a secondary object."); + m.def("calc_photon_flux", &calcPhotonFlux, + py::arg("relativeMagnitude"), py::arg("filterBandwidth"), + py::arg("wavelength"), py::arg("steradian"), + "Calculates the photon flux."); + m.def("calc_rel_magnitude", &calcRelMagnitude, + py::arg("photonFlux"), py::arg("filterBandwidth"), + py::arg("wavelength"), py::arg("steradian"), + "Calculates the relative magnitude."); + m.def("estimate_absolute_magnitude", &estimateAbsoluteMagnitude, + py::arg("deltaDistance"), py::arg("deltaMagnitude"), + "Estimates the absolute magnitude."); + m.def("baseline_2d_projection", &baseline2dProjection, + py::arg("altitude"), py::arg("azimuth"), + "Calculates the 2D projection of a baseline."); + m.def("baseline_delay", &baselineDelay, py::arg("altitude"), + py::arg("azimuth"), py::arg("baseline"), + "Calculates the baseline delay."); + m.def("calculate_julian_date", &calculateJulianDate, + py::arg("dateTime"), "Calculates the Julian date."); + m.def("calculate_sidereal_time", &calculateSiderealTime, + py::arg("dateTime"), py::arg("longitude"), + "Calculates the sidereal time."); + m.def("calculate_refraction", &calculateRefraction, + py::arg("altitude"), py::arg("temperature") = 10.0, + py::arg("pressure") = 1010.0, "Calculates atmospheric refraction."); + m.def("apply_parallax", &applyParallax, py::arg("coords"), + py::arg("observer"), py::arg("distance"), py::arg("dt"), + "Applies parallax correction to celestial coordinates."); + m.def("equatorial_to_ecliptic", &equatorialToEcliptic, + py::arg("coords"), py::arg("obliquity"), + "Converts equatorial coordinates to ecliptic coordinates."); + m.def("calculate_precession", &calculatePrecession, + py::arg("coords"), py::arg("from"), py::arg("to"), + "Calculates the precession of celestial coordinates."); + m.def("format_ra", &formatRa, py::arg("ra"), + "Formats right ascension as a string."); + m.def("format_dec", &formatDec, py::arg("dec"), + "Formats declination as a string."); + + py::class_(m, "EquatorialCoordinates") + .def(py::init<>()) + .def_readwrite("rightAscension", &EquatorialCoordinates::rightAscension) + .def_readwrite("declination", &EquatorialCoordinates::declination); + + py::class_(m, "HorizontalCoordinates") + .def(py::init<>()) + .def_readwrite("azimuth", &HorizontalCoordinates::azimuth) + .def_readwrite("altitude", &HorizontalCoordinates::altitude); + + py::class_(m, "GeographicCoordinates") + .def(py::init<>()) + .def_readwrite("longitude", &GeographicCoordinates::longitude) + .def_readwrite("latitude", &GeographicCoordinates::latitude) + .def_readwrite("elevation", &GeographicCoordinates::elevation); + + m.def("deg_to_rad", °ToRad, py::arg("deg"), + "Converts degrees to radians."); + m.def("rad_to_deg", &radToDeg, py::arg("rad"), + "Converts radians to degrees."); + m.def("range_360", &range360, py::arg("angle"), + "Clamps an angle to the range 0 to 360 degrees."); + + m.def("observed_to_j2000", &observedToJ2000, py::arg("observed"), + py::arg("julianDate"), + "Converts observed equatorial coordinates to J2000 coordinates."); + m.def("j2000_to_observed", &j2000ToObserved, py::arg("j2000"), + py::arg("julianDate"), + "Converts J2000 equatorial coordinates to observed coordinates."); + m.def("equatorial_to_horizontal", &equatorialToHorizontal, + py::arg("object"), py::arg("observer"), py::arg("julianDate"), + "Converts equatorial coordinates to horizontal coordinates."); + m.def("horizontal_to_equatorial", &horizontalToEquatorial, + py::arg("object"), py::arg("observer"), py::arg("julianDate"), + "Converts horizontal coordinates to equatorial coordinates."); + + m.def("get_nutation", &getNutation, py::arg("julianDate"), + "Calculates the nutation for a given Julian date."); + m.def("apply_nutation", &applyNutation, py::arg("position"), + py::arg("julianDate"), py::arg("reverse") = false, + "Applies nutation to equatorial coordinates."); + m.def("apply_aberration", &applyAberration, py::arg("position"), + py::arg("julianDate"), + "Applies aberration to equatorial coordinates."); + m.def("apply_precession", &applyPrecession, py::arg("position"), + py::arg("fromJulianDate"), py::arg("toJulianDate"), + "Applies precession to equatorial coordinates."); +} \ No newline at end of file diff --git a/pysrc/tools/hotspot.py b/pysrc/tools/hotspot.py new file mode 100644 index 00000000..f1d56147 --- /dev/null +++ b/pysrc/tools/hotspot.py @@ -0,0 +1,111 @@ +import subprocess +import argparse + + +class HotspotManager: + def __init__(self): + pass + + def start(self, name='MyHotspot', password=None, authentication='wpa-psk', encryption='aes', channel=11, max_clients=10): + if password is None: + raise ValueError("Password is required when starting a hotspot") + cmd = [ + 'nmcli', 'dev', 'wifi', 'hotspot', 'ifname', 'wlan0', 'ssid', name, 'password', password + ] + self._run_command(cmd) + self._run_command(['nmcli', 'connection', 'modify', + 'Hotspot', '802-11-wireless.security', authentication]) + self._run_command(['nmcli', 'connection', 'modify', + 'Hotspot', '802-11-wireless.band', 'bg']) + self._run_command(['nmcli', 'connection', 'modify', + 'Hotspot', '802-11-wireless.channel', str(channel)]) + self._run_command(['nmcli', 'connection', 'modify', 'Hotspot', + '802-11-wireless.cloned-mac-address', 'stable']) + self._run_command(['nmcli', 'connection', 'modify', 'Hotspot', + '802-11-wireless.mac-address-randomization', 'no']) + print(f"Hotspot {name} is now running with password {password}") + + def stop(self): + self._run_command(['nmcli', 'connection', 'down', 'Hotspot']) + print("Hotspot has been stopped") + + def status(self): + status = self._run_command(['nmcli', 'dev', 'status']) + if 'connected' in status: + print("Hotspot is running") + self._run_command(['nmcli', 'connection', 'show', 'Hotspot']) + else: + print("Hotspot is not running") + + def list(self): + self._run_command(['nmcli', 'connection', 'show', '--active']) + + def set(self, name='MyHotspot', password=None, authentication='wpa-psk', encryption='aes', channel=11, max_clients=10): + if password is None: + raise ValueError( + "Password is required when setting a hotspot profile") + self._run_command(['nmcli', 'connection', 'modify', + 'Hotspot', '802-11-wireless.ssid', name]) + self._run_command(['nmcli', 'connection', 'modify', 'Hotspot', + '802-11-wireless-security.key-mgmt', authentication]) + self._run_command(['nmcli', 'connection', 'modify', + 'Hotspot', '802-11-wireless-security.proto', 'rsn']) + self._run_command(['nmcli', 'connection', 'modify', 'Hotspot', + '802-11-wireless-security.group', encryption]) + self._run_command(['nmcli', 'connection', 'modify', 'Hotspot', + '802-11-wireless-security.pairwise', encryption]) + self._run_command(['nmcli', 'connection', 'modify', + 'Hotspot', '802-11-wireless-security.psk', password]) + self._run_command(['nmcli', 'connection', 'modify', + 'Hotspot', '802-11-wireless.band', 'bg']) + self._run_command(['nmcli', 'connection', 'modify', + 'Hotspot', '802-11-wireless.channel', str(channel)]) + self._run_command(['nmcli', 'connection', 'modify', 'Hotspot', + '802-11-wireless.mac-address-randomization', 'no']) + print(f"Hotspot profile '{name}' has been updated") + + def _run_command(self, cmd): + try: + result = subprocess.run( + cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + return result.stdout + except subprocess.CalledProcessError as e: + print(e.stderr) + return e.stderr + + +def main(): + parser = argparse.ArgumentParser(description='Manage WiFi Hotspot') + parser.add_argument('action', choices=[ + 'Start', 'Stop', 'Status', 'List', 'Set'], help='Action to perform') + parser.add_argument('--name', default='MyHotspot', help='Hotspot name') + parser.add_argument('--password', help='Hotspot password') + parser.add_argument('--authentication', default='wpa-psk', + choices=['wpa-psk', 'wpa2'], help='Authentication type') + parser.add_argument('--encryption', default='aes', + choices=['aes', 'tkip'], help='Encryption type') + parser.add_argument('--channel', type=int, + default=11, help='Channel number') + parser.add_argument('--max_clients', type=int, default=10, + help='Maximum number of clients') + + args = parser.parse_args() + + manager = HotspotManager() + + if args.action == 'Start': + manager.start(args.name, args.password, args.authentication, + args.encryption, args.channel, args.max_clients) + elif args.action == 'Stop': + manager.stop() + elif args.action == 'Status': + manager.status() + elif args.action == 'List': + manager.list() + elif args.action == 'Set': + manager.set(args.name, args.password, args.authentication, + args.encryption, args.channel, args.max_clients) + + +if __name__ == "__main__": + main() diff --git a/pysrc/tools/nginx.py b/pysrc/tools/nginx.py new file mode 100644 index 00000000..0b7d3032 --- /dev/null +++ b/pysrc/tools/nginx.py @@ -0,0 +1,169 @@ +import subprocess +import os +import sys +import platform +import shutil + +# Define Nginx paths +NGINX_PATH = "/etc/nginx" if platform.system() != "Windows" else "C:\\nginx" +NGINX_CONF = f"{NGINX_PATH}/nginx.conf" if platform.system( +) != "Windows" else f"{NGINX_PATH}\\conf\\nginx.conf" +NGINX_BINARY = "/usr/sbin/nginx" if platform.system( +) != "Windows" else f"{NGINX_PATH}\\nginx.exe" +BACKUP_PATH = f"{NGINX_PATH}/backup" if platform.system( +) != "Windows" else f"{NGINX_PATH}\\backup" + +# Define output colors +GREEN = '\033[0;32m' if platform.system() != "Windows" else "" +RED = '\033[0;31m' if platform.system() != "Windows" else "" +NC = '\033[0m' if platform.system() != "Windows" else "" + + +def install_nginx(): + """Install Nginx if not already installed""" + if platform.system() == "Linux": + result = subprocess.run("nginx -v", shell=True, + stderr=subprocess.PIPE, check=True) + if result.returncode != 0: + print("Installing Nginx...") + if os.path.isfile("/etc/debian_version"): + subprocess.run( + "sudo apt-get update && sudo apt-get install nginx -y", shell=True, check=True) + elif os.path.isfile("/etc/redhat-release"): + subprocess.run( + "sudo yum update && sudo yum install nginx -y", shell=True, check=True) + else: + print( + f"{RED}Unsupported platform. Please install Nginx manually.{NC}") + sys.exit(1) + + +def start_nginx(): + """Start Nginx""" + if os.path.isfile(NGINX_BINARY): + subprocess.run([NGINX_BINARY], check=True) + print(f"{GREEN}Nginx has been started{NC}") + else: + print(f"{RED}Nginx binary not found{NC}") + + +def stop_nginx(): + """Stop Nginx""" + if os.path.isfile(NGINX_BINARY): + subprocess.run([NGINX_BINARY, '-s', 'stop'], check=True) + print(f"{GREEN}Nginx has been stopped{NC}") + else: + print(f"{RED}Nginx binary not found{NC}") + + +def reload_nginx(): + """Reload Nginx configuration""" + if os.path.isfile(NGINX_BINARY): + subprocess.run([NGINX_BINARY, '-s', 'reload'], check=True) + print(f"{GREEN}Nginx configuration has been reloaded{NC}") + else: + print(f"{RED}Nginx binary not found{NC}") + + +def restart_nginx(): + """Restart Nginx""" + stop_nginx() + start_nginx() + + +def check_config(): + """Check Nginx configuration syntax""" + if os.path.isfile(NGINX_CONF): + result = subprocess.run( + [NGINX_BINARY, '-t', '-c', NGINX_CONF], check=True) + if result.returncode == 0: + print(f"{GREEN}Nginx configuration syntax is correct{NC}") + else: + print(f"{RED}Nginx configuration syntax is incorrect{NC}") + else: + print(f"{RED}Nginx configuration file not found{NC}") + + +def show_status(): + """Show Nginx status""" + if subprocess.run("pgrep nginx", shell=True, stdout=subprocess.PIPE, check=True).stdout: + print(f"{GREEN}Nginx is running{NC}") + else: + print(f"{RED}Nginx is not running{NC}") + + +def show_version(): + """Show Nginx version""" + result = subprocess.run([NGINX_BINARY, '-v'], + stderr=subprocess.PIPE, check=True) + print(result.stderr.decode()) + + +def backup_config(): + """Backup Nginx configuration file""" + if not os.path.exists(BACKUP_PATH): + os.makedirs(BACKUP_PATH) + backup_file = os.path.join(BACKUP_PATH, "nginx.conf.bak") + shutil.copy(NGINX_CONF, backup_file) + print(f"{GREEN}Nginx configuration file has been backed up to {backup_file}{NC}") + + +def restore_config(): + """Restore Nginx configuration file""" + backup_file = os.path.join(BACKUP_PATH, "nginx.conf.bak") + if os.path.isfile(backup_file): + shutil.copy(backup_file, NGINX_CONF) + print(f"{GREEN}Nginx configuration file has been restored from backup{NC}") + else: + print(f"{RED}Backup file not found{NC}") + + +def show_help(): + """Show help message""" + print( + "Usage: python nginx_manager.py [start|stop|reload|restart|check|status|version|backup|restore|help]") + print(" start Start Nginx") + print(" stop Stop Nginx") + print(" reload Reload Nginx configuration") + print(" restart Restart Nginx") + print(" check Check Nginx configuration syntax") + print(" status Show Nginx status") + print(" version Show Nginx version") + print(" backup Backup Nginx configuration file") + print(" restore Restore Nginx configuration file") + print(" help Show help message") + + +def main(): + if len(sys.argv) < 2: + show_help() + sys.exit(1) + + command = sys.argv[1] + + # Check if Nginx is installed + install_nginx() + + commands = { + "start": start_nginx, + "stop": stop_nginx, + "reload": reload_nginx, + "restart": restart_nginx, + "check": check_config, + "status": show_status, + "version": show_version, + "backup": backup_config, + "restore": restore_config, + "help": show_help + } + + if command in commands: + commands[command]() + else: + print(f"{RED}Invalid command{NC}") + show_help() + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/src/atom/algorithm/sha1.cpp b/src/atom/algorithm/sha1.cpp new file mode 100644 index 00000000..380dd02c --- /dev/null +++ b/src/atom/algorithm/sha1.cpp @@ -0,0 +1,138 @@ +#include "sha1.hpp" + +#include +#include +#include + +namespace atom::algorithm { +SHA1::SHA1() { reset(); } + +void SHA1::update(const uint8_t* data, size_t length) { + size_t remaining = length; + size_t offset = 0; + + while (remaining > 0) { + size_t bufferOffset = (bitCount_ / 8) % BLOCK_SIZE; + + size_t bytesToFill = BLOCK_SIZE - bufferOffset; + size_t bytesToCopy = std::min(remaining, bytesToFill); + + std::copy(data + offset, data + offset + bytesToCopy, + buffer_.data() + bufferOffset); + offset += bytesToCopy; + remaining -= bytesToCopy; + bitCount_ += bytesToCopy * BITS_PER_BYTE; + + if (bufferOffset + bytesToCopy == BLOCK_SIZE) { + processBlock(buffer_.data()); + } + } +} + +std::array SHA1::digest() { + uint64_t bitLength = bitCount_; + + // Padding + size_t bufferOffset = (bitCount_ / 8) % BLOCK_SIZE; + buffer_[bufferOffset] = PADDING_BYTE; // Append the bit '1' + + if (bufferOffset >= BLOCK_SIZE - LENGTH_SIZE) { + // Not enough space for the length, process the block + processBlock(buffer_.data()); + std::fill(buffer_.begin(), buffer_.end(), 0); + } + + // Append the length of the message + for (size_t i = 0; i < LENGTH_SIZE; ++i) { + buffer_[BLOCK_SIZE - LENGTH_SIZE + i] = + (bitLength >> (LENGTH_SIZE * BITS_PER_BYTE - i * BITS_PER_BYTE)) & + BYTE_MASK; + } + processBlock(buffer_.data()); + + // Produce the final hash value + std::array result; + for (size_t i = 0; i < HASH_SIZE; ++i) { + result[i * 4] = (hash_[i] >> 24) & BYTE_MASK; + result[i * 4 + 1] = (hash_[i] >> 16) & BYTE_MASK; + result[i * 4 + 2] = (hash_[i] >> 8) & BYTE_MASK; + result[i * 4 + 3] = hash_[i] & BYTE_MASK; + } + + return result; +} + +void SHA1::reset() { + bitCount_ = 0; + hash_.fill(0); + hash_[0] = 0x67452301; + hash_[1] = 0xEFCDAB89; + hash_[2] = 0x98BADCFE; + hash_[3] = 0x10325476; + hash_[4] = 0xC3D2E1F0; + buffer_.fill(0); +} + +void SHA1::processBlock(const uint8_t* block) { + std::array schedule{}; + for (size_t i = 0; i < 16; ++i) { + schedule[i] = (block[i * 4] << 24) | (block[i * 4 + 1] << 16) | + (block[i * 4 + 2] << 8) | block[i * 4 + 3]; + } + + for (size_t i = 16; i < SCHEDULE_SIZE; ++i) { + schedule[i] = rotateLeft(schedule[i - 3] ^ schedule[i - 8] ^ + schedule[i - 14] ^ schedule[i - 16], + 1); + } + + uint32_t a = hash_[0]; + uint32_t b = hash_[1]; + uint32_t c = hash_[2]; + uint32_t d = hash_[3]; + uint32_t e = hash_[4]; + + for (size_t i = 0; i < SCHEDULE_SIZE; ++i) { + uint32_t f; + uint32_t k; + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5A827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + + uint32_t temp = rotateLeft(a, 5) + f + e + k + schedule[i]; + e = d; + d = c; + c = rotateLeft(b, 30); + b = a; + a = temp; + } + + hash_[0] += a; + hash_[1] += b; + hash_[2] += c; + hash_[3] += d; + hash_[4] += e; +} + +auto SHA1::rotateLeft(uint32_t value, size_t bits) -> uint32_t { + return (value << bits) | (value >> (WORD_SIZE - bits)); +} + +auto bytesToHex(const std::array& bytes) -> std::string { + std::ostringstream oss; + for (uint8_t byte : bytes) { + oss << std::setw(2) << std::setfill('0') << std::hex << (int)byte; + } + return oss.str(); +} +} // namespace atom::algorithm diff --git a/src/atom/algorithm/sha1.hpp b/src/atom/algorithm/sha1.hpp new file mode 100644 index 00000000..19f52b0c --- /dev/null +++ b/src/atom/algorithm/sha1.hpp @@ -0,0 +1,41 @@ +#ifndef ATOM_ALGORITHM_SHA1_HPP +#define ATOM_ALGORITHM_SHA1_HPP + +#include +#include +#include + +namespace atom::algorithm { +class SHA1 { +public: + SHA1(); + + void update(const uint8_t* data, size_t length); + auto digest() -> std::array; + void reset(); + + static constexpr size_t DIGEST_SIZE = 20; + +private: + void processBlock(const uint8_t* block); + static auto rotateLeft(uint32_t value, size_t bits) -> uint32_t; + + static constexpr size_t BLOCK_SIZE = 64; + static constexpr size_t HASH_SIZE = 5; + static constexpr size_t SCHEDULE_SIZE = 80; + static constexpr size_t LENGTH_SIZE = 8; + static constexpr size_t BITS_PER_BYTE = 8; + static constexpr uint8_t PADDING_BYTE = 0x80; + static constexpr uint8_t BYTE_MASK = 0xFF; + static constexpr size_t WORD_SIZE = 32; + + std::array hash_; + std::array buffer_; + uint64_t bitCount_; +}; + +auto bytesToHex(const std::array& bytes) -> std::string; + +} // namespace atom::algorithm + +#endif // ATOM_ALGORITHM_SHA1_HPP \ No newline at end of file diff --git a/src/atom/connection/async_fifoclient.cpp b/src/atom/connection/async_fifoclient.cpp index 421851d1..06c9c6a8 100644 --- a/src/atom/connection/async_fifoclient.cpp +++ b/src/atom/connection/async_fifoclient.cpp @@ -14,7 +14,7 @@ #include #endif -namespace atom::connection { +namespace atom::async::connection { struct FifoClient::Impl { asio::io_context io_context; diff --git a/src/atom/connection/async_fifoclient.hpp b/src/atom/connection/async_fifoclient.hpp index ff8c39f8..1030b92f 100644 --- a/src/atom/connection/async_fifoclient.hpp +++ b/src/atom/connection/async_fifoclient.hpp @@ -7,7 +7,7 @@ #include #include -namespace atom::connection { +namespace atom::async::connection { /** * @brief A class for interacting with a FIFO (First In, First Out) pipe. diff --git a/src/atom/connection/async_udpserver.cpp b/src/atom/connection/async_udpserver.cpp index 43b443d6..0d7b8d37 100644 --- a/src/atom/connection/async_udpserver.cpp +++ b/src/atom/connection/async_udpserver.cpp @@ -20,7 +20,7 @@ Description: A simple Asio-based UDP server. #include -namespace atom::connection { +namespace atom::async::connection { constexpr std::size_t BUFFER_SIZE = 1024; diff --git a/src/atom/connection/async_udpserver.hpp b/src/atom/connection/async_udpserver.hpp index f348b704..a3e720cd 100644 --- a/src/atom/connection/async_udpserver.hpp +++ b/src/atom/connection/async_udpserver.hpp @@ -19,8 +19,7 @@ Description: A simple Asio-based UDP server. #include #include -namespace atom::connection { - +namespace atom::async::connection { /** * @class UdpSocketHub * @brief Represents a hub for managing UDP sockets and message handling using diff --git a/src/atom/connection/fifoclient.hpp b/src/atom/connection/fifoclient.hpp index 27cec8fa..bfef84b6 100644 --- a/src/atom/connection/fifoclient.hpp +++ b/src/atom/connection/fifoclient.hpp @@ -22,12 +22,6 @@ Description: FIFO Client #include namespace atom::connection { - -#include -#include -#include -#include - /** * @brief A class for interacting with a FIFO (First In, First Out) pipe. * diff --git a/src/atom/extra/boost/charconv.hpp b/src/atom/extra/boost/charconv.hpp index 4191a516..db5d294e 100644 --- a/src/atom/extra/boost/charconv.hpp +++ b/src/atom/extra/boost/charconv.hpp @@ -1,6 +1,7 @@ #ifndef ATOM_EXTRA_BOOST_CHARCONV_HPP #define ATOM_EXTRA_BOOST_CHARCONV_HPP +#if __has_include() #include #include #include @@ -274,4 +275,6 @@ class BoostCharConv { } // namespace atom::extra::boost +#endif + #endif // ATOM_EXTRA_BOOST_CHARCONV_HPP diff --git a/src/atom/extra/boost/system.hpp b/src/atom/extra/boost/system.hpp index 6d30b9f1..35ec7e3d 100644 --- a/src/atom/extra/boost/system.hpp +++ b/src/atom/extra/boost/system.hpp @@ -1,7 +1,9 @@ #ifndef ATOM_EXTRA_BOOST_SYSTEM_HPP #define ATOM_EXTRA_BOOST_SYSTEM_HPP +#if __has_include() #include +#endif #include #include diff --git a/src/atom/io/compress.cpp b/src/atom/io/compress.cpp index bf81e310..08fbb117 100644 --- a/src/atom/io/compress.cpp +++ b/src/atom/io/compress.cpp @@ -241,6 +241,120 @@ auto compressFolder(const char *folder_name) -> bool { return compressFolder(fs::path(folder_name)); } +void compressFileSlice(const std::string &inputFile, size_t sliceSize) { + std::ifstream inFile(inputFile, std::ios::binary); + if (!inFile) { + LOG_F(ERROR, "Failed to open input file."); + return; + } + + std::vector buffer(sliceSize); + size_t bytesRead; + int fileIndex = 0; + + while (inFile) { + // Read a slice of the file + inFile.read(buffer.data(), sliceSize); + bytesRead = inFile.gcount(); + + if (bytesRead > 0) { + // Prepare compressed data + std::vector compressedData(compressBound(bytesRead)); + uLongf compressedSize = compressedData.size(); + + // Compress the data + if (compress(reinterpret_cast(compressedData.data()), + &compressedSize, + reinterpret_cast(buffer.data()), + bytesRead) != Z_OK) { + LOG_F(ERROR, "Compression failed."); + inFile.close(); + return; + } + + // Write the compressed data to a new file + std::string compressedFileName = + "slice_" + std::to_string(fileIndex++) + ".zlib"; + std::ofstream outFile(compressedFileName, std::ios::binary); + if (!outFile) { + LOG_F(ERROR, "Failed to open output file."); + inFile.close(); + return; + } + + // Write the size of the compressed data and the data itself + outFile.write(reinterpret_cast(&compressedSize), + sizeof(compressedSize)); + outFile.write(compressedData.data(), compressedSize); + outFile.close(); + } + } + + inFile.close(); + LOG_F(INFO, "File sliced and compressed successfully."); +} + +void decompressFileSlice(const std::string &sliceFile, size_t sliceSize) { + std::ifstream inFile(sliceFile, std::ios::binary); + if (!inFile) { + LOG_F(ERROR, "Failed to open compressed file: {}", sliceFile); + return; + } + + // Read the compressed size + uLongf compressedSize; + inFile.read(reinterpret_cast(&compressedSize), + sizeof(compressedSize)); + + // Prepare buffer for compressed data + std::vector compressedData(compressedSize); + inFile.read(compressedData.data(), compressedSize); + inFile.close(); + + // Prepare buffer for decompressed data + std::vector decompressedData( + sliceSize); // Adjust sliceSize for max expected original size + uLongf decompressedSize = sliceSize; + + // Decompress the data + if (uncompress(reinterpret_cast(decompressedData.data()), + &decompressedSize, + reinterpret_cast(compressedData.data()), + compressedSize) != Z_OK) { + LOG_F(ERROR, "Decompression failed for file: {}", sliceFile); + return; + } + + // Write the decompressed data to a new file + std::string decompressedFileName = "decompressed_" + sliceFile; + std::ofstream outFile(decompressedFileName, std::ios::binary); + if (!outFile) { + LOG_F(ERROR, "Failed to open decompressed output file."); + return; + } + + outFile.write(decompressedData.data(), decompressedSize); + outFile.close(); + LOG_F(INFO, "Decompressed file created: {}", decompressedFileName); +} + +void listCompressedFiles() { + for (const auto &entry : std::filesystem::directory_iterator(".")) { + if (entry.path().extension() == ".zlib") { + LOG_F(INFO, "{}", entry.path().filename().string()); + } + } +} + +void deleteCompressedFiles() { + for (const auto &entry : std::filesystem::directory_iterator(".")) { + if (entry.path().extension() == ".zlib") { + std::filesystem::remove(entry.path()); + LOG_F(INFO, "Deleted: {}", entry.path().filename().string()); + } + } +} + auto extractZip(std::string_view zip_file, std::string_view destination_folder) -> bool { LOG_F(INFO, "extractZip called with zip_file: {}, destination_folder: {}", diff --git a/src/atom/type/argsview.hpp b/src/atom/type/argsview.hpp index 27c92fa1..8fdac9e3 100644 --- a/src/atom/type/argsview.hpp +++ b/src/atom/type/argsview.hpp @@ -17,6 +17,7 @@ Description: Argument View for C++20 #include #include +#include #include #include #include @@ -62,6 +63,11 @@ class ArgsView { [](const auto&... args) { return std::tuple(args...); }, other_args_view.args_)) {} + template + + constexpr explicit ArgsView(std::optional... optional_args) + : args_(std::make_tuple(optional_args.value_or(Args{})...)) {} + /** * @brief Get the argument at the specified index. * @@ -110,7 +116,6 @@ class ArgsView { * the transformed arguments. */ template - auto transform(F&& f) const { return ArgsView()))>...>( std::apply( @@ -185,6 +190,60 @@ class ArgsView { return *this; } + /** + * @brief Filter the arguments using a predicate. + * + * @tparam Pred Type of the predicate. + * @param pred The predicate to apply. + * @return ArgsView...> A new ArgsView with the filtered + * arguments. + */ + template + auto filter(Pred&& pred) const { + return std::apply( + [&](const auto&... args) { + return ArgsView{ + (pred(args) ? std::optional{args} : std::nullopt)...}; + }, + args_); + } + + /** + * @brief Find the first argument that satisfies a predicate. + * + * @tparam Pred Type of the predicate. + * @param pred The predicate to apply. + * @return std::optional> The first argument that + * satisfies the predicate, or std::nullopt if none do. + */ + template + auto find(Pred&& pred) const { + return std::apply( + [&](const auto&... args) + -> std::optional> { + return ((pred(args) + ? std::optional>{args} + : std::nullopt) || + ...); + }, + args_); + } + + /** + * @brief Check if the arguments contain a specific value. + * + * @tparam T Type of the value. + * @param value The value to check for. + * @return true If the value is found. + * @return false Otherwise. + */ + template + auto contains(const T& value) const -> bool { + return std::apply( + [&](const auto&... args) { return ((args == value) || ...); }, + args_); + } + private: std::tuple args_; }; @@ -457,4 +516,4 @@ void print(Args&&... args) { } #endif -#endif +#endif \ No newline at end of file diff --git a/src/atom/type/auto_table.hpp b/src/atom/type/auto_table.hpp index 7b7ad6e3..5f76a945 100644 --- a/src/atom/type/auto_table.hpp +++ b/src/atom/type/auto_table.hpp @@ -15,7 +15,7 @@ #include "atom/type/json.hpp" namespace atom::type { - +using json = nlohmann::json; /** * @brief A thread-safe hash table that counts the number of accesses to each * entry. @@ -31,8 +31,8 @@ class CountingHashTable { * @brief Struct representing an entry in the hash table. */ struct Entry { - Value value; ///< The value stored in the entry. std::atomic count{0}; ///< The access count of the entry. + Value value; ///< The value stored in the entry. /** * @brief Default constructor. @@ -46,7 +46,7 @@ class CountingHashTable { */ explicit Entry(Value val) : value(std::move(val)) {} - // 禁用拷贝构造和拷贝赋值 + // Disable copy constructor and copy assignment Entry(const Entry&) = delete; auto operator=(const Entry&) -> Entry& = delete; @@ -54,8 +54,8 @@ class CountingHashTable { * @brief Move constructor. */ Entry(Entry&& other) noexcept - : value(std::move(other.value)), - count(other.count.load(std::memory_order_relaxed)) {} + : count(other.count.load(std::memory_order_relaxed)), + value(std::move(other.value)) {} /** * @brief Move assignment operator. @@ -73,7 +73,8 @@ class CountingHashTable { /** * @brief Constructs a new CountingHashTable object. */ - CountingHashTable(); + CountingHashTable(size_t num_mutexes = 16, + size_t initial_bucket_count = 1024); /** * @brief Destroys the CountingHashTable object. @@ -179,21 +180,23 @@ class CountingHashTable { * * @return A JSON object representing the hash table. */ - nlohmann::json serializeToJson() const; + json serializeToJson() const; /** * @brief Deserializes the hash table from a JSON object. * * @param j The JSON object to deserialize from. */ - void deserializeFromJson(const nlohmann::json& j); + void deserializeFromJson(const json& j); private: - mutable std::shared_mutex mutex_; ///< Mutex for thread-safe access. + mutable std::vector + mutexes_; ///< Vector of mutexes for lock striping. std::unordered_map table_; ///< The underlying hash table. std::atomic stopSorting{ false}; ///< Flag to indicate whether to stop automatic sorting. std::thread sortingThread_; ///< Thread for automatic sorting. + size_t num_mutexes_; ///< Number of mutexes for lock striping. /** * @brief The worker function for automatic sorting. @@ -202,13 +205,25 @@ class CountingHashTable { * @param ascending Whether to sort in ascending order. */ void sortingWorker(std::chrono::milliseconds interval, bool ascending); + + /** + * @brief Gets the mutex index for a given key. + * + * @param key The key to get the mutex index for. + * @return size_t The index of the mutex. + */ + size_t getMutexIndex(const Key& key) const; }; /////////////////////////// Implementation /////////////////////////// template requires std::equality_comparable && std::movable -CountingHashTable::CountingHashTable() {} +CountingHashTable::CountingHashTable(size_t num_mutexes, + size_t initial_bucket_count) + : mutexes_(num_mutexes), num_mutexes_(num_mutexes) { + table_.reserve(initial_bucket_count); +} template requires std::equality_comparable && std::movable @@ -216,16 +231,23 @@ CountingHashTable::~CountingHashTable() { stopAutoSorting(); } +template + requires std::equality_comparable && std::movable +size_t CountingHashTable::getMutexIndex(const Key& key) const { + return std::hash{}(key) % num_mutexes_; +} + template requires std::equality_comparable && std::movable void CountingHashTable::insert(const Key& key, const Value& value) { - std::unique_lock lock(mutex_); + size_t index = getMutexIndex(key); + std::unique_lock lock(mutexes_[index]); auto it = table_.find(key); if (it == table_.end()) { table_.emplace(key, Entry(value)); } else { - it->second.value = - std::move(const_cast(value)); // 假设值可以被移动 + it->second.value = std::move( + const_cast(value)); // Assuming value can be moved } } @@ -233,22 +255,33 @@ template requires std::equality_comparable && std::movable void CountingHashTable::insertBatch( const std::vector>& items) { - std::unique_lock lock(mutex_); + // Group items by mutex to minimize locking overhead + std::unordered_map>> grouped; for (const auto& [key, value] : items) { - auto it = table_.find(key); - if (it == table_.end()) { - table_.emplace(key, Entry(value)); - } else { - it->second.value = - std::move(const_cast(value)); // 假设值可以被移动 + size_t index = getMutexIndex(key); + grouped[index].emplace_back(key, value); + } + + for (auto& [index, group] : grouped) { + std::unique_lock lock(mutexes_[index]); + for (auto& [key, value] : group) { + auto it = table_.find(key); + if (it == table_.end()) { + table_.emplace(key, Entry(value)); + } else { + it->second.value = std::move( + const_cast(value)); // Assuming value can be moved + } } } } template requires std::equality_comparable && std::movable -std::optional CountingHashTable::get(const Key& key) { - std::shared_lock lock(mutex_); +auto CountingHashTable::get(const Key& key) + -> std::optional { + size_t index = getMutexIndex(key); + std::shared_lock lock(mutexes_[index]); auto it = table_.find(key); if (it != table_.end()) { it->second.count.fetch_add(1, std::memory_order_relaxed); @@ -261,7 +294,8 @@ template requires std::equality_comparable && std::movable auto CountingHashTable::getAccessCount(const Key& key) const -> std::optional { - std::shared_lock lock(mutex_); + size_t index = getMutexIndex(key); + std::shared_lock lock(mutexes_[index]); auto it = table_.find(key); if (it != table_.end()) { return it->second.count.load(std::memory_order_relaxed); @@ -273,75 +307,110 @@ template requires std::equality_comparable && std::movable auto CountingHashTable::getBatch(const std::vector& keys) -> std::vector> { - std::shared_lock lock(mutex_); std::vector> results; results.reserve(keys.size()); + + // Group keys by mutex to minimize locking overhead + std::unordered_map> grouped; for (const auto& key : keys) { - auto it = table_.find(key); - if (it != table_.end()) { - it->second.count.fetch_add(1, std::memory_order_relaxed); - results.emplace_back(it->second.value); - } else { - results.emplace_back(std::nullopt); + size_t index = getMutexIndex(key); + grouped[index].emplace_back(&key); + } + + for (auto& [index, group] : grouped) { + std::shared_lock lock(mutexes_[index]); + for (const auto* keyPtr : group) { + auto it = table_.find(*keyPtr); + if (it != table_.end()) { + it->second.count.fetch_add(1, std::memory_order_relaxed); + results.emplace_back(it->second.value); + } else { + results.emplace_back(std::nullopt); + } } } + return results; } template requires std::equality_comparable && std::movable auto CountingHashTable::erase(const Key& key) -> bool { - std::unique_lock lock(mutex_); + size_t index = getMutexIndex(key); + std::unique_lock lock(mutexes_[index]); return table_.erase(key) > 0; } template requires std::equality_comparable && std::movable void CountingHashTable::clear() { - std::unique_lock lock(mutex_); + for (size_t i = 0; i < num_mutexes_; ++i) { + std::unique_lock lock(mutexes_[i]); + } table_.clear(); } template requires std::equality_comparable && std::movable auto CountingHashTable::getAllEntries() const - -> std::vector< - std::pair::Entry>> { - std::shared_lock lock(mutex_); + -> std::vector> { std::vector> entries; - entries.reserve(table_.size()); + // Lock all mutexes in a consistent order to avoid deadlocks + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].lock(); + } for (const auto& [key, entry] : table_) { entries.emplace_back(key, entry); } + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].unlock(); + } return entries; } template requires std::equality_comparable && std::movable void CountingHashTable::sortEntriesByCountDesc() { - std::unique_lock lock(mutex_); + std::unique_lock global_lock( + mutexes_[0]); // Simple approach: lock first mutex std::vector> entries(table_.begin(), table_.end()); + global_lock.unlock(); + std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) { return a.second.count.load(std::memory_order_relaxed) > b.second.count.load(std::memory_order_relaxed); }); - table_.clear(); + + // Rebuild the table for (auto& [key, entry] : entries) { - table_.emplace(std::move(key), std::move(entry)); + size_t index = getMutexIndex(key); + std::unique_lock lock(mutexes_[index]); + table_[key] = std::move(entry); } } template requires std::equality_comparable && std::movable auto CountingHashTable::getTopNEntries(size_t N) const - -> std::vector< - std::pair::Entry>> { - std::shared_lock lock(mutex_); - std::vector> entries(table_.begin(), table_.end()); + -> std::vector> { + std::vector> entries; + // Lock all mutexes in a consistent order to avoid deadlocks + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].lock(); + } + entries.reserve(table_.size()); + for (const auto& [key, entry] : table_) { + entries.emplace_back(key, entry); + } + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].unlock(); + } + std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) { return a.second.count.load(std::memory_order_relaxed) > b.second.count.load(std::memory_order_relaxed); }); + if (N > entries.size()) { N = entries.size(); } @@ -354,7 +423,6 @@ template void CountingHashTable::startAutoSorting( std::chrono::milliseconds interval, bool ascending) { { - std::unique_lock lock(mutex_); if (sortingThread_.joinable()) { return; } @@ -382,9 +450,19 @@ void CountingHashTable::sortingWorker( if (stopSorting.load(std::memory_order_relaxed)) { break; } - std::unique_lock lock(mutex_); - std::vector> entries(table_.begin(), - table_.end()); + std::vector> entries; + // Lock all mutexes in a consistent order to avoid deadlocks + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].lock(); + } + entries.reserve(table_.size()); + for (const auto& [key, entry] : table_) { + entries.emplace_back(key, entry); + } + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].unlock(); + } + std::sort( entries.begin(), entries.end(), [ascending](const auto& a, const auto& b) -> bool { @@ -395,31 +473,42 @@ void CountingHashTable::sortingWorker( return a.second.count.load(std::memory_order_relaxed) > b.second.count.load(std::memory_order_relaxed); }); - table_.clear(); + + // Rebuild the table for (auto& [key, entry] : entries) { - table_.emplace(std::move(key), std::move(entry)); + size_t index = getMutexIndex(key); + std::unique_lock lock(mutexes_[index]); + table_[key] = std::move(entry); } } } template requires std::equality_comparable && std::movable -auto CountingHashTable::serializeToJson() const -> nlohmann::json { - nlohmann::json j; - std::shared_lock lock(mutex_); +auto CountingHashTable::serializeToJson() const -> json { + json j; + // Lock all mutexes in a consistent order to avoid deadlocks + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].lock(); + } for (const auto& [key, entry] : table_) { j.push_back({{"key", key}, {"value", entry.value}, {"count", entry.count.load(std::memory_order_relaxed)}}); } + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].unlock(); + } return j; } template requires std::equality_comparable && std::movable -void CountingHashTable::deserializeFromJson( - const nlohmann::json& j) { - std::unique_lock lock(mutex_); +void CountingHashTable::deserializeFromJson(const json& j) { + // Lock all mutexes in a consistent order to avoid deadlocks + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].lock(); + } table_.clear(); for (const auto& item : j) { Key key = item.at("key").get(); @@ -429,6 +518,9 @@ void CountingHashTable::deserializeFromJson( entry.count.store(count, std::memory_order_relaxed); table_.emplace(std::move(key), std::move(entry)); } + for (size_t i = 0; i < num_mutexes_; ++i) { + mutexes_[i].unlock(); + } } } // namespace atom::type diff --git a/src/atom/type/expected.hpp b/src/atom/type/expected.hpp index da15782d..177eab92 100644 --- a/src/atom/type/expected.hpp +++ b/src/atom/type/expected.hpp @@ -10,7 +10,10 @@ namespace atom::type { /** - * @brief Generic Error class template. + * @brief A generic error class template that encapsulates error information. + * + * The `Error` class is used to represent and store error details. It provides + * access to the error and supports comparison operations. * * @tparam E The type of the error. */ @@ -18,44 +21,49 @@ template class Error { public: /** - * @brief Constructs an Error with the given error value. + * @brief Constructs an `Error` object with the given error. * - * @param error The error value. + * @param error The error to be stored. */ explicit Error(E error) : error_(std::move(error)) {} /** - * @brief Special constructor for const char* when E is std::string. + * @brief Constructs an `Error` object from a C-style string if the error + * type is `std::string`. * - * @param error The error message. + * @tparam T The type of the C-style string. + * @param error The C-style string representing the error. */ template requires std::is_same_v explicit Error(const T* error) : error_(error) {} /** - * @brief Retrieves the error value. + * @brief Retrieves the stored error. * - * @return The error value. + * @return A constant reference to the stored error. */ [[nodiscard]] auto error() const -> const E& { return error_; } /** - * @brief Equality operator for Error. + * @brief Compares two `Error` objects for equality. * - * @param other The other Error to compare with. - * @return True if the errors are equal, false otherwise. + * @param other The other `Error` object to compare with. + * @return `true` if both errors are equal, `false` otherwise. */ auto operator==(const Error& other) const -> bool { return error_ == other.error_; } private: - E error_; ///< The error value. + E error_; ///< The encapsulated error. }; /** - * @brief unexpected class similar to std::unexpected. + * @brief An `unexpected` class template similar to `std::unexpected`. + * + * The `unexpected` class is used to represent an error state in the `expected` + * type. * * @tparam E The type of the error. */ @@ -63,90 +71,158 @@ template class unexpected { public: /** - * @brief Constructs an unexpected with the given error value. + * @brief Constructs an `unexpected` object with a constant reference to an + * error. * - * @param error The error value. + * @param error The error to be stored. */ explicit unexpected(const E& error) : error_(error) {} /** - * @brief Constructs an unexpected with the given error value. + * @brief Constructs an `unexpected` object by moving an error. * - * @param error The error value. + * @param error The error to be stored. */ explicit unexpected(E&& error) : error_(std::move(error)) {} /** - * @brief Retrieves the error value. + * @brief Retrieves the stored error. * - * @return The error value. + * @return A constant reference to the stored error. */ [[nodiscard]] auto error() const -> const E& { return error_; } + /** + * @brief Compares two `unexpected` objects for equality. + * + * @param other The other `unexpected` object to compare with. + * @return `true` if both errors are equal, `false` otherwise. + */ + bool operator==(const unexpected& other) const { + return error_ == other.error_; + } + private: - E error_; ///< The error value. + E error_; ///< The encapsulated error. }; /** - * @brief Primary expected class template. + * @brief The primary `expected` class template. * - * @tparam T The type of the value. - * @tparam E The type of the error (default is std::string). + * The `expected` class represents a value that may either contain a valid value + * of type `T` or an error of type `E`. It provides mechanisms to access the + * value or the error and supports various monadic operations. + * + * @tparam T The type of the expected value. + * @tparam E The type of the error (default is `std::string`). */ template class expected { public: + // Constructors for value + + /** + * @brief Default constructs an `expected` object containing a + * default-constructed value. + * + * This constructor is only enabled if `T` is default constructible. + */ + constexpr expected() + requires std::is_default_constructible_v + : value_(std::in_place_index<0>, T()) {} + /** - * @brief Constructs an expected with the given value. + * @brief Constructs an `expected` object containing a copy of the given + * value. * - * @param value The value. + * @param value The value to be stored. */ - expected(const T& value) : value_(value) {} + constexpr expected(const T& value) + : value_(std::in_place_index<0>, value) {} /** - * @brief Constructs an expected with the given value. + * @brief Constructs an `expected` object containing a moved value. * - * @param value The value. + * @param value The value to be moved and stored. */ - expected(T&& value) : value_(std::move(value)) {} + constexpr expected(T&& value) + : value_(std::in_place_index<0>, std::move(value)) {} + + // Constructors for error /** - * @brief Constructs an expected with the given error. + * @brief Constructs an `expected` object containing a copy of the given + * error. * - * @param error The error. + * @param error The error to be stored. */ - expected(const Error& error) : value_(error) {} + constexpr expected(const Error& error) : value_(error) {} /** - * @brief Constructs an expected with the given error. + * @brief Constructs an `expected` object containing a moved error. * - * @param error The error. + * @param error The error to be moved and stored. */ - expected(Error&& error) : value_(std::move(error)) {} + constexpr expected(Error&& error) : value_(std::move(error)) {} /** - * @brief Constructs an expected with the given unexpected error. + * @brief Constructs an `expected` object from an `unexpected` error by + * copying it. * - * @param unex The unexpected error. + * @param unex The `unexpected` error to be stored. */ - expected(const unexpected& unex) : value_(Error(unex.error())) {} + constexpr expected(const unexpected& unex) + : value_(Error(unex.error())) {} /** - * @brief Checks if the expected object contains a value. + * @brief Constructs an `expected` object from an `unexpected` error by + * moving it. * - * @return True if it contains a value, false otherwise. + * @param unex The `unexpected` error to be moved and stored. */ - [[nodiscard]] auto has_value() const -> bool { + constexpr expected(unexpected&& unex) + : value_(Error(std::move(unex.error()))) {} + + // Copy and move constructors + + /** + * @brief Default copy constructor. + */ + constexpr expected(const expected&) = default; + + /** + * @brief Default move constructor. + */ + constexpr expected(expected&&) noexcept = default; + + /** + * @brief Default copy assignment operator. + */ + constexpr expected& operator=(const expected&) = default; + + /** + * @brief Default move assignment operator. + */ + constexpr expected& operator=(expected&&) noexcept = default; + + // Observers + + /** + * @brief Checks if the `expected` object contains a valid value. + * + * @return `true` if it contains a value, `false` if it contains an error. + */ + [[nodiscard]] constexpr bool has_value() const noexcept { return std::holds_alternative(value_); } /** - * @brief Retrieves the value, throws if it's an error. + * @brief Retrieves a reference to the stored value. * - * @return The value. - * @throws std::logic_error if it contains an error. + * @return A reference to the stored value. + * @throws std::logic_error If the `expected` contains an error. */ - auto value() -> T& { + [[nodiscard]] constexpr T& value() & { if (!has_value()) { throw std::logic_error( "Attempted to access value, but it contains an error."); @@ -155,12 +231,12 @@ class expected { } /** - * @brief Retrieves the value, throws if it's an error. + * @brief Retrieves a constant reference to the stored value. * - * @return The value. - * @throws std::logic_error if it contains an error. + * @return A constant reference to the stored value. + * @throws std::logic_error If the `expected` contains an error. */ - [[nodiscard]] auto value() const -> const T& { + [[nodiscard]] constexpr const T& value() const& { if (!has_value()) { throw std::logic_error( "Attempted to access value, but it contains an error."); @@ -169,12 +245,26 @@ class expected { } /** - * @brief Retrieves the error, throws if it's a value. + * @brief Retrieves an rvalue reference to the stored value. * - * @return The error. - * @throws std::logic_error if it contains a value. + * @return An rvalue reference to the stored value. + * @throws std::logic_error If the `expected` contains an error. */ - auto error() -> Error& { + [[nodiscard]] constexpr T&& value() && { + if (!has_value()) { + throw std::logic_error( + "Attempted to access value, but it contains an error."); + } + return std::get(std::move(value_)); + } + + /** + * @brief Retrieves a constant reference to the stored error. + * + * @return A constant reference to the stored error. + * @throws std::logic_error If the `expected` contains a value. + */ + [[nodiscard]] constexpr const Error& error() const& { if (has_value()) { throw std::logic_error( "Attempted to access error, but it contains a value."); @@ -183,12 +273,12 @@ class expected { } /** - * @brief Retrieves the error, throws if it's a value. + * @brief Retrieves a reference to the stored error. * - * @return The error. - * @throws std::logic_error if it contains a value. + * @return A reference to the stored error. + * @throws std::logic_error If the `expected` contains a value. */ - [[nodiscard]] auto error() const -> const Error& { + [[nodiscard]] constexpr Error& error() & { if (has_value()) { throw std::logic_error( "Attempted to access error, but it contains a value."); @@ -197,182 +287,311 @@ class expected { } /** - * @brief Retrieves the value or a default value if it contains an error. + * @brief Retrieves an rvalue reference to the stored error. * - * @tparam U The type of the default value. - * @param default_value The default value. - * @return The value or the default value. + * @return An rvalue reference to the stored error. + * @throws std::logic_error If the `expected` contains a value. */ - template - auto value_or(U&& default_value) const -> T { + [[nodiscard]] constexpr Error&& error() && { if (has_value()) { - return value(); - } - if constexpr (std::is_invocable_v) { - return std::forward(default_value)(error().error()); - } else { - return static_cast(std::forward(default_value)); + throw std::logic_error( + "Attempted to access error, but it contains a value."); } + return std::get>(std::move(value_)); } /** - * @brief Maps the value to another type using the given function. + * @brief Conversion operator to `bool`. + * + * @return `true` if the `expected` contains a value, `false` otherwise. + */ + constexpr explicit operator bool() const noexcept { return has_value(); } + + // Monadic operations + + /** + * @brief Applies a function to the stored value if it exists. * - * @tparam Func The type of the function. - * @param func The function to apply to the value. - * @return An expected object with the mapped value or the original error. + * @tparam Func The type of the function to apply. + * @param func The function to apply to the stored value. + * @return The result of the function if a value exists, or an `expected` + * containing the error. */ template - auto map(Func&& func) const - -> expected())), E> { - using ReturnType = decltype(func(std::declval())); + constexpr auto and_then( + Func&& func) & -> decltype(func(std::declval())) { if (has_value()) { - return expected(func(value())); - } else { - return expected(error()); + return func(value()); } + return decltype(func(std::declval()))(error()); } /** - * @brief Applies the given function to the value if it exists. + * @brief Applies a constant function to the stored value if it exists. * - * @tparam Func The type of the function. - * @param func The function to apply to the value. - * @return The result of the function or the original error. + * @tparam Func The type of the function to apply. + * @param func The function to apply to the stored value. + * @return The result of the function if a value exists, or an `expected` + * containing the error. */ template - auto and_then(Func&& func) const -> decltype(func(std::declval())) { + constexpr auto and_then( + Func&& func) const& -> decltype(func(std::declval())) { if (has_value()) { return func(value()); } - using ReturnType = decltype(func(value())); - return ReturnType(error()); + return decltype(func(std::declval()))(error()); } /** - * @brief Transforms the error using the given function. + * @brief Applies a function to the stored value if it exists, moving the + * value. * - * @tparam Func The type of the function. - * @param func The function to apply to the error. - * @return An expected object with the original value or the transformed - * error. + * @tparam Func The type of the function to apply. + * @param func The function to apply to the stored value. + * @return The result of the function if a value exists, or an `expected` + * containing the error. + */ + template + constexpr auto and_then( + Func&& func) && -> decltype(func(std::declval())) { + if (has_value()) { + return func(std::move(value())); + } + return decltype(func(std::declval()))(std::move(error())); + } + + /** + * @brief Applies a function to the stored value if it exists and wraps the + * result in an `expected`. + * + * @tparam Func The type of the function to apply. + * @param func The function to apply to the stored value. + * @return An `expected` containing the result of the function, or an + * `expected` containing the error. + */ + template + constexpr auto map( + Func&& func) & -> expected())), E> { + if (has_value()) { + return expected(func(value())); + } + return expected())), E>(error()); + } + + /** + * @brief Applies a constant function to the stored value if it exists and + * wraps the result in an `expected`. + * + * @tparam Func The type of the function to apply. + * @param func The function to apply to the stored value. + * @return An `expected` containing the result of the function, or an + * `expected` containing the error. + */ + template + constexpr auto map(Func&& func) + const& -> expected())), E> { + if (has_value()) { + return expected(func(value())); + } + return expected())), E>(error()); + } + + /** + * @brief Applies a function to the stored value if it exists, moving the + * value, and wraps the result in an `expected`. + * + * @tparam Func The type of the function to apply. + * @param func The function to apply to the stored value. + * @return An `expected` containing the result of the function, or an + * `expected` containing the error. + */ + template + constexpr auto map( + Func&& func) && -> expected())), E> { + if (has_value()) { + return expected())), E>( + func(std::move(value()))); + } + return expected())), E>( + std::move(error())); + } + + /** + * @brief Transforms the stored error using the provided function. + * + * @tparam Func The type of the function to apply to the error. + * @param func The function to apply to the stored error. + * @return An `expected` with the transformed error type if an error exists, + * otherwise the original `expected`. */ template - auto transform_error(Func&& func) const - -> expected()))> { - using ErrorType = decltype(func(std::declval())); + constexpr auto transform_error( + Func&& func) & -> expected()))> { if (has_value()) { - return expected(value()); + return *this; } - return expected(Error(func(error().error()))); + return expected()))>( + func(error().error())); } /** - * @brief Applies the given function to the error if it exists. + * @brief Transforms the stored error using the provided constant function. * - * @tparam Func The type of the function. - * @param func The function to apply to the error. - * @return An expected object with the original value or the result of the - * function. + * @tparam Func The type of the function to apply to the error. + * @param func The function to apply to the stored error. + * @return An `expected` with the transformed error type if an error exists, + * otherwise the original `expected`. */ template - auto or_else(Func&& func) const -> expected { + constexpr auto transform_error(Func&& func) + const& -> expected()))> { if (has_value()) { return *this; } - return func(error().error()); + return expected()))>( + func(error().error())); } /** - * @brief Equality operator for expected. + * @brief Transforms the stored error using the provided function, moving + * the error. * - * @param lhs The left-hand side expected. - * @param rhs The right-hand side expected. - * @return True if the expected objects are equal, false otherwise. + * @tparam Func The type of the function to apply to the error. + * @param func The function to apply to the stored error. + * @return An `expected` with the transformed error type if an error exists, + * otherwise the original `expected`. */ - friend auto operator==(const expected& lhs, const expected& rhs) -> bool { - return lhs.value_ == rhs.value_; + template + constexpr auto transform_error( + Func&& func) && -> expected()))> { + if (has_value()) { + return std::move(*this); + } + return expected()))>( + func(std::move(error().error()))); } + // Equality operators + /** - * @brief Inequality operator for expected. + * @brief Compares two `expected` objects for equality. * - * @param lhs The left-hand side expected. - * @param rhs The right-hand side expected. - * @return True if the expected objects are not equal, false otherwise. + * @param lhs The left-hand side `expected` object. + * @param rhs The right-hand side `expected` object. + * @return `true` if both `expected` objects are equal, `false` otherwise. */ - friend auto operator!=(const expected& lhs, const expected& rhs) -> bool { + friend constexpr bool operator==(const expected& lhs, const expected& rhs) { + if (lhs.has_value() != rhs.has_value()) + return false; + if (lhs.has_value()) { + return lhs.value_ == rhs.value_; + } + return lhs.error_ == rhs.error_; + } + + /** + * @brief Compares two `expected` objects for inequality. + * + * @param lhs The left-hand side `expected` object. + * @param rhs The right-hand side `expected` object. + * @return `true` if both `expected` objects are not equal, `false` + * otherwise. + */ + friend constexpr bool operator!=(const expected& lhs, const expected& rhs) { return !(lhs == rhs); } private: - std::variant> value_; ///< The value or error. + std::variant> + value_; ///< The variant holding either the value or the error. }; /** - * @brief Specialization of expected for void type. + * @brief Specialization of the `expected` class template for `void` type. + * + * This specialization handles cases where no value is expected, only an error. * * @tparam E The type of the error. */ template class expected { public: + // Constructors for value + /** - * @brief Constructs an expected with a void value. + * @brief Default constructs an `expected` object containing no value. */ - expected() : value_(std::monostate{}) {} + constexpr expected() noexcept : value_(std::monostate{}) {} + + // Constructors for error /** - * @brief Constructs an expected with the given error. + * @brief Constructs an `expected` object containing a copy of the given + * error. * - * @param error The error. + * @param error The error to be stored. */ - expected(const Error& error) : value_(error) {} + constexpr expected(const Error& error) : value_(error) {} /** - * @brief Constructs an expected with the given error. + * @brief Constructs an `expected` object containing a moved error. * - * @param error The error. + * @param error The error to be moved and stored. */ - expected(Error&& error) : value_(std::move(error)) {} + constexpr expected(Error&& error) : value_(std::move(error)) {} /** - * @brief Constructs an expected with the given unexpected error. + * @brief Constructs an `expected` object from an `unexpected` error by + * copying it. * - * @param unex The unexpected error. + * @param unex The `unexpected` error to be stored. */ - expected(const unexpected& unex) : value_(Error(unex.error())) {} + constexpr expected(const unexpected& unex) + : value_(Error(unex.error())) {} /** - * @brief Checks if the expected object contains a value. + * @brief Constructs an `expected` object from an `unexpected` error by + * moving it. * - * @return True if it contains a value, false otherwise. + * @param unex The `unexpected` error to be moved and stored. */ - [[nodiscard]] auto has_value() const -> bool { + constexpr expected(unexpected&& unex) + : value_(Error(std::move(unex.error()))) {} + + // Observers + + /** + * @brief Checks if the `expected` object contains a valid value. + * + * @return `true` if it contains a value, `false` if it contains an error. + */ + [[nodiscard]] constexpr bool has_value() const noexcept { return std::holds_alternative(value_); } /** - * @brief A no-op value_or function, returns nothing as the value type is - * void. + * @brief Retrieves the stored value. * - * @tparam U The type of the default value. - * @param default_value The default value. + * Since the value type is `void`, this function does nothing but can throw + * if an error exists. + * + * @throws std::logic_error If the `expected` contains an error. */ - template - auto value_or(U&& default_value) const -> void { + constexpr void value() const { if (!has_value()) { - std::forward(default_value)(error().error()); + throw std::logic_error( + "Attempted to access value, but it contains an error."); } } /** - * @brief Retrieves the error, throws if it's a value. + * @brief Retrieves a constant reference to the stored error. * - * @return The error. - * @throws std::logic_error if it contains a value. + * @return A constant reference to the stored error. + * @throws std::logic_error If the `expected` contains a value. */ - auto error() -> Error& { + [[nodiscard]] constexpr const Error& error() const& { if (has_value()) { throw std::logic_error( "Attempted to access error, but it contains a value."); @@ -381,12 +600,12 @@ class expected { } /** - * @brief Retrieves the error, throws if it's a value. + * @brief Retrieves a reference to the stored error. * - * @return The error. - * @throws std::logic_error if it contains a value. + * @return A reference to the stored error. + * @throws std::logic_error If the `expected` contains a value. */ - [[nodiscard]] auto error() const -> const Error& { + [[nodiscard]] constexpr Error& error() & { if (has_value()) { throw std::logic_error( "Attempted to access error, but it contains a value."); @@ -395,129 +614,216 @@ class expected { } /** - * @brief Applies the given function if it contains a value. + * @brief Retrieves an rvalue reference to the stored error. + * + * @return An rvalue reference to the stored error. + * @throws std::logic_error If the `expected` contains a value. + */ + [[nodiscard]] constexpr Error&& error() && { + if (has_value()) { + throw std::logic_error( + "Attempted to access error, but it contains a value."); + } + return std::get>(std::move(value_)); + } + + /** + * @brief Conversion operator to `bool`. * - * @tparam Func The type of the function. + * @return `true` if the `expected` contains a value, `false` otherwise. + */ + constexpr explicit operator bool() const noexcept { return has_value(); } + + // Monadic operations + + /** + * @brief Applies a function to the `expected` object if it contains a + * value. + * + * @tparam Func The type of the function to apply. * @param func The function to apply. - * @return An expected object with the result of the function or the - * original error. + * @return The result of the function if a value exists, or an `expected` + * containing the error. */ template - auto and_then(Func&& func) const -> expected { + constexpr auto and_then(Func&& func) & -> decltype(func()) { if (has_value()) { - func(); - return expected(); + return func(); } - return expected(error()); + return decltype(func())(error()); } /** - * @brief Transforms the error using the given function. + * @brief Applies a constant function to the `expected` object if it + * contains a value. * - * @tparam Func The type of the function. - * @param func The function to apply to the error. - * @return An expected object with the original value or the transformed - * error. + * @tparam Func The type of the function to apply. + * @param func The function to apply. + * @return The result of the function if a value exists, or an `expected` + * containing the error. + */ + template + constexpr auto and_then(Func&& func) const& -> decltype(func()) { + if (has_value()) { + return func(); + } + return decltype(func())(error()); + } + + /** + * @brief Applies a function to the `expected` object if it contains a + * value, moving the error. + * + * @tparam Func The type of the function to apply. + * @param func The function to apply. + * @return The result of the function if a value exists, or an `expected` + * containing the error. + */ + template + constexpr auto and_then(Func&& func) && -> decltype(func()) { + if (has_value()) { + return func(); + } + return decltype(func())(std::move(error())); + } + + /** + * @brief Transforms the stored error using the provided function. + * + * @tparam Func The type of the function to apply to the error. + * @param func The function to apply to the stored error. + * @return An `expected` with the transformed error type if an error exists, + * otherwise the original `expected`. */ template - auto transform_error(Func&& func) const - -> expected()))> { - using ErrorType = decltype(func(std::declval())); + constexpr auto transform_error( + Func&& func) & -> expected()))> { if (has_value()) { - return expected(); + return *this; } - return expected( - Error(func(error().error()))); + return expected()))>( + func(error().error())); } /** - * @brief Applies the given function to the error if it exists. + * @brief Transforms the stored error using the provided constant function. * - * @tparam Func The type of the function. - * @param func The function to apply to the error. - * @return An expected object with the original value or the result of the - * function. + * @tparam Func The type of the function to apply to the error. + * @param func The function to apply to the stored error. + * @return An `expected` with the transformed error type if an error exists, + * otherwise the original `expected`. */ template - auto or_else(Func&& func) const -> expected { + constexpr auto transform_error(Func&& func) + const& -> expected()))> { if (has_value()) { return *this; } - return func(error().error()); + return expected()))>( + func(error().error())); + } + + /** + * @brief Transforms the stored error using the provided function, moving + * the error. + * + * @tparam Func The type of the function to apply to the error. + * @param func The function to apply to the stored error. + * @return An `expected` with the transformed error type if an error exists, + * otherwise the original `expected`. + */ + template + constexpr auto transform_error( + Func&& func) && -> expected()))> { + if (has_value()) { + return std::move(*this); + } + return expected()))>( + func(std::move(error().error()))); } + // Equality operators + /** - * @brief Equality operator for expected. + * @brief Compares two `expected` objects for equality. * - * @param lhs The left-hand side expected. - * @param rhs The right-hand side expected. - * @return True if the expected objects are equal, false otherwise. + * @param lhs The left-hand side `expected` object. + * @param rhs The right-hand side `expected` object. + * @return `true` if both `expected` objects are equal, `false` otherwise. */ - friend auto operator==(const expected& lhs, const expected& rhs) -> bool { - return lhs.value_ == rhs.value_; + friend constexpr bool operator==(const expected& lhs, const expected& rhs) { + if (lhs.has_value() != rhs.has_value()) + return false; + if (lhs.has_value()) { + return true; + } + return lhs.error_ == rhs.error_; } /** - * @brief Inequality operator for expected. + * @brief Compares two `expected` objects for inequality. * - * @param lhs The left-hand side expected. - * @param rhs The right-hand side expected. - * @return True if the expected objects are not equal, false otherwise. + * @param lhs The left-hand side `expected` object. + * @param rhs The right-hand side `expected` object. + * @return `true` if both `expected` objects are not equal, `false` + * otherwise. */ - friend auto operator!=(const expected& lhs, const expected& rhs) -> bool { + friend constexpr bool operator!=(const expected& lhs, const expected& rhs) { return !(lhs == rhs); } private: - std::variant> value_; ///< The value or error. + std::variant> + value_; ///< The variant holding either no value or the error. }; /** - * @brief Utility function to create an expected object. + * @brief Creates an `expected` object containing the given value. * * @tparam T The type of the value. - * @param value The value. - * @return An expected object containing the value. + * @param value The value to be stored in the `expected`. + * @return An `expected` object containing the value. */ template -auto make_expected(T&& value) -> expected> { +constexpr auto make_expected(T&& value) -> expected> { return expected>(std::forward(value)); } /** - * @brief Utility function to create an unexpected object. + * @brief Creates an `unexpected` object containing the given error. * * @tparam E The type of the error. - * @param error The error. - * @return An unexpected object containing the error. + * @param error The error to be stored in the `unexpected`. + * @return An `unexpected` object containing the error. */ template -auto make_unexpected(const E& error) -> unexpected> { +constexpr auto make_unexpected(const E& error) -> unexpected> { return unexpected>(error); } /** - * @brief Utility function to create an unexpected object from a const char*. + * @brief Creates an `unexpected` object by moving the given error. * - * @param error The error message. - * @return An unexpected object containing the error message. + * @tparam E The type of the error. + * @param error The error to be moved and stored in the `unexpected`. + * @return An `unexpected` object containing the moved error. */ -auto make_unexpected(const char* error) -> unexpected { - return unexpected(std::string(error)); +template +constexpr auto make_unexpected(E&& error) -> unexpected> { + return unexpected>(std::forward(error)); } /** - * @brief Utility function to create an unexpected object. + * @brief Creates an `unexpected` object containing a `std::string` error from a + * C-style string. * - * @tparam E The type of the error. - * @param error The error. - * @return An unexpected object containing the error. + * @param error The C-style string representing the error. + * @return An `unexpected` object containing the error. */ -template -auto make_unexpected(E&& error) -> unexpected> { - return unexpected>(std::forward(error)); +inline auto make_unexpected(const char* error) -> unexpected { + return unexpected(std::string(error)); } } // namespace atom::type -#endif // ATOM_TYPE_EXPECTED_HPP +#endif // ATOM_TYPE_EXPECTED_HPP \ No newline at end of file diff --git a/src/atom/utils/argsview.hpp b/src/atom/utils/argsview.hpp index b5208e55..13578812 100644 --- a/src/atom/utils/argsview.hpp +++ b/src/atom/utils/argsview.hpp @@ -3,11 +3,14 @@ #include #include +#include #include #include #include +#include #include #include +#include #include #include "atom/error/exception.hpp" @@ -30,19 +33,46 @@ class ArgumentParser { AUTO }; + enum class NargsType { + NONE, + OPTIONAL, + ZERO_OR_MORE, + ONE_OR_MORE, + CONSTANT + }; + + struct Nargs { + NargsType type; + int count; // Used if type is CONSTANT + + Nargs() : type(NargsType::NONE), count(1) {} + Nargs(NargsType t, int c = 1) : type(t), count(c) {} + }; + ArgumentParser() = default; explicit ArgumentParser(std::string program_name); + // 设置描述和结尾 + void setDescription(const std::string& description); + void setEpilog(const std::string& epilog); + void addArgument(const std::string& name, ArgType type = ArgType::AUTO, bool required = false, const std::any& default_value = {}, const std::string& help = "", - const std::vector& aliases = {}); + const std::vector& aliases = {}, + bool is_positional = false, const Nargs& nargs = Nargs()); void addFlag(const std::string& name, const std::string& help = "", const std::vector& aliases = {}); void addSubcommand(const std::string& name, const std::string& help = ""); + void addMutuallyExclusiveGroup(const std::vector& group_args); + + // 自定义文件解析 + void addArgumentFromFile(const std::string& prefix = "@"); + void setFileDelimiter(char delimiter); + void parse(int argc, std::vector argv); template @@ -58,19 +88,35 @@ class ArgumentParser { private: struct Argument { ArgType type; - bool required; + bool required{}; std::any defaultValue; std::optional value; std::string help; std::vector aliases; - bool isMultivalue; - }; + bool isMultivalue{}; + bool is_positional{}; + Nargs nargs; + + Argument() = default; + + Argument(ArgType t, bool req, std::any def, std::string hlp, + const std::vector& als, bool mult = false, + bool pos = false, const Nargs& ng = Nargs()) + : type(t), + required(req), + defaultValue(std::move(def)), + help(std::move(hlp)), + aliases(als), + isMultivalue(mult), + is_positional(pos), + nargs(ng) {} + } ATOM_ALIGNAS(128); struct Flag { bool value; std::string help; std::vector aliases; - }; + } ATOM_ALIGNAS(64); struct Subcommand; @@ -83,97 +129,205 @@ class ArgumentParser { std::string epilog_; std::string programName_; + std::vector> mutuallyExclusiveGroups_; + + // 文件解析相关 + bool enableFileParsing_ = false; + std::string filePrefix_ = "@"; + char fileDelimiter_ = ' '; + static auto detectType(const std::any& value) -> ArgType; static auto parseValue(ArgType type, const std::string& value) -> std::any; static auto argTypeToString(ArgType type) -> std::string; static auto anyToString(const std::any& value) -> std::string; + void expandArgumentsFromFile(std::vector& argv); }; struct ArgumentParser::Subcommand { std::string help; ArgumentParser parser; -}; +} ATOM_ALIGNAS(128); -ATOM_INLINE ArgumentParser::ArgumentParser(std::string program_name) +inline ArgumentParser::ArgumentParser(std::string program_name) : programName_(std::move(program_name)) {} -ATOM_INLINE void ArgumentParser::addArgument( - const std::string& name, ArgType type, bool required, - const std::any& default_value, const std::string& help, - const std::vector& aliases) { +inline void ArgumentParser::setDescription(const std::string& description) { + description_ = description; +} + +inline void ArgumentParser::setEpilog(const std::string& epilog) { + epilog_ = epilog; +} + +inline void ArgumentParser::addArgument(const std::string& name, ArgType type, + bool required, + const std::any& default_value, + const std::string& help, + const std::vector& aliases, + bool is_positional, + const Nargs& nargs) { if (type == ArgType::AUTO && default_value.has_value()) { type = detectType(default_value); } else if (type == ArgType::AUTO) { type = ArgType::STRING; } - arguments_[name] = Argument{type, required, default_value, std::nullopt, - help, aliases, false}; + arguments_[name] = + Argument{type, required, default_value, + help, aliases, nargs.type != NargsType::NONE, + is_positional, nargs}; for (const auto& alias : aliases) { aliases_[alias] = name; } } -ATOM_INLINE void ArgumentParser::addFlag( - const std::string& name, const std::string& help, - const std::vector& aliases) { +inline void ArgumentParser::addFlag(const std::string& name, + const std::string& help, + const std::vector& aliases) { flags_[name] = Flag{false, help, aliases}; for (const auto& alias : aliases) { aliases_[alias] = name; } } -ATOM_INLINE void ArgumentParser::addSubcommand(const std::string& name, - const std::string& help) { +inline void ArgumentParser::addSubcommand(const std::string& name, + const std::string& help) { subcommands_[name] = Subcommand{help, ArgumentParser(name)}; } -ATOM_INLINE void ArgumentParser::parse(int argc, - std::vector argv) { +inline void ArgumentParser::addMutuallyExclusiveGroup( + const std::vector& group_args) { + mutuallyExclusiveGroups_.emplace_back(group_args); +} + +inline void ArgumentParser::addArgumentFromFile(const std::string& prefix) { + enableFileParsing_ = true; + filePrefix_ = prefix; +} + +inline void ArgumentParser::setFileDelimiter(char delimiter) { + fileDelimiter_ = delimiter; +} + +inline void ArgumentParser::parse(int argc, std::vector argv) { if (argc < 1) return; + // 扩展来自文件的参数 + if (enableFileParsing_) { + expandArgumentsFromFile(argv); + } + std::string currentSubcommand; std::vector subcommandArgs; - for (int i = 1; i < argc; ++i) { + // Track which mutually exclusive groups have been used + std::vector groupUsed(mutuallyExclusiveGroups_.size(), false); + + for (size_t i = 0; i < argv.size(); ++i) { std::string arg = argv[i]; + + // Check for subcommand if (subcommands_.find(arg) != subcommands_.end()) { currentSubcommand = arg; subcommandArgs.push_back(argv[0]); // Program name continue; } + // If inside a subcommand, pass arguments to subcommand parser if (!currentSubcommand.empty()) { subcommandArgs.push_back(argv[i]); continue; } + // Handle help flag if (arg == "--help" || arg == "-h") { printHelp(); std::exit(0); - } else if (arg.starts_with("--") || arg.starts_with("-")) { - arg = arg.starts_with("--") ? arg.substr(2) : arg.substr(1); - if (aliases_.find(arg) != aliases_.end()) { - arg = aliases_[arg]; + } + + // Handle optional arguments and flags + if (arg.starts_with("--") || arg.starts_with("-")) { + std::string argName; + bool isFlag = false; + + if (arg.starts_with("--")) { + argName = arg.substr(2); + } else { + argName = arg.substr(1); + } + + // Resolve aliases + if (aliases_.find(argName) != aliases_.end()) { + argName = aliases_[argName]; + } + + // Check if it's a flag + if (flags_.find(argName) != flags_.end()) { + flags_[argName].value = true; + continue; } - if (flags_.find(arg) != flags_.end()) { - flags_[arg].value = true; - } else if (arguments_.find(arg) != arguments_.end()) { - if (i + 1 < argc) { - arguments_[arg].value = - parseValue(arguments_[arg].type, argv[++i]); - } else { - THROW_INVALID_ARGUMENT("Value for argument " + arg + - " not provided"); + + // Check if it's an argument + if (arguments_.find(argName) != arguments_.end()) { + Argument& argument = arguments_[argName]; + std::vector values; + + // Handle nargs + int expected = 1; + bool is_constant = false; + if (argument.nargs.type == NargsType::ONE_OR_MORE) { + expected = -1; // Indicate multiple + } else if (argument.nargs.type == NargsType::ZERO_OR_MORE) { + expected = -1; + } else if (argument.nargs.type == NargsType::OPTIONAL) { + expected = 1; + } else if (argument.nargs.type == NargsType::CONSTANT) { + expected = argument.nargs.count; + is_constant = true; } - } else { - THROW_INVALID_ARGUMENT("Unknown argument: " + arg); + + // Collect values based on nargs + for (int j = 0; j < expected || expected == -1; ++j) { + if (i + 1 < static_cast(argv.size()) && + !argv[i + 1].starts_with("-")) { + values.emplace_back(argv[++i]); + } else { + break; + } + } + + if (is_constant && + static_cast(values.size()) != argument.nargs.count) { + THROW_INVALID_ARGUMENT( + "Argument " + argName + " expects " + + std::to_string(argument.nargs.count) + " value(s)."); + } + + if (values.empty() && + argument.nargs.type == NargsType::OPTIONAL) { + // Optional argument without a value + if (argument.defaultValue.has_value()) { + argument.value = argument.defaultValue; + } + } else if (!values.empty()) { + if (expected == -1) { // Multiple values + // Store as vector + argument.value = std::any(values); + } else { // Single value + argument.value = parseValue(argument.type, values[0]); + } + } + + continue; } - } else { - positionalArguments_.push_back(arg); + + THROW_INVALID_ARGUMENT("Unknown argument: " + arg); } + + // Handle positional arguments + positionalArguments_.push_back(arg); } if (!currentSubcommand.empty() && !subcommandArgs.empty()) { @@ -181,6 +335,26 @@ ATOM_INLINE void ArgumentParser::parse(int argc, static_cast(subcommandArgs.size()), subcommandArgs); } + // Validate mutually exclusive groups + for (size_t g = 0; g < mutuallyExclusiveGroups_.size(); ++g) { + int count = 0; + for (const auto& arg : mutuallyExclusiveGroups_[g]) { + if (flags_.find(arg) != flags_.end() && flags_[arg].value) { + count++; + } + if (arguments_.find(arg) != arguments_.end() && + arguments_[arg].value.has_value()) { + count++; + } + } + if (count > 1) { + THROW_INVALID_ARGUMENT("Arguments in mutually exclusive group " + + std::to_string(g + 1) + + " cannot be used together."); + } + } + + // Check required arguments for (const auto& [name, argument] : arguments_) { if (argument.required && !argument.value.has_value() && !argument.defaultValue.has_value()) { @@ -194,32 +368,39 @@ auto ArgumentParser::get(const std::string& name) const -> std::optional { if (arguments_.find(name) != arguments_.end()) { const auto& arg = arguments_.at(name); if (arg.value.has_value()) { - return std::any_cast(arg.value.value()); + try { + return std::any_cast(arg.value.value()); + } catch (const std::bad_any_cast&) { + return std::nullopt; + } } if (arg.defaultValue.has_value()) { - return std::any_cast(arg.defaultValue); + try { + return std::any_cast(arg.defaultValue); + } catch (const std::bad_any_cast&) { + return std::nullopt; + } } } return std::nullopt; } -ATOM_INLINE auto ArgumentParser::getFlag(const std::string& name) const - -> bool { +inline auto ArgumentParser::getFlag(const std::string& name) const -> bool { if (flags_.find(name) != flags_.end()) { return flags_.at(name).value; } return false; } -ATOM_INLINE auto ArgumentParser::getSubcommandParser(const std::string& name) - const -> std::optional> { +inline auto ArgumentParser::getSubcommandParser(const std::string& name) const + -> std::optional> { if (subcommands_.find(name) != subcommands_.end()) { return subcommands_.at(name).parser; } return std::nullopt; } -ATOM_INLINE void ArgumentParser::printHelp() const { +inline void ArgumentParser::printHelp() const { std::cout << "Usage:\n " << programName_ << " [options] "; if (!subcommands_.empty()) { std::cout << " [subcommand options]"; @@ -232,6 +413,8 @@ ATOM_INLINE void ArgumentParser::printHelp() const { std::cout << "Options:\n"; for (const auto& [name, argument] : arguments_) { + if (argument.is_positional) + continue; std::cout << " --" << name; for (const auto& alias : argument.aliases) { std::cout << ", -" << alias; @@ -241,6 +424,26 @@ ATOM_INLINE void ArgumentParser::printHelp() const { std::cout << " (default: " << anyToString(argument.defaultValue) << ")"; } + if (argument.nargs.type != NargsType::NONE) { + std::cout << " [nargs: "; + switch (argument.nargs.type) { + case NargsType::OPTIONAL: + std::cout << "?"; + break; + case NargsType::ZERO_OR_MORE: + std::cout << "*"; + break; + case NargsType::ONE_OR_MORE: + std::cout << "+"; + break; + case NargsType::CONSTANT: + std::cout << std::to_string(argument.nargs.count); + break; + default: + std::cout << "1"; + } + std::cout << "]"; + } std::cout << "\n"; } for (const auto& [name, flag] : flags_) { @@ -251,6 +454,61 @@ ATOM_INLINE void ArgumentParser::printHelp() const { std::cout << " : " << flag.help << "\n"; } + // Positional arguments + std::vector positional; + for (const auto& [name, argument] : arguments_) { + if (argument.is_positional) { + positional.push_back(name); + } + } + if (!positional.empty()) { + std::cout << "\nPositional Arguments:\n"; + for (const auto& name : positional) { + const auto& argument = arguments_.at(name); + std::cout << " " << name; + std::cout << " : " << argument.help; + if (argument.defaultValue.has_value()) { + std::cout << " (default: " << anyToString(argument.defaultValue) + << ")"; + } + if (argument.nargs.type != NargsType::NONE) { + std::cout << " [nargs: "; + switch (argument.nargs.type) { + case NargsType::OPTIONAL: + std::cout << "?"; + break; + case NargsType::ZERO_OR_MORE: + std::cout << "*"; + break; + case NargsType::ONE_OR_MORE: + std::cout << "+"; + break; + case NargsType::CONSTANT: + std::cout << std::to_string(argument.nargs.count); + break; + default: + std::cout << "1"; + } + std::cout << "]"; + } + std::cout << "\n"; + } + } + + if (!mutuallyExclusiveGroups_.empty()) { + std::cout << "\nMutually Exclusive Groups:\n"; + for (size_t g = 0; g < mutuallyExclusiveGroups_.size(); ++g) { + std::cout << " Group " << g + 1 << ": "; + for (size_t i = 0; i < mutuallyExclusiveGroups_[g].size(); ++i) { + std::cout << "--" << mutuallyExclusiveGroups_[g][i]; + if (i != mutuallyExclusiveGroups_[g].size() - 1) { + std::cout << ", "; + } + } + std::cout << "\n"; + } + } + if (!subcommands_.empty()) { std::cout << "\nSubcommands:\n"; for (const auto& [name, subcommand] : subcommands_) { @@ -263,7 +521,7 @@ ATOM_INLINE void ArgumentParser::printHelp() const { } } -ATOM_INLINE auto ArgumentParser::detectType(const std::any& value) -> ArgType { +inline auto ArgumentParser::detectType(const std::any& value) -> ArgType { if (value.type() == typeid(int)) { return ArgType::INTEGER; } @@ -294,8 +552,8 @@ ATOM_INLINE auto ArgumentParser::detectType(const std::any& value) -> ArgType { return ArgType::STRING; } -ATOM_INLINE auto ArgumentParser::parseValue( - ArgType type, const std::string& value) -> std::any { +inline auto ArgumentParser::parseValue(ArgType type, + const std::string& value) -> std::any { try { switch (type) { case ArgType::STRING: @@ -324,7 +582,7 @@ ATOM_INLINE auto ArgumentParser::parseValue( } } -ATOM_INLINE auto ArgumentParser::argTypeToString(ArgType type) -> std::string { +inline auto ArgumentParser::argTypeToString(ArgType type) -> std::string { switch (type) { case ArgType::STRING: return "string"; @@ -351,8 +609,7 @@ ATOM_INLINE auto ArgumentParser::argTypeToString(ArgType type) -> std::string { } } -ATOM_INLINE auto ArgumentParser::anyToString(const std::any& value) - -> std::string { +inline auto ArgumentParser::anyToString(const std::any& value) -> std::string { if (value.type() == typeid(std::string)) { return std::any_cast(value); } @@ -383,6 +640,35 @@ ATOM_INLINE auto ArgumentParser::anyToString(const std::any& value) return "unknown type"; } +// 自定义文件解析实现 +inline void ArgumentParser::expandArgumentsFromFile( + std::vector& argv) { + std::vector expandedArgs; + for (const auto& arg : argv) { + if (arg.starts_with(filePrefix_)) { + std::string filename = arg.substr(filePrefix_.length()); + std::ifstream infile(filename); + if (!infile.is_open()) { + THROW_INVALID_ARGUMENT("Unable to open argument file: " + + filename); + } + std::string line; + while (std::getline(infile, line)) { + std::istringstream iss(line); + std::string token; + while (std::getline(iss, token, fileDelimiter_)) { + if (!token.empty()) { + expandedArgs.emplace_back(token); + } + } + } + } else { + expandedArgs.emplace_back(arg); + } + } + argv = expandedArgs; +} + } // namespace atom::utils -#endif // ATOM_UTILS_ARGUMENT_PARSER_HPP +#endif // ATOM_UTILS_ARGUMENT_PARSER_HPP \ No newline at end of file diff --git a/src/atom/web/address.cpp b/src/atom/web/address.cpp index 7a511099..d10e89cf 100644 --- a/src/atom/web/address.cpp +++ b/src/atom/web/address.cpp @@ -22,6 +22,7 @@ Description: Enhanced Address class for IPv4, IPv6, and Unix domain sockets. #include "atom/log/loguru.hpp" +namespace atom::web { constexpr int IPV4_BIT_LENGTH = 32; constexpr int IPV6_SEGMENT_COUNT = 8; constexpr int IPV6_SEGMENT_BIT_LENGTH = 16; @@ -62,9 +63,7 @@ auto IPv4::parseCIDR(const std::string& cidr) -> bool { return true; } -void IPv4::printAddressType() const { - LOG_F(INFO, "Address type: IPv4"); -} +void IPv4::printAddressType() const { LOG_F(INFO, "Address type: IPv4"); } auto IPv4::isInRange(const std::string& start, const std::string& end) -> bool { uint32_t startIp = ipToInteger(start); @@ -170,9 +169,7 @@ auto IPv6::parseCIDR(const std::string& cidr) -> bool { return true; } -void IPv6::printAddressType() const { - LOG_F(INFO, "Address type: IPv6"); -} +void IPv6::printAddressType() const { LOG_F(INFO, "Address type: IPv6"); } auto IPv6::isInRange(const std::string& start, const std::string& end) -> bool { auto startIp = ipToVector(start); @@ -320,3 +317,4 @@ auto UnixDomain::isSameSubnet([[maybe_unused]] const Address& other, // 不适用 return false; } +} // namespace atom::web diff --git a/src/atom/web/address.hpp b/src/atom/web/address.hpp index d5a082e4..a1da9500 100644 --- a/src/atom/web/address.hpp +++ b/src/atom/web/address.hpp @@ -19,6 +19,7 @@ Description: Enhanced Address class for IPv4, IPv6, and Unix domain sockets. #include #include +namespace atom::web { /** * @class Address * @brief 基础类,表示通用的网络地址。 @@ -231,5 +232,6 @@ class UnixDomain : public Address { const Address& other, const std::string& mask) const -> bool override; [[nodiscard]] auto toHex() const -> std::string override; }; +} // namespace atom::web #endif // ATOM_WEB_ADDRESS_HPP diff --git a/src/script/checker.cpp b/src/script/checker.cpp index 5ef0d067..d5337f17 100644 --- a/src/script/checker.cpp +++ b/src/script/checker.cpp @@ -46,6 +46,9 @@ class ScriptAnalyzerImpl { std::vector dangers; detectScriptTypeAndAnalyze(script, dangers); suggestSafeReplacements(script, dangers); + detectExternalCommands(script, dangers); + detectEnvironmentVariables(script, dangers); + detectFileOperations(script, dangers); int complexity = calculateComplexity(script); generateReport(dangers, complexity, output_json, format); } catch (const std::exception& e) { @@ -77,6 +80,24 @@ class ScriptAnalyzerImpl { return config; } + static auto loadConfigFromDatabase(const std::string& db_file) -> json { + if (!atom::io::isFileExists(db_file)) { + THROW_FILE_NOT_FOUND("Database file not found: " + db_file); + } + std::ifstream file(db_file); + if (!file.is_open()) { + THROW_FAIL_TO_OPEN_FILE("Unable to open database file: " + db_file); + } + json db; + try { + file >> db; + } catch (const json::parse_error& e) { + THROW_INVALID_FORMAT("Invalid JSON format in database file: " + + db_file); + } + return db; + } + static auto isSkippableLine(const std::string& line) -> bool { return line.empty() || std::regex_match(line, std::regex(R"(^\s*#.*)")) || @@ -97,8 +118,16 @@ class ScriptAnalyzerImpl { "CMD Security Issue", dangers); } #else - checkPattern(script, config_["bash_danger_patterns"], - "Shell Script Security Issue", dangers); + if (detectPython(script)) { + checkPattern(script, config_["python_danger_patterns"], + "Python Script Security Issue", dangers); + } else if (detectRuby(script)) { + checkPattern(script, config_["ruby_danger_patterns"], + "Ruby Script Security Issue", dangers); + } else { + checkPattern(script, config_["bash_danger_patterns"], + "Shell Script Security Issue", dangers); + } #endif } @@ -106,6 +135,14 @@ class ScriptAnalyzerImpl { return script.contains("param(") || script.contains("$PSVersionTable"); } + static bool detectPython(const std::string& script) { + return script.contains("import ") || script.contains("def "); + } + + static bool detectRuby(const std::string& script) { + return script.contains("require ") || script.contains("def "); + } + void suggestSafeReplacements(const std::string& script, std::vector& dangers) { std::unordered_map replacements = { @@ -120,6 +157,34 @@ class ScriptAnalyzerImpl { checkReplacements(script, replacements, dangers); } + void detectExternalCommands(const std::string& script, + std::vector& dangers) { + std::unordered_set externalCommands = { +#ifdef _WIN32 + "Invoke-WebRequest", + "Invoke-RestMethod", +#else + "curl", + "wget", +#endif + }; + checkExternalCommands(script, externalCommands, dangers); + } + + void detectEnvironmentVariables(const std::string& script, + std::vector& dangers) { + std::regex envVarPattern(R"(\$\{?[A-Za-z_][A-Za-z0-9_]*\}?)"); + checkPattern(script, envVarPattern, "Environment Variable Usage", + dangers); + } + + void detectFileOperations(const std::string& script, + std::vector& dangers) { + std::regex fileOpPattern( + R"(\b(open|read|write|close|unlink|rename)\b)"); + checkPattern(script, fileOpPattern, "File Operation", dangers); + } + static auto calculateComplexity(const std::string& script) -> int { std::regex complexityPatterns(R"(if\b|while\b|for\b|case\b|&&|\|\|)"); std::istringstream scriptStream(script); @@ -224,6 +289,64 @@ class ScriptAnalyzerImpl { } } + static void checkPattern(const std::string& script, + const std::regex& pattern, + const std::string& category, + std::vector& dangers) { + std::unordered_set detectedIssues; + std::istringstream scriptStream(script); + std::string line; + int lineNum = 0; + + while (std::getline(scriptStream, line)) { + lineNum++; + if (isSkippableLine(line)) { + continue; + } + + if (std::regex_search(line, pattern)) { + std::string key = std::to_string(lineNum) + ":" + category; + if (!detectedIssues.contains(key)) { + dangers.emplace_back(DangerItem{ + category, line, "Detected usage", lineNum, {}}); + detectedIssues.insert(key); + } + } + } + } + + static void checkExternalCommands( + const std::string& script, + const std::unordered_set& externalCommands, + std::vector& dangers) { + std::istringstream scriptStream(script); + std::string line; + int lineNum = 0; + std::unordered_set detectedIssues; + + while (std::getline(scriptStream, line)) { + lineNum++; + if (isSkippableLine(line)) { + continue; + } + + for (const auto& command : externalCommands) { + if (line.find(command) != std::string::npos) { + std::string key = std::to_string(lineNum) + ":" + command; + if (!detectedIssues.contains(key)) { + dangers.emplace_back(DangerItem{ + "External Command", + line, + "Detected usage of external command: " + command, + lineNum, + {}}); + detectedIssues.insert(key); + } + } + } + } + } + static void checkReplacements( const std::string& script, const std::unordered_map& replacements, diff --git a/src/script/custom/shm.sh b/src/script/custom/shm.sh index e2b736f5..79815c6e 100644 --- a/src/script/custom/shm.sh +++ b/src/script/custom/shm.sh @@ -88,6 +88,65 @@ check_memory_usage() { log "Displayed current memory usage." } +# Mount shared memory segment +# This function allows the user to mount a shared memory segment to the file system. +mount_shm_segment() { + echo -n "Enter the shared memory segment ID to mount: " + read shm_id + echo -n "Enter the mount point (directory): " + read mount_point + if [ ! -d "$mount_point" ]; then + mkdir -p $mount_point + fi + mount -t tmpfs -o size=$(ipcs -m -i $shm_id | grep 'bytes' | awk '{print $5}') shm $mount_point + if [ $? -eq 0 ]; then + echo "Shared memory segment ID: $shm_id mounted to $mount_point." + log "Mounted shared memory segment ID: $shm_id to $mount_point." + else + echo "Failed to mount shared memory segment ID: $shm_id." + log "Failed to mount shared memory segment ID: $shm_id." + fi +} + +# Unmount shared memory segment +# This function allows the user to unmount a shared memory segment from the file system. +unmount_shm_segment() { + echo -n "Enter the mount point (directory) to unmount: " + read mount_point + umount $mount_point + if [ $? -eq 0 ]; then + echo "Unmounted shared memory segment from $mount_point." + log "Unmounted shared memory segment from $mount_point." + else + echo "Failed to unmount shared memory segment from $mount_point." + log "Failed to unmount shared memory segment from $mount_point." + fi +} + +# List all shared memory segments with details +# This function lists all shared memory segments with detailed information. +list_all_shm_segments() { + echo "Listing all shared memory segments with details:" + ipcs -m -p -t -a + log "Listed all shared memory segments with details." +} + +# Clean up unused shared memory segments +# This function cleans up all unused shared memory segments. +cleanup_unused_shm_segments() { + echo "Cleaning up unused shared memory segments..." + for shm_id in $(ipcs -m | awk '/^0x/ {print $2}'); do + ipcrm -m $shm_id + if [ $? -eq 0 ]; then + echo "Deleted unused shared memory segment ID: $shm_id." + log "Deleted unused shared memory segment ID: $shm_id." + else + echo "Failed to delete unused shared memory segment ID: $shm_id." + log "Failed to delete unused shared memory segment ID: $shm_id." + fi + done +} + # Show help # This function displays usage information for the script. show_help() { @@ -98,7 +157,11 @@ show_help() { echo "4. Change shared memory segment permissions - Change the permissions of a specific segment." echo "5. Batch delete shared memory segments - Delete multiple shared memory segments at once." echo "6. Check memory usage - Display current system memory usage." - echo "7. Help - Display this help information." + echo "7. Mount shared memory segment - Mount a shared memory segment to the file system." + echo "8. Unmount shared memory segment - Unmount a shared memory segment from the file system." + echo "9. List all shared memory segments - List all shared memory segments with details." + echo "10. Clean up unused shared memory segments - Clean up all unused shared memory segments." + echo "11. Help - Display this help information." } # Main menu @@ -111,8 +174,12 @@ while true; do echo "4. Change shared memory segment permissions" echo "5. Batch delete shared memory segments" echo "6. Check memory usage" - echo "7. Help" - echo "8. Exit" + echo "7. Mount shared memory segment" + echo "8. Unmount shared memory segment" + echo "9. List all shared memory segments" + echo "10. Clean up unused shared memory segments" + echo "11. Help" + echo "12. Exit" echo -n "Please select an option: " read choice # Read user choice @@ -123,8 +190,12 @@ while true; do 4) change_shm_permissions ;; 5) delete_multiple_shm_segments ;; 6) check_memory_usage ;; - 7) show_help ;; - 8) echo "Exiting"; log "Exited the script."; exit 0 ;; + 7) mount_shm_segment ;; + 8) unmount_shm_segment ;; + 9) list_all_shm_segments ;; + 10) cleanup_unused_shm_segments ;; + 11) show_help ;; + 12) echo "Exiting"; log "Exited the script."; exit 0 ;; *) echo "Invalid option, please try again." ;; esac -done +done \ No newline at end of file diff --git a/src/server/controller/ScriptController.hpp b/src/server/controller/ScriptController.hpp index 6fd1b291..7ba04c28 100644 --- a/src/server/controller/ScriptController.hpp +++ b/src/server/controller/ScriptController.hpp @@ -799,11 +799,11 @@ class ScriptController : public oatpp::web::server::api::ApiController { } \ }; - DEFINE_SCRIPT_GET_COROUTINE(ScriptJsonGetCoroutine, "json", + DEFINE_SCRIPT_GET_COROUTINE(ScriptJsonGetCoroutine, {"json"}, GetScriptJsonCoroutine) - DEFINE_SCRIPT_GET_COROUTINE(ScriptYamlGetCoroutine, "yaml", + DEFINE_SCRIPT_GET_COROUTINE(ScriptYamlGetCoroutine, {"yaml"}, GetScriptYamlCoroutine) - DEFINE_SCRIPT_GET_COROUTINE(ScriptXmlGetCoroutine, "xml", + DEFINE_SCRIPT_GET_COROUTINE(ScriptXmlGetCoroutine, {"xml"}, GetScriptXmlCoroutine) public: diff --git a/src/tools/libastro.cpp b/src/tools/libastro.cpp index 2d655446..37d739e0 100644 --- a/src/tools/libastro.cpp +++ b/src/tools/libastro.cpp @@ -1,7 +1,7 @@ #include "libastro.hpp" #include -namespace lithium { +namespace lithium::tools { namespace { diff --git a/src/tools/libastro.hpp b/src/tools/libastro.hpp index f5f5b1cc..fb1ef5b5 100644 --- a/src/tools/libastro.hpp +++ b/src/tools/libastro.hpp @@ -4,7 +4,7 @@ #include #include -namespace lithium { +namespace lithium::tools { constexpr double JD2000 = 2451545.0; constexpr double DEG_TO_RAD = std::numbers::pi / 180.0; diff --git a/tests/atom/algorithm/annealing.cpp b/tests/atom/algorithm/annealing.cpp new file mode 100644 index 00000000..00e2875c --- /dev/null +++ b/tests/atom/algorithm/annealing.cpp @@ -0,0 +1,73 @@ +#ifndef ATOM_ALGORITHM_TEST_ANNEALING_HPP +#define ATOM_ALGORITHM_TEST_ANNEALING_HPP + +#include +#include "atom/algorithm/annealing.hpp" + +// Test fixture for TSP tests +class TSPTest : public ::testing::Test { +protected: + void SetUp() override { + // Initialize cities for testing + cities_ = { + {0.0, 0.0}, + {1.0, 0.0}, + {1.0, 1.0}, + {0.0, 1.0} + }; + tsp_ = std::make_unique(cities_); + } + + std::vector> cities_; + std::unique_ptr tsp_; +}; + +// Test case for energy calculation with a valid solution +TEST_F(TSPTest, EnergyCalculationValidSolution) { + std::vector solution = {0, 1, 2, 3}; + double energy = tsp_->energy(solution); + double expected_energy = 4.0; // Perimeter of the square + EXPECT_DOUBLE_EQ(energy, expected_energy); +} + +// Test case for energy calculation with a different valid solution +TEST_F(TSPTest, EnergyCalculationDifferentSolution) { + std::vector solution = {0, 2, 1, 3}; + double energy = tsp_->energy(solution); + double expected_energy = 4.82842712474619; // Perimeter with diagonal + EXPECT_DOUBLE_EQ(energy, expected_energy); +} + +// Test case for energy calculation with an invalid solution (duplicate cities) +TEST_F(TSPTest, EnergyCalculationInvalidSolution) { + std::vector solution = {0, 1, 1, 3}; + double energy = tsp_->energy(solution); + // The energy calculation should still work, but the result may not be meaningful + EXPECT_TRUE(std::isfinite(energy)); +} + +// Test case for energy calculation with an empty solution +TEST_F(TSPTest, EnergyCalculationEmptySolution) { + std::vector solution = {}; + double energy = tsp_->energy(solution); + double expected_energy = 0.0; + EXPECT_DOUBLE_EQ(energy, expected_energy); +} + +// Test case for energy calculation with a single city +TEST_F(TSPTest, EnergyCalculationSingleCity) { + std::vector solution = {0}; + double energy = tsp_->energy(solution); + double expected_energy = 0.0; + EXPECT_DOUBLE_EQ(energy, expected_energy); +} + +// Test case for energy calculation with two cities +TEST_F(TSPTest, EnergyCalculationTwoCities) { + std::vector solution = {0, 1}; + double energy = tsp_->energy(solution); + double expected_energy = 2.0; // Distance from (0,0) to (1,0) and back + EXPECT_DOUBLE_EQ(energy, expected_energy); +} + +#endif // ATOM_ALGORITHM_TEST_ANNEALING_HPP \ No newline at end of file diff --git a/tests/atom/type/argsview.cpp b/tests/atom/type/argsview.cpp index f06fd1b6..eb0570f9 100644 --- a/tests/atom/type/argsview.cpp +++ b/tests/atom/type/argsview.cpp @@ -1,31 +1,27 @@ -#include "atom/type/argsview.hpp" - +#include "argsview.hpp" #include -// 测试 ArgsView 的构造函数 -TEST(ArgsViewTest, Constructor) { - ArgsView args(1, 2.5, "test"); - EXPECT_EQ(args.size(), 3); - EXPECT_EQ(args.get<0>(), 1); - EXPECT_EQ(args.get<1>(), 2.5); - EXPECT_EQ(args.get<2>(), "test"); +TEST(ArgsViewTest, ConstructorAndSize) { + ArgsView view(1, 2.0, "test"); + EXPECT_EQ(view.size(), 3); +} + +TEST(ArgsViewTest, Get) { + ArgsView view(1, 2.0, "test"); + EXPECT_EQ(view.get<0>(), 1); + EXPECT_EQ(view.get<1>(), 2.0); + EXPECT_EQ(view.get<2>(), "test"); } -// 测试 from tuple 的构造函数 -TEST(ArgsViewTest, ConstructorFromTuple) { - std::tuple tpl(1, 2.5, "test"); - ArgsView args(tpl); - EXPECT_EQ(args.size(), 3); - EXPECT_EQ(args.get<0>(), 1); - EXPECT_EQ(args.get<1>(), 2.5); - EXPECT_EQ(args.get<2>(), "test"); +TEST(ArgsViewTest, Empty) { + ArgsView<> view; + EXPECT_TRUE(view.empty()); } -// 测试 forEach 方法 TEST(ArgsViewTest, ForEach) { - ArgsView args(1, 2.5, "test"); + ArgsView view(1, 2.0, "test"); std::vector results; - args.forEach([&results](const auto& arg) { + view.forEach([&results](const auto& arg) { if constexpr (std::is_same_v, std::string>) { results.push_back(arg); @@ -35,108 +31,100 @@ TEST(ArgsViewTest, ForEach) { }); EXPECT_EQ(results.size(), 3); EXPECT_EQ(results[0], "1"); - EXPECT_EQ(results[1], "2.500000"); + EXPECT_EQ(results[1], "2.000000"); EXPECT_EQ(results[2], "test"); } -// 测试 transform 方法 TEST(ArgsViewTest, Transform) { - ArgsView args(1, 2.5); + ArgsView view(1, 2.0); auto transformed = - args.transform([](const auto& arg) { return std::to_string(arg); }); - EXPECT_EQ(transformed.size(), 2); + view.transform([](const auto& arg) { return std::to_string(arg); }); EXPECT_EQ(transformed.get<0>(), "1"); - EXPECT_EQ(transformed.get<1>(), "2.500000"); + EXPECT_EQ(transformed.get<1>(), "2.000000"); } -// 测试 accumulate 方法 TEST(ArgsViewTest, Accumulate) { - ArgsView args(1, 2, 3); - int sum = args.accumulate([](int a, int b) { return a + b; }, 0); + ArgsView view(1, 2, 3); + auto sum = view.accumulate([](int lhs, int rhs) { return lhs + rhs; }, 0); EXPECT_EQ(sum, 6); } -// 测试 apply 方法 TEST(ArgsViewTest, Apply) { - ArgsView args(1, 2.5); - auto result = args.apply( - [](const auto&... args) { return std::make_tuple(args...); }); - EXPECT_EQ(std::get<0>(result), 1); - EXPECT_EQ(std::get<1>(result), 2.5); + ArgsView view(1, 2.0); + auto result = view.apply([](const auto&... args) { return (args + ...); }); + EXPECT_EQ(result, 3.0); +} + +TEST(ArgsViewTest, Filter) { + ArgsView view(1, 2.0, 3); + auto filtered = view.filter([](const auto& arg) { return arg > 1; }); + EXPECT_EQ(filtered.size(), 3); + EXPECT_EQ(filtered.template get<0>(), std::nullopt); + EXPECT_EQ(filtered.template get<1>(), 2.0); + EXPECT_EQ(filtered.template get<2>(), 3); +} + +TEST(ArgsViewTest, Find) { + ArgsView view(1, 2.0, 3); + auto found = view.find([](const auto& arg) { return arg > 1; }); + EXPECT_EQ(found, 2.0); +} + +TEST(ArgsViewTest, Contains) { + ArgsView view(1, 2.0, 3); + EXPECT_TRUE(view.contains(2.0)); + EXPECT_FALSE(view.contains(4)); +} + +TEST(ArgsViewTest, SumFunction) { + auto result = sum(1, 2, 3); + EXPECT_EQ(result, 6); +} + +TEST(ArgsViewTest, ConcatFunction) { + constexpr double testValue = 3.0; + auto result = concat(1, "test", testValue); + EXPECT_EQ(result, "1test3.000000"); } -// 测试运算符== TEST(ArgsViewTest, EqualityOperator) { - ArgsView args1(1, 2.5); - ArgsView args2(1, 2.5); - ArgsView args3(1, 3.5); - EXPECT_TRUE(args1 == args2); - EXPECT_FALSE(args1 == args3); + ArgsView view1(1, 2.0); + ArgsView view2(1, 2.0); + EXPECT_TRUE(view1 == view2); } -// 测试运算符!= TEST(ArgsViewTest, InequalityOperator) { - ArgsView args1(1, 2.5); - ArgsView args2(1, 2.5); - ArgsView args3(1, 3.5); - EXPECT_FALSE(args1 != args2); - EXPECT_TRUE(args1 != args3); + ArgsView view1(1, 2.0); + ArgsView view2(1, 3.0); + EXPECT_TRUE(view1 != view2); } -// 测试运算符 < TEST(ArgsViewTest, LessThanOperator) { - ArgsView args1(1, 2.5); - ArgsView args2(1, 3.5); - EXPECT_TRUE(args1 < args2); - EXPECT_FALSE(args2 < args1); + ArgsView view1(1, 2.0); + ArgsView view2(1, 3.0); + EXPECT_TRUE(view1 < view2); } -// 测试运算符<= -TEST(ArgsViewTest, LessThanOrEqualOperator) { - ArgsView args1(1, 2.5); - ArgsView args2(1, 3.5); - ArgsView args3(1, 2.5); - EXPECT_TRUE(args1 <= args2); - EXPECT_TRUE(args1 <= args3); - EXPECT_FALSE(args2 <= args1); +TEST(ArgsViewTest, LessThanOrEqualToOperator) { + ArgsView view1(1, 2.0); + ArgsView view2(1, 2.0); + EXPECT_TRUE(view1 <= view2); } -// 测试运算符> TEST(ArgsViewTest, GreaterThanOperator) { - ArgsView args1(1, 3.5); - ArgsView args2(1, 2.5); - EXPECT_TRUE(args1 > args2); - EXPECT_FALSE(args2 > args1); + ArgsView view1(1, 3.0); + ArgsView view2(1, 2.0); + EXPECT_TRUE(view1 > view2); } -// 测试运算符>= -TEST(ArgsViewTest, GreaterThanOrEqualOperator) { - ArgsView args1(1, 3.5); - ArgsView args2(1, 2.5); - ArgsView args3(1, 3.5); - EXPECT_TRUE(args1 >= args2); - EXPECT_TRUE(args1 >= args3); - EXPECT_FALSE(args2 >= args1); +TEST(ArgsViewTest, GreaterThanOrEqualToOperator) { + ArgsView view1(1, 2.0); + ArgsView view2(1, 2.0); + EXPECT_TRUE(view1 >= view2); } -// 测试 hash 特化 TEST(ArgsViewTest, Hash) { - ArgsView args(1, 2.5); + ArgsView view(1, 2.0); std::hash> hasher; - EXPECT_NE(hasher(args), 0); -} - -#ifdef __DEBUG__ -TEST(ArgsViewTest, Print) { - std::ostringstream oss; - auto coutbuf = std::cout.rdbuf(oss.rdbuf()); - print(1, 2.5, "test"); - std::cout.rdbuf(coutbuf); - EXPECT_EQ(oss.str(), "1 2.5 test \n"); -} -#endif - -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} + EXPECT_NE(hasher(view), 0); +} \ No newline at end of file diff --git a/tests/atom/type/auto_table.cpp b/tests/atom/type/auto_table.cpp index 230dbd30..8d57383b 100644 --- a/tests/atom/type/auto_table.cpp +++ b/tests/atom/type/auto_table.cpp @@ -1,95 +1,127 @@ -#include "atom/type/auto_table.hpp" #include +#include "atom/type/auto_table.hpp" + using namespace atom::type; -// Test fixture for CountingHashTable -class CountingHashTableTest : public ::testing::Test { -protected: +TEST(CountingHashTableTest, InsertAndGet) { CountingHashTable table; - - void SetUp() override { - // Initialize table with some values - table.insert(1, "one"); - table.insert(2, "two"); - table.insert(3, "three"); - } -}; - -TEST_F(CountingHashTableTest, InsertTest) { - table.insert(4, "four"); - auto value = table.get(4); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(value.value(), "four"); + table.insert(1, "one"); + auto result = table.get(1); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result.value(), "one"); } -TEST_F(CountingHashTableTest, GetTest) { - auto value = table.get(1); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(value.value(), "one"); - - value = table.get(2); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(value.value(), "two"); - - value = table.get(3); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(value.value(), "three"); - - value = table.get(99); // non-existing key - EXPECT_FALSE(value.has_value()); +TEST(CountingHashTableTest, InsertBatchAndGetBatch) { + CountingHashTable table; + std::vector> items = {{1, "one"}, {2, "two"}}; + table.insertBatch(items); + auto results = table.getBatch({1, 2, 3}); + ASSERT_EQ(results.size(), 3); + EXPECT_EQ(results[0].value(), "one"); + EXPECT_EQ(results[1].value(), "two"); + EXPECT_FALSE(results[2].has_value()); } -TEST_F(CountingHashTableTest, EraseTest) { - bool erased = table.erase(2); - EXPECT_TRUE(erased); - auto value = table.get(2); - EXPECT_FALSE(value.has_value()); +TEST(CountingHashTableTest, GetAccessCount) { + CountingHashTable table; + table.insert(1, "one"); + table.get(1); + table.get(1); + auto count = table.getAccessCount(1); + ASSERT_TRUE(count.has_value()); + EXPECT_EQ(count.value(), 2); +} - erased = table.erase(99); // non-existing key - EXPECT_FALSE(erased); +TEST(CountingHashTableTest, Erase) { + CountingHashTable table; + table.insert(1, "one"); + EXPECT_TRUE(table.erase(1)); + EXPECT_FALSE(table.get(1).has_value()); } -TEST_F(CountingHashTableTest, ClearTest) { +TEST(CountingHashTableTest, Clear) { + CountingHashTable table; + table.insert(1, "one"); + table.insert(2, "two"); table.clear(); - auto entries = table.getAllEntries(); - EXPECT_TRUE(entries.empty()); + EXPECT_FALSE(table.get(1).has_value()); + EXPECT_FALSE(table.get(2).has_value()); } -TEST_F(CountingHashTableTest, GetAllEntriesTest) { +TEST(CountingHashTableTest, GetAllEntries) { + CountingHashTable table; + table.insert(1, "one"); + table.insert(2, "two"); auto entries = table.getAllEntries(); - ASSERT_EQ(entries.size(), 3); + ASSERT_EQ(entries.size(), 2); EXPECT_EQ(entries[0].second.value, "one"); EXPECT_EQ(entries[1].second.value, "two"); - EXPECT_EQ(entries[2].second.value, "three"); } -TEST_F(CountingHashTableTest, SortEntriesByCountDescTest) { +TEST(CountingHashTableTest, SortEntriesByCountDesc) { + CountingHashTable table; + table.insert(1, "one"); + table.insert(2, "two"); table.get(1); table.get(1); - table.get(3); + table.get(2); table.sortEntriesByCountDesc(); - auto entries = table.getAllEntries(); - ASSERT_EQ(entries.size(), 3); + ASSERT_EQ(entries.size(), 2); EXPECT_EQ(entries[0].second.value, "one"); - EXPECT_EQ(entries[1].second.value, "three"); - EXPECT_EQ(entries[2].second.value, "two"); + EXPECT_EQ(entries[1].second.value, "two"); } -TEST_F(CountingHashTableTest, AutoSortingTest) { +TEST(CountingHashTableTest, GetTopNEntries) { + CountingHashTable table; + table.insert(1, "one"); + table.insert(2, "two"); table.get(1); table.get(1); - table.get(3); + table.get(2); + auto topEntries = table.getTopNEntries(1); + ASSERT_EQ(topEntries.size(), 1); + EXPECT_EQ(topEntries[0].second.value, "one"); +} +TEST(CountingHashTableTest, AutoSorting) { + CountingHashTable table; + table.insert(1, "one"); + table.insert(2, "two"); + table.get(1); + table.get(1); + table.get(2); table.startAutoSorting(std::chrono::milliseconds(100)); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); table.stopAutoSorting(); - auto entries = table.getAllEntries(); - ASSERT_EQ(entries.size(), 3); + ASSERT_EQ(entries.size(), 2); EXPECT_EQ(entries[0].second.value, "one"); - EXPECT_EQ(entries[1].second.value, "three"); - EXPECT_EQ(entries[2].second.value, "two"); + EXPECT_EQ(entries[1].second.value, "two"); +} + +TEST(CountingHashTableTest, SerializeToJson) { + CountingHashTable table; + table.insert(1, "one"); + table.insert(2, "two"); + auto json = table.serializeToJson(); + EXPECT_EQ(json.size(), 2); + EXPECT_EQ(json[0]["value"], "one"); + EXPECT_EQ(json[1]["value"], "two"); } + +TEST(CountingHashTableTest, DeserializeFromJson) { + CountingHashTable table; + nlohmann::json json = {{{"key", 1}, {"value", "one"}, {"count", 2}}, + {{"key", 2}, {"value", "two"}, {"count", 1}}}; + table.deserializeFromJson(json); + auto result1 = table.get(1); + auto result2 = table.get(2); + ASSERT_TRUE(result1.has_value()); + ASSERT_TRUE(result2.has_value()); + EXPECT_EQ(result1.value(), "one"); + EXPECT_EQ(result2.value(), "two"); + EXPECT_EQ(table.getAccessCount(1).value(), 2); + EXPECT_EQ(table.getAccessCount(2).value(), 1); +} \ No newline at end of file diff --git a/tests/atom/type/expected.cpp b/tests/atom/type/expected.cpp index c540434c..05968cf0 100644 --- a/tests/atom/type/expected.cpp +++ b/tests/atom/type/expected.cpp @@ -4,146 +4,173 @@ using namespace atom::type; -// 测试expected 的基础功能 -TEST(ExpectedTest, BasicFunctionality) { - // 测试成功情况 - expected success(42); - EXPECT_TRUE(success.has_value()); - EXPECT_EQ(success.value(), 42); +// Test fixture for expected class +template +class ExpectedTest : public ::testing::Test { +protected: + expected value_expected; + expected error_expected; + + ExpectedTest() : value_expected(T{}), error_expected(Error("error")) {} +}; + +// Test fixture for expected specialization +template +class ExpectedVoidTest : public ::testing::Test { +protected: + expected value_expected; + expected error_expected; + + ExpectedVoidTest() : value_expected(), error_expected(Error("error")) {} +}; + +// Test cases for expected +using ExpectedIntTest = ExpectedTest; + +TEST_F(ExpectedIntTest, DefaultConstructor) { + expected e; + EXPECT_TRUE(e.has_value()); + EXPECT_EQ(e.value(), 0); +} + +TEST_F(ExpectedIntTest, ValueConstructor) { + expected e(42); + EXPECT_TRUE(e.has_value()); + EXPECT_EQ(e.value(), 42); +} + +TEST_F(ExpectedIntTest, ErrorConstructor) { + expected e(Error("error")); + EXPECT_FALSE(e.has_value()); + EXPECT_EQ(e.error().error(), "error"); +} + +TEST_F(ExpectedIntTest, UnexpectedConstructor) { + expected e(unexpected("error")); + EXPECT_FALSE(e.has_value()); + EXPECT_EQ(e.error().error(), "error"); +} + +TEST_F(ExpectedIntTest, CopyConstructor) { + expected e1(42); + expected e2(e1); + EXPECT_TRUE(e2.has_value()); + EXPECT_EQ(e2.value(), 42); +} + +TEST_F(ExpectedIntTest, MoveConstructor) { + expected e1(42); + expected e2(std::move(e1)); + EXPECT_TRUE(e2.has_value()); + EXPECT_EQ(e2.value(), 42); +} + +TEST_F(ExpectedIntTest, CopyAssignment) { + expected e1(42); + expected e2; + e2 = e1; + EXPECT_TRUE(e2.has_value()); + EXPECT_EQ(e2.value(), 42); +} + +TEST_F(ExpectedIntTest, MoveAssignment) { + expected e1(42); + expected e2; + e2 = std::move(e1); + EXPECT_TRUE(e2.has_value()); + EXPECT_EQ(e2.value(), 42); +} + +TEST_F(ExpectedIntTest, AndThen) { + auto result = + value_expected.and_then([](int& v) { return expected(v + 1); }); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(result.value(), 1); + + result = + error_expected.and_then([](int& v) { return expected(v + 1); }); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error().error(), "error"); +} + +TEST_F(ExpectedIntTest, Map) { + auto result = value_expected.map([](int& v) { return v + 1; }); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(result.value(), 1); + + result = error_expected.map([](int& v) { return v + 1; }); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error().error(), "error"); +} + +/* +TEST_F(ExpectedIntTest, TransformError) { + auto result = error_expected.transform_error([](const std::string& e) { +return Error(e + " transformed"); }); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error().error(), "error transformed"); +} +*/ + +// Test cases for expected +using ExpectedVoidStringTest = ExpectedVoidTest<>; + +TEST_F(ExpectedVoidStringTest, DefaultConstructor) { + expected e; + EXPECT_TRUE(e.has_value()); +} + +TEST_F(ExpectedVoidStringTest, ErrorConstructor) { + expected e(Error("error")); + EXPECT_FALSE(e.has_value()); + EXPECT_EQ(e.error().error(), "error"); +} + +TEST_F(ExpectedVoidStringTest, UnexpectedConstructor) { + expected e(unexpected("error")); + EXPECT_FALSE(e.has_value()); + EXPECT_EQ(e.error().error(), "error"); +} + +TEST_F(ExpectedVoidStringTest, CopyConstructor) { + expected e1; + expected e2(e1); + EXPECT_TRUE(e2.has_value()); +} + +TEST_F(ExpectedVoidStringTest, MoveConstructor) { + expected e1; + expected e2(std::move(e1)); + EXPECT_TRUE(e2.has_value()); +} + +TEST_F(ExpectedVoidStringTest, CopyAssignment) { + expected e1; + expected e2; + e2 = e1; + EXPECT_TRUE(e2.has_value()); +} + +TEST_F(ExpectedVoidStringTest, MoveAssignment) { + expected e1; + expected e2; + e2 = std::move(e1); + EXPECT_TRUE(e2.has_value()); +} - // 测试错误情况 - expected failure(make_unexpected("error")); - EXPECT_FALSE(failure.has_value()); - EXPECT_EQ(failure.error().error(), "error"); -} - -// 测试expected 的基础功能 -TEST(ExpectedTest, VoidTypeFunctionality) { - // 测试成功情况 - expected success; - EXPECT_TRUE(success.has_value()); +TEST_F(ExpectedVoidStringTest, AndThen) { + auto result = value_expected.and_then([]() { return expected(); }); + EXPECT_TRUE(result.has_value()); - // 测试错误情况 - expected failure(make_unexpected("void error")); - EXPECT_FALSE(failure.has_value()); - EXPECT_EQ(failure.error().error(), "void error"); - - // 测试value_or功能 - bool lambda_called = false; - failure.value_or([&](std::string err) { - lambda_called = true; - EXPECT_EQ(err, "void error"); - }); - EXPECT_TRUE(lambda_called); -} - -// 测试错误比较和处理 -TEST(ExpectedTest, ErrorComparison) { - Error error1("Error1"); - Error error2("Error2"); - - EXPECT_EQ(error1, Error("Error1")); - EXPECT_NE(error1, error2); -} - -// 测试map功能 -TEST(ExpectedTest, MapFunctionality) { - expected success(10); - auto mapped = success.map([](int value) { return value * 2; }); - - EXPECT_TRUE(mapped.has_value()); - EXPECT_EQ(mapped.value(), 20); - - expected failure(make_unexpected("map error")); - auto mapped_failure = failure.map([](int value) { return value * 2; }); - - EXPECT_FALSE(mapped_failure.has_value()); - EXPECT_EQ(mapped_failure.error().error(), "map error"); -} - -// 测试and_then功能 -TEST(ExpectedTest, AndThenFunctionality) { - expected success(10); - auto chained = - success.and_then([](int value) { return make_expected(value + 5); }); - - EXPECT_TRUE(chained.has_value()); - EXPECT_EQ(chained.value(), 15); - - expected failure(make_unexpected("and_then error")); - auto chained_failure = - failure.and_then([](int value) { return make_expected(value + 5); }); - - EXPECT_FALSE(chained_failure.has_value()); - EXPECT_EQ(chained_failure.error().error(), "and_then error"); -} - -// 测试边缘情况:空字符串错误 -TEST(ExpectedTest, EmptyStringError) { - expected failure(make_unexpected("")); - EXPECT_FALSE(failure.has_value()); - EXPECT_EQ(failure.error().error(), ""); - - bool lambda_called = false; - int result = failure.value_or([&](std::string err) { - lambda_called = true; - EXPECT_EQ(err, ""); - return 0; - }); - EXPECT_TRUE(lambda_called); - EXPECT_EQ(result, 0); -} - -// 测试边缘情况:传递const char*的错误 -TEST(ExpectedTest, ConstCharError) { - expected failure(make_unexpected("const char* error")); - EXPECT_FALSE(failure.has_value()); - EXPECT_EQ(failure.error().error(), "const char* error"); -} - -// 测试异常情况:访问错误的value -TEST(ExpectedTest, AccessErrorInsteadOfValue) { - expected failure(make_unexpected("access error")); - - EXPECT_THROW( - { - try { - [[maybe_unused]] int value = failure.value(); - } catch (const std::logic_error& e) { - EXPECT_STREQ( - "Attempted to access value, but it contains an error.", - e.what()); - throw; - } - }, - std::logic_error); -} - -// 测试异常情况:访问value时的错误 -TEST(ExpectedTest, AccessValueInsteadOfError) { - expected success(42); - - EXPECT_THROW( - { - try { - auto error = success.error(); - } catch (const std::logic_error& e) { - EXPECT_STREQ( - "Attempted to access error, but it contains a value.", - e.what()); - throw; - } - }, - std::logic_error); + result = error_expected.and_then([]() { return expected(); }); + EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error().error(), "error"); } -// 测试不同类型的错误 -TEST(ExpectedTest, DifferentErrorTypes) { - expected int_error(make_unexpected(404)); - EXPECT_FALSE(int_error.has_value()); - EXPECT_EQ(int_error.error().error(), 404); - - expected string_error(make_unexpected("error message")); - EXPECT_FALSE(string_error.has_value()); - EXPECT_EQ(string_error.error().error(), "error message"); +/* +TEST_F(ExpectedVoidStringTest, TransformError) { + auto result = error_expected.transform_error([](const std::string& e) { +return e + " transformed"; }); EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result.error().error(), "error transformed"); } +*/