From 58db5f47dd83a144bcda3c25cff97eda29dc3de1 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Wed, 24 Jul 2024 11:48:04 +0300 Subject: [PATCH 01/11] Stubs refactoring --- directio/{directio_windows.go => directio_stubs.go} | 3 +++ env/{env_windows.go => env_stubs.go} | 3 +++ fsutil/{fs_windows.go => fs_stubs.go} | 3 +++ fsutil/{helpers_windows.go => helpers_stubs.go} | 3 +++ fsutil/list.go | 4 ++-- fsutil/{list_windows.go => list_stubs.go} | 3 +++ fsutil/walker.go | 4 ++-- fsutil/{walker_windows.go => walker_stubs.go} | 3 +++ initsystem/{initsystem_windows.go => initsystem_stubs.go} | 3 +++ initsystem/sdnotify/sdnotify_stubs.go | 4 ++-- lock/{lock_windows.go => lock_stubs.go} | 3 +++ netutil/{netutil_windows.go => netutil_stubs.go} | 3 +++ pager/{pager_windows.go => pager_stubs.go} | 3 +++ path/{path_windows.go => path_stubs.go} | 3 +++ pid/{pid_windows.go => pid_stubs.go} | 3 +++ secstr/{secure_string.go => secstring.go} | 0 secstr/{secure_string_windows.go => secstring_stubs.go} | 3 +++ secstr/{secure_string_test.go => secstring_test.go} | 0 signal/{signal_windows.go => signal_stubs.go} | 3 +++ system/exec/exec.go | 4 ++-- system/exec/{exec_windows.go => exec_stubs.go} | 3 +++ system/{info_windows.go => info_stubs.go} | 3 +++ system/process/{process_windows.go => process_stubs.go} | 3 +++ system/process/{processes_windows.go => processes_stubs.go} | 3 +++ .../procname/{setprocname_windows.go => setprocname_stubs.go} | 3 +++ system/sensors/{sensors_windows.go => sensors_stubs.go} | 3 +++ system/{user_windows.go => user_stubs.go} | 3 +++ terminal/input/{input_windows.go => input_stubs.go} | 3 +++ terminal/tty/{tty_windows.go => tty_stubs.go} | 3 +++ 29 files changed, 77 insertions(+), 8 deletions(-) rename directio/{directio_windows.go => directio_stubs.go} (95%) rename env/{env_windows.go => env_stubs.go} (91%) rename fsutil/{fs_windows.go => fs_stubs.go} (98%) rename fsutil/{helpers_windows.go => helpers_stubs.go} (94%) rename fsutil/{list_windows.go => list_stubs.go} (97%) rename fsutil/{walker_windows.go => walker_stubs.go} (93%) rename initsystem/{initsystem_windows.go => initsystem_stubs.go} (96%) rename lock/{lock_windows.go => lock_stubs.go} (96%) rename netutil/{netutil_windows.go => netutil_stubs.go} (94%) rename pager/{pager_windows.go => pager_stubs.go} (96%) rename path/{path_windows.go => path_stubs.go} (97%) rename pid/{pid_windows.go => pid_stubs.go} (92%) rename secstr/{secure_string.go => secstring.go} (100%) rename secstr/{secure_string_windows.go => secstring_stubs.go} (97%) rename secstr/{secure_string_test.go => secstring_test.go} (100%) rename signal/{signal_windows.go => signal_stubs.go} (97%) rename system/exec/{exec_windows.go => exec_stubs.go} (93%) rename system/{info_windows.go => info_stubs.go} (98%) rename system/process/{process_windows.go => process_stubs.go} (99%) rename system/process/{processes_windows.go => processes_stubs.go} (96%) rename system/procname/{setprocname_windows.go => setprocname_stubs.go} (96%) rename system/sensors/{sensors_windows.go => sensors_stubs.go} (96%) rename system/{user_windows.go => user_stubs.go} (98%) rename terminal/input/{input_windows.go => input_stubs.go} (98%) rename terminal/tty/{tty_windows.go => tty_stubs.go} (95%) diff --git a/directio/directio_windows.go b/directio/directio_stubs.go similarity index 95% rename from directio/directio_windows.go rename to directio/directio_stubs.go index 08f3a0be..95941c65 100644 --- a/directio/directio_windows.go +++ b/directio/directio_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + // Package directio provides methods for reading/writing files with direct io package directio diff --git a/env/env_windows.go b/env/env_stubs.go similarity index 91% rename from env/env_windows.go rename to env/env_stubs.go index f95ae8d2..8094dcc5 100644 --- a/env/env_windows.go +++ b/env/env_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin || !freebsd +// +build !linux !darwin !freebsd + // Package env provides methods for working with environment variables package env diff --git a/fsutil/fs_windows.go b/fsutil/fs_stubs.go similarity index 98% rename from fsutil/fs_windows.go rename to fsutil/fs_stubs.go index 4b90f703..1caa501e 100644 --- a/fsutil/fs_windows.go +++ b/fsutil/fs_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin || !freebsd +// +build !linux !darwin !freebsd + // Package fsutil provides methods for working with files on POSIX compatible systems package fsutil diff --git a/fsutil/helpers_windows.go b/fsutil/helpers_stubs.go similarity index 94% rename from fsutil/helpers_windows.go rename to fsutil/helpers_stubs.go index 07d1b766..81ab0de8 100644 --- a/fsutil/helpers_windows.go +++ b/fsutil/helpers_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin || !freebsd +// +build !linux !darwin !freebsd + package fsutil // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/fsutil/list.go b/fsutil/list.go index 93ac7321..8d0963e2 100644 --- a/fsutil/list.go +++ b/fsutil/list.go @@ -1,5 +1,5 @@ -//go:build !windows -// +build !windows +//go:build linux || darwin || freebsd +// +build linux darwin freebsd package fsutil diff --git a/fsutil/list_windows.go b/fsutil/list_stubs.go similarity index 97% rename from fsutil/list_windows.go rename to fsutil/list_stubs.go index f5b43f34..599def7c 100644 --- a/fsutil/list_windows.go +++ b/fsutil/list_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin || !freebsd +// +build !linux !darwin !freebsd + package fsutil // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/fsutil/walker.go b/fsutil/walker.go index d2dc032d..ad7f1620 100644 --- a/fsutil/walker.go +++ b/fsutil/walker.go @@ -1,5 +1,5 @@ -//go:build !windows -// +build !windows +//go:build linux || darwin || freebsd +// +build linux darwin freebsd package fsutil diff --git a/fsutil/walker_windows.go b/fsutil/walker_stubs.go similarity index 93% rename from fsutil/walker_windows.go rename to fsutil/walker_stubs.go index 750bfbd8..39830492 100644 --- a/fsutil/walker_windows.go +++ b/fsutil/walker_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + package fsutil // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/initsystem/initsystem_windows.go b/initsystem/initsystem_stubs.go similarity index 96% rename from initsystem/initsystem_windows.go rename to initsystem/initsystem_stubs.go index 68e4ac20..8e6b52d5 100644 --- a/initsystem/initsystem_windows.go +++ b/initsystem/initsystem_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + // Package initsystem provides methods for working with different init systems package initsystem diff --git a/initsystem/sdnotify/sdnotify_stubs.go b/initsystem/sdnotify/sdnotify_stubs.go index a9bfce4a..7fdfb03b 100644 --- a/initsystem/sdnotify/sdnotify_stubs.go +++ b/initsystem/sdnotify/sdnotify_stubs.go @@ -1,5 +1,5 @@ -//go:build windows || darwin -// +build windows darwin +//go:build !linux +// +build !linux // Package sdnotify provides methods for sending notifications to systemd package sdnotify diff --git a/lock/lock_windows.go b/lock/lock_stubs.go similarity index 96% rename from lock/lock_windows.go rename to lock/lock_stubs.go index 9fa3168b..0dff2750 100644 --- a/lock/lock_windows.go +++ b/lock/lock_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + // Package lock provides methods for working with lock files package lock diff --git a/netutil/netutil_windows.go b/netutil/netutil_stubs.go similarity index 94% rename from netutil/netutil_windows.go rename to netutil/netutil_stubs.go index c21dc1ca..10adfcb5 100644 --- a/netutil/netutil_windows.go +++ b/netutil/netutil_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + // Package netutil provides methods for working with network package netutil diff --git a/pager/pager_windows.go b/pager/pager_stubs.go similarity index 96% rename from pager/pager_windows.go rename to pager/pager_stubs.go index 11539973..c0b4b6c8 100644 --- a/pager/pager_windows.go +++ b/pager/pager_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + // Package pager provides methods for pager setup (more/less) package pager diff --git a/path/path_windows.go b/path/path_stubs.go similarity index 97% rename from path/path_windows.go rename to path/path_stubs.go index 60fc5776..969895ee 100644 --- a/path/path_windows.go +++ b/path/path_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin || !freebsd +// +build !linux !darwin !freebsd + // Package path provides methods for working with paths (fully compatible with base path package) package path diff --git a/pid/pid_windows.go b/pid/pid_stubs.go similarity index 92% rename from pid/pid_windows.go rename to pid/pid_stubs.go index 8f09e0b7..3e5b13ba 100644 --- a/pid/pid_windows.go +++ b/pid/pid_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin || !freebsd +// +build !linux !darwin !freebsd + // Package pid provides methods for working with PID files package pid diff --git a/secstr/secure_string.go b/secstr/secstring.go similarity index 100% rename from secstr/secure_string.go rename to secstr/secstring.go diff --git a/secstr/secure_string_windows.go b/secstr/secstring_stubs.go similarity index 97% rename from secstr/secure_string_windows.go rename to secstr/secstring_stubs.go index 5bd7766e..6dddb675 100644 --- a/secstr/secure_string_windows.go +++ b/secstr/secstring_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux +// +build !linux + // Package secstr provides methods and structs for working with protected (secure) strings package secstr diff --git a/secstr/secure_string_test.go b/secstr/secstring_test.go similarity index 100% rename from secstr/secure_string_test.go rename to secstr/secstring_test.go diff --git a/signal/signal_windows.go b/signal/signal_stubs.go similarity index 97% rename from signal/signal_windows.go rename to signal/signal_stubs.go index 633434aa..8a1c8aae 100644 --- a/signal/signal_windows.go +++ b/signal/signal_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + package signal // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/system/exec/exec.go b/system/exec/exec.go index d621acbb..fe490c44 100644 --- a/system/exec/exec.go +++ b/system/exec/exec.go @@ -1,5 +1,5 @@ -//go:build !windows -// +build !windows +//go:build linux || darwin || freebsd +// +build linux darwin freebsd // Package exec provides methods for executing commands package exec diff --git a/system/exec/exec_windows.go b/system/exec/exec_stubs.go similarity index 93% rename from system/exec/exec_windows.go rename to system/exec/exec_stubs.go index d19a4e0e..5984603e 100644 --- a/system/exec/exec_windows.go +++ b/system/exec/exec_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin || !freebsd +// +build !linux !darwin !freebsd + // Package exec provides methods for executing commands package exec diff --git a/system/info_windows.go b/system/info_stubs.go similarity index 98% rename from system/info_windows.go rename to system/info_stubs.go index 3600bfd3..708a1da8 100644 --- a/system/info_windows.go +++ b/system/info_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + // Package system provides methods for working with system data (metrics/users) package system diff --git a/system/process/process_windows.go b/system/process/process_stubs.go similarity index 99% rename from system/process/process_windows.go rename to system/process/process_stubs.go index badfb4cb..4e237785 100644 --- a/system/process/process_windows.go +++ b/system/process/process_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + package process // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/system/process/processes_windows.go b/system/process/processes_stubs.go similarity index 96% rename from system/process/processes_windows.go rename to system/process/processes_stubs.go index 99877873..3d95c09d 100644 --- a/system/process/processes_windows.go +++ b/system/process/processes_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + package process // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/system/procname/setprocname_windows.go b/system/procname/setprocname_stubs.go similarity index 96% rename from system/procname/setprocname_windows.go rename to system/procname/setprocname_stubs.go index d4e4699b..4c955c09 100644 --- a/system/procname/setprocname_windows.go +++ b/system/procname/setprocname_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + package procname // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/system/sensors/sensors_windows.go b/system/sensors/sensors_stubs.go similarity index 96% rename from system/sensors/sensors_windows.go rename to system/sensors/sensors_stubs.go index ab662833..3622e91c 100644 --- a/system/sensors/sensors_windows.go +++ b/system/sensors/sensors_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + // Package sensors provide methods for collecting sensors information package sensors diff --git a/system/user_windows.go b/system/user_stubs.go similarity index 98% rename from system/user_windows.go rename to system/user_stubs.go index 4e9dacaf..5f64e379 100644 --- a/system/user_windows.go +++ b/system/user_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + package system // ////////////////////////////////////////////////////////////////////////////////// // diff --git a/terminal/input/input_windows.go b/terminal/input/input_stubs.go similarity index 98% rename from terminal/input/input_windows.go rename to terminal/input/input_stubs.go index 02600b1d..151ef810 100644 --- a/terminal/input/input_windows.go +++ b/terminal/input/input_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + // Package input provides methods for reading user input package input diff --git a/terminal/tty/tty_windows.go b/terminal/tty/tty_stubs.go similarity index 95% rename from terminal/tty/tty_windows.go rename to terminal/tty/tty_stubs.go index a5af45d2..9944e339 100644 --- a/terminal/tty/tty_windows.go +++ b/terminal/tty/tty_stubs.go @@ -1,3 +1,6 @@ +//go:build !linux || !darwin +// +build !linux !darwin + package tty // ////////////////////////////////////////////////////////////////////////////////// // From 790fd98786460b21d7c6ea14a0bc118f75b76d7d Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Wed, 24 Jul 2024 11:58:41 +0300 Subject: [PATCH 02/11] Stubs refactoring --- directio/directio_stubs.go | 4 ++-- env/env_stubs.go | 4 ++-- fsutil/fs_stubs.go | 4 ++-- fsutil/helpers_stubs.go | 4 ++-- fsutil/list_stubs.go | 4 ++-- fsutil/walker_stubs.go | 4 ++-- initsystem/initsystem_stubs.go | 4 ++-- lock/lock_stubs.go | 4 ++-- netutil/netutil_stubs.go | 4 ++-- pager/pager_posix.go | 4 ++-- pager/pager_stubs.go | 4 ++-- path/path_stubs.go | 4 ++-- pid/pid_stubs.go | 4 ++-- signal/signal_stubs.go | 4 ++-- system/exec/exec_stubs.go | 4 ++-- system/info_stubs.go | 4 ++-- system/process/process_stubs.go | 4 ++-- system/process/processes_stubs.go | 4 ++-- system/procname/setprocname_stubs.go | 4 ++-- system/sensors/sensors_stubs.go | 4 ++-- system/user_stubs.go | 4 ++-- terminal/input/input_stubs.go | 4 ++-- terminal/tty/tty_stubs.go | 4 ++-- 23 files changed, 46 insertions(+), 46 deletions(-) diff --git a/directio/directio_stubs.go b/directio/directio_stubs.go index 95941c65..6d712245 100644 --- a/directio/directio_stubs.go +++ b/directio/directio_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin // Package directio provides methods for reading/writing files with direct io package directio diff --git a/env/env_stubs.go b/env/env_stubs.go index 8094dcc5..1aa77cab 100644 --- a/env/env_stubs.go +++ b/env/env_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin || !freebsd -// +build !linux !darwin !freebsd +//go:build !linux && !darwin && !freebsd +// +build !linux,!darwin,!freebsd // Package env provides methods for working with environment variables package env diff --git a/fsutil/fs_stubs.go b/fsutil/fs_stubs.go index 1caa501e..baa874f1 100644 --- a/fsutil/fs_stubs.go +++ b/fsutil/fs_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin || !freebsd -// +build !linux !darwin !freebsd +//go:build !linux && !darwin && !freebsd +// +build !linux,!darwin,!freebsd // Package fsutil provides methods for working with files on POSIX compatible systems package fsutil diff --git a/fsutil/helpers_stubs.go b/fsutil/helpers_stubs.go index 81ab0de8..f383ffc9 100644 --- a/fsutil/helpers_stubs.go +++ b/fsutil/helpers_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin || !freebsd -// +build !linux !darwin !freebsd +//go:build !linux && !darwin && !freebsd +// +build !linux,!darwin,!freebsd package fsutil diff --git a/fsutil/list_stubs.go b/fsutil/list_stubs.go index 599def7c..11093588 100644 --- a/fsutil/list_stubs.go +++ b/fsutil/list_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin || !freebsd -// +build !linux !darwin !freebsd +//go:build !linux && !darwin && !freebsd +// +build !linux,!darwin,!freebsd package fsutil diff --git a/fsutil/walker_stubs.go b/fsutil/walker_stubs.go index 39830492..68daf86a 100644 --- a/fsutil/walker_stubs.go +++ b/fsutil/walker_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin package fsutil diff --git a/initsystem/initsystem_stubs.go b/initsystem/initsystem_stubs.go index 8e6b52d5..a0c6e220 100644 --- a/initsystem/initsystem_stubs.go +++ b/initsystem/initsystem_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin // Package initsystem provides methods for working with different init systems package initsystem diff --git a/lock/lock_stubs.go b/lock/lock_stubs.go index 0dff2750..f3ad36e1 100644 --- a/lock/lock_stubs.go +++ b/lock/lock_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin // Package lock provides methods for working with lock files package lock diff --git a/netutil/netutil_stubs.go b/netutil/netutil_stubs.go index 10adfcb5..f9d8a918 100644 --- a/netutil/netutil_stubs.go +++ b/netutil/netutil_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin // Package netutil provides methods for working with network package netutil diff --git a/pager/pager_posix.go b/pager/pager_posix.go index 235733aa..d6de03a3 100644 --- a/pager/pager_posix.go +++ b/pager/pager_posix.go @@ -1,5 +1,5 @@ -//go:build !windows -// +build !windows +//go:build linux || darwin || freebsd +// +build linux darwin freebsd // Package pager provides methods for pager setup (more/less) package pager diff --git a/pager/pager_stubs.go b/pager/pager_stubs.go index c0b4b6c8..5535b97f 100644 --- a/pager/pager_stubs.go +++ b/pager/pager_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin && !freebsd +// +build !linux,!darwin,!freebsd // Package pager provides methods for pager setup (more/less) package pager diff --git a/path/path_stubs.go b/path/path_stubs.go index 969895ee..2f297fec 100644 --- a/path/path_stubs.go +++ b/path/path_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin || !freebsd -// +build !linux !darwin !freebsd +//go:build !linux && !darwin && !freebsd +// +build !linux,!darwin,!freebsd // Package path provides methods for working with paths (fully compatible with base path package) package path diff --git a/pid/pid_stubs.go b/pid/pid_stubs.go index 3e5b13ba..9c2f8ecc 100644 --- a/pid/pid_stubs.go +++ b/pid/pid_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin || !freebsd -// +build !linux !darwin !freebsd +//go:build !linux && !darwin && !freebsd +// +build !linux,!darwin,!freebsd // Package pid provides methods for working with PID files package pid diff --git a/signal/signal_stubs.go b/signal/signal_stubs.go index 8a1c8aae..532a5159 100644 --- a/signal/signal_stubs.go +++ b/signal/signal_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin package signal diff --git a/system/exec/exec_stubs.go b/system/exec/exec_stubs.go index 5984603e..c8bf7069 100644 --- a/system/exec/exec_stubs.go +++ b/system/exec/exec_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin || !freebsd -// +build !linux !darwin !freebsd +//go:build !linux && !darwin && !freebsd +// +build !linux,!darwin,!freebsd // Package exec provides methods for executing commands package exec diff --git a/system/info_stubs.go b/system/info_stubs.go index 708a1da8..cbe79200 100644 --- a/system/info_stubs.go +++ b/system/info_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin // Package system provides methods for working with system data (metrics/users) package system diff --git a/system/process/process_stubs.go b/system/process/process_stubs.go index 4e237785..e93bde5a 100644 --- a/system/process/process_stubs.go +++ b/system/process/process_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin package process diff --git a/system/process/processes_stubs.go b/system/process/processes_stubs.go index 3d95c09d..41451825 100644 --- a/system/process/processes_stubs.go +++ b/system/process/processes_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin package process diff --git a/system/procname/setprocname_stubs.go b/system/procname/setprocname_stubs.go index 4c955c09..d3ca307b 100644 --- a/system/procname/setprocname_stubs.go +++ b/system/procname/setprocname_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin package procname diff --git a/system/sensors/sensors_stubs.go b/system/sensors/sensors_stubs.go index 3622e91c..ad91bcf4 100644 --- a/system/sensors/sensors_stubs.go +++ b/system/sensors/sensors_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin // Package sensors provide methods for collecting sensors information package sensors diff --git a/system/user_stubs.go b/system/user_stubs.go index 5f64e379..0b7ada36 100644 --- a/system/user_stubs.go +++ b/system/user_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin package system diff --git a/terminal/input/input_stubs.go b/terminal/input/input_stubs.go index 151ef810..f5d79924 100644 --- a/terminal/input/input_stubs.go +++ b/terminal/input/input_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin // Package input provides methods for reading user input package input diff --git a/terminal/tty/tty_stubs.go b/terminal/tty/tty_stubs.go index 9944e339..8d259b4c 100644 --- a/terminal/tty/tty_stubs.go +++ b/terminal/tty/tty_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux || !darwin -// +build !linux !darwin +//go:build !linux && !darwin +// +build !linux,!darwin package tty From aac09d60789d63243de31e19388b6d2bc94ace9c Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Wed, 24 Jul 2024 12:02:49 +0300 Subject: [PATCH 03/11] Stubs refactoring --- secstr/secstring.go | 4 ++-- secstr/secstring_stubs.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/secstr/secstring.go b/secstr/secstring.go index a2dfe567..a186fcb3 100644 --- a/secstr/secstring.go +++ b/secstr/secstring.go @@ -1,5 +1,5 @@ -//go:build !windows -// +build !windows +//go:build linux || darwin +// +build linux darwin // Package secstr provides methods and structs for working with protected (secure) strings package secstr diff --git a/secstr/secstring_stubs.go b/secstr/secstring_stubs.go index 6dddb675..0b8e7f39 100644 --- a/secstr/secstring_stubs.go +++ b/secstr/secstring_stubs.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !darwin +// +build !linux,!darwin // Package secstr provides methods and structs for working with protected (secure) strings package secstr From 89e77fd0b4278f4b23a748f614028e2d4c47ba38 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Thu, 25 Jul 2024 13:13:18 +0300 Subject: [PATCH 04/11] [sliceutil] Add method 'Join' --- CHANGELOG.md | 4 ++ sliceutil/example_test.go | 15 ++++++ sliceutil/sliceutil.go | 102 +++++++++++++++++++++--------------- sliceutil/sliceutil_test.go | 6 +++ version.go | 2 +- 5 files changed, 87 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 519e0eb7..6829e239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Changelog +### [13.3.0](https://kaos.sh/ek/13.3.0) + +- `[sliceutil]` Added method `Join` + ### [13.2.1](https://kaos.sh/ek/13.2.1) - `[terminal/input]` Added `NewLine` flag diff --git a/sliceutil/example_test.go b/sliceutil/example_test.go index 864f392a..d061e4cf 100644 --- a/sliceutil/example_test.go +++ b/sliceutil/example_test.go @@ -76,3 +76,18 @@ func ExampleDeduplicate() { fmt.Println(Deduplicate(s)) // Output: [A B C D] } + +func ExampleJoin() { + s1 := []string{"A", "B", "C", "D"} + s2 := []int{1, 2, 3, 4, 5} + s3 := []any{"John", 183, 98.123, false} + + fmt.Println(Join(s1, ":")) + fmt.Println(Join(s2, ",")) + fmt.Println(Join(s3, ";")) + + // Output: + // A:B:C:D + // 1,2,3,4,5 + // John;183;98.123;false +} diff --git a/sliceutil/sliceutil.go b/sliceutil/sliceutil.go index 08a8934a..5a1002c5 100644 --- a/sliceutil/sliceutil.go +++ b/sliceutil/sliceutil.go @@ -9,45 +9,13 @@ package sliceutil // ////////////////////////////////////////////////////////////////////////////////// // import ( + "bytes" "errors" + "fmt" ) // ////////////////////////////////////////////////////////////////////////////////// // -// Copy creates copy of given slice -// -// Deprecated: Use method slices.Clone instead -func Copy[K comparable](slice []K) []K { - if len(slice) == 0 { - return nil - } - - s := make([]K, len(slice)) - copy(s, slice) - - return s -} - -// IsEqual compares two slices and returns true if the slices are equal -// -// Deprecated: Use method slices.Equal instead -func IsEqual[K comparable](s1, s2 []K) bool { - switch { - case s1 == nil && s2 == nil: - return true - case len(s1) != len(s2): - return false - } - - for i := range s1 { - if s1[i] != s2[i] { - return false - } - } - - return true -} - // StringToInterface converts slice with strings to slice with any func StringToInterface(data []string) []any { if len(data) == 0 { @@ -125,13 +93,6 @@ func Index[K comparable](slice []K, item K) int { return -1 } -// Contains checks if string slice contains some value -// -// Deprecated: Use method slices.Contains instead -func Contains[K comparable](slice []K, value K) bool { - return Index(slice, value) != -1 -} - // Exclude removes items from slice func Exclude[K comparable](slice []K, items ...K) []K { var n int @@ -157,6 +118,65 @@ LOOP: return s[:n] } +// Join concatenates the elements of its first argument to create a single string. +// Unlike strings.Join, this method supports slices of any type. +func Join[K any](slice []K, sep string) string { + var buf bytes.Buffer + + for i, v := range slice { + fmt.Fprintf(&buf, "%v", v) + + if i+1 != len(slice) { + buf.WriteString(sep) + } + } + + return buf.String() +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Copy creates copy of given slice +// +// Deprecated: Use method slices.Clone instead +func Copy[K comparable](slice []K) []K { + if len(slice) == 0 { + return nil + } + + s := make([]K, len(slice)) + copy(s, slice) + + return s +} + +// IsEqual compares two slices and returns true if the slices are equal +// +// Deprecated: Use method slices.Equal instead +func IsEqual[K comparable](s1, s2 []K) bool { + switch { + case s1 == nil && s2 == nil: + return true + case len(s1) != len(s2): + return false + } + + for i := range s1 { + if s1[i] != s2[i] { + return false + } + } + + return true +} + +// Contains checks if string slice contains some value +// +// Deprecated: Use method slices.Contains instead +func Contains[K comparable](slice []K, value K) bool { + return Index(slice, value) != -1 +} + // Deduplicate removes duplicates from slice. // Slice must be sorted before deduplication. // diff --git a/sliceutil/sliceutil_test.go b/sliceutil/sliceutil_test.go index 0dbf8a37..86626e3a 100644 --- a/sliceutil/sliceutil_test.go +++ b/sliceutil/sliceutil_test.go @@ -125,6 +125,12 @@ func (s *SliceSuite) TestDeduplicate(c *C) { c.Assert(Deduplicate([]string{"1"}), DeepEquals, []string{"1"}) } +func (s *SliceSuite) TestJoin(c *C) { + c.Assert(Join([]int{1, 2, 3, 4, 5}, ";"), Equals, "1;2;3;4;5") + c.Assert(Join([]string{"test1", "test2", "test3"}, "--"), Equals, "test1--test2--test3") + c.Assert(Join([]any{"test", 34, 12.50}, ","), Equals, "test,34,12.5") +} + // ////////////////////////////////////////////////////////////////////////////////// // func (s *SliceSuite) BenchmarkStringToInterface(c *C) { diff --git a/version.go b/version.go index a28a3a3c..ee141523 100644 --- a/version.go +++ b/version.go @@ -8,4 +8,4 @@ package ek // ////////////////////////////////////////////////////////////////////////////////// // // VERSION is current ek package version -const VERSION = "13.2.1" +const VERSION = "13.3.0" From c94a8ac4dd3eeb7b518dfc2871f92dd70c27dc88 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 26 Jul 2024 12:42:55 +0300 Subject: [PATCH 05/11] [cache] Code refactoring --- cache/cache.go | 238 ++--------------- cache/{ => memory}/examples_test.go | 2 +- cache/memory/memory.go | 245 ++++++++++++++++++ .../{cache_test.go => memory/memory_test.go} | 2 +- 4 files changed, 265 insertions(+), 222 deletions(-) rename cache/{ => memory}/examples_test.go (99%) create mode 100644 cache/memory/memory.go rename cache/{cache_test.go => memory/memory_test.go} (99%) diff --git a/cache/cache.go b/cache/cache.go index 37979c97..3516d00e 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -1,4 +1,4 @@ -// Package cache provides a simple in-memory key:value cache +// Package cache provides methods and structs for caching data package cache // ////////////////////////////////////////////////////////////////////////////////// // @@ -8,232 +8,30 @@ package cache // // // ////////////////////////////////////////////////////////////////////////////////// // -import ( - "sync" - "time" -) +import "time" // ////////////////////////////////////////////////////////////////////////////////// // -// Cache is cache instance -type Cache struct { - expiration time.Duration - data map[string]any - expiry map[string]int64 - mu *sync.RWMutex - isJanitorWorks bool -} - -// ////////////////////////////////////////////////////////////////////////////////// // - -// New creates new cache instance -func New(defaultExpiration, cleanupInterval time.Duration) *Cache { - s := &Cache{ - expiration: defaultExpiration, - data: make(map[string]any), - expiry: make(map[string]int64), - mu: &sync.RWMutex{}, - } - - if cleanupInterval != 0 { - s.isJanitorWorks = true - go s.janitor(cleanupInterval) - } - - return s -} - -// ////////////////////////////////////////////////////////////////////////////////// // - -// Has returns true if cache contains data for given key -func (s *Cache) Has(key string) bool { - if s == nil { - return false - } - - s.mu.RLock() - - expiration, ok := s.expiry[key] - - if !ok { - s.mu.RUnlock() - return false - } - - if time.Now().UnixNano() > expiration { - s.mu.RUnlock() - - if !s.isJanitorWorks { - s.Delete(key) - } - - return false - } - - s.mu.RUnlock() - - return ok -} - -// Size returns number of items in cache -func (s *Cache) Size() int { - if s == nil { - return 0 - } - - s.mu.RLock() - defer s.mu.RUnlock() - - return len(s.data) -} - -// Expired returns number of expired items in cache -func (s *Cache) Expired() int { - if s == nil { - return 0 - } - - items := 0 - now := time.Now().UnixNano() - - s.mu.Lock() - - for _, expiration := range s.expiry { - if now > expiration { - items++ - } - } - - s.mu.Unlock() - - return items -} - -// Set adds or updates item in cache -func (s *Cache) Set(key string, data any) { - if s == nil { - return - } - - s.mu.Lock() - - s.expiry[key] = time.Now().Add(s.expiration).UnixNano() - s.data[key] = data - - s.mu.Unlock() -} - -// Get returns item from cache or nil -func (s *Cache) Get(key string) any { - if s == nil { - return nil - } - - s.mu.RLock() - - expiration, ok := s.expiry[key] - - if !ok { - s.mu.RUnlock() - return nil - } - - if time.Now().UnixNano() > expiration { - s.mu.RUnlock() - - if !s.isJanitorWorks { - s.Delete(key) - } - - return nil - } - - item := s.data[key] - - s.mu.RUnlock() - - return item -} - -// GetWithExpiration returns item from cache and expiration date or nil -func (s *Cache) GetWithExpiration(key string) (any, time.Time) { - if s == nil { - return nil, time.Time{} - } - - s.mu.RLock() - - expiration, ok := s.expiry[key] - - if !ok { - s.mu.RUnlock() - return nil, time.Time{} - } - - if time.Now().UnixNano() > expiration { - s.mu.RUnlock() +// Cache is cache backend interface +type Cache interface { + // Has returns true if cache contains data for given key + Has(key string) bool - if !s.isJanitorWorks { - s.Delete(key) - } - - return nil, time.Time{} - } - - item := s.data[key] - - s.mu.RUnlock() - - return item, time.Unix(0, expiration) -} - -// Delete removes item from cache -func (s *Cache) Delete(key string) { - if s == nil { - return - } - - s.mu.Lock() - - delete(s.data, key) - delete(s.expiry, key) - - s.mu.Unlock() -} - -// Flush removes all data from cache -func (s *Cache) Flush() { - if s == nil { - return - } - - s.mu.Lock() - - s.data = make(map[string]any) - s.expiry = make(map[string]int64) - - s.mu.Unlock() -} - -// ////////////////////////////////////////////////////////////////////////////////// // + // Size returns number of items in cache + Size() int -func (s *Cache) janitor(interval time.Duration) { - for range time.NewTicker(interval).C { - if len(s.data) == 0 { - continue - } + // Expired returns number of expired items in cache + Expired() int - now := time.Now().UnixNano() + // Set adds or updates item in cache + Set(key string, data any) bool - s.mu.Lock() + // GetWithExpiration returns item from cache and expiration date or nil + GetWithExpiration(key string) (any, time.Time) - for key, expiration := range s.expiry { - if now > expiration { - delete(s.data, key) - delete(s.expiry, key) - } - } + // Delete removes item from cache + Delete(key string) bool - s.mu.Unlock() - } + // Flush removes all data from cache + Flush() bool } diff --git a/cache/examples_test.go b/cache/memory/examples_test.go similarity index 99% rename from cache/examples_test.go rename to cache/memory/examples_test.go index 4d90d00c..0451f503 100644 --- a/cache/examples_test.go +++ b/cache/memory/examples_test.go @@ -1,4 +1,4 @@ -package cache +package memory // ////////////////////////////////////////////////////////////////////////////////// // // // diff --git a/cache/memory/memory.go b/cache/memory/memory.go new file mode 100644 index 00000000..d2963568 --- /dev/null +++ b/cache/memory/memory.go @@ -0,0 +1,245 @@ +// Package cache provides in-memory cache +package memory + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "sync" + "time" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Cache is in-memory cache instance +type Cache struct { + expiration time.Duration + data map[string]any + expiry map[string]int64 + mu *sync.RWMutex + isJanitorWorks bool +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// New creates new cache instance +func New(defaultExpiration, cleanupInterval time.Duration) *Cache { + s := &Cache{ + expiration: defaultExpiration, + data: make(map[string]any), + expiry: make(map[string]int64), + mu: &sync.RWMutex{}, + } + + if cleanupInterval != 0 { + s.isJanitorWorks = true + go s.janitor(cleanupInterval) + } + + return s +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Has returns true if cache contains data for given key +func (s *Cache) Has(key string) bool { + if s == nil { + return false + } + + s.mu.RLock() + + expiration, ok := s.expiry[key] + + if !ok { + s.mu.RUnlock() + return false + } + + if time.Now().UnixNano() > expiration { + s.mu.RUnlock() + + if !s.isJanitorWorks { + s.Delete(key) + } + + return false + } + + s.mu.RUnlock() + + return ok +} + +// Size returns number of items in cache +func (s *Cache) Size() int { + if s == nil { + return 0 + } + + s.mu.RLock() + defer s.mu.RUnlock() + + return len(s.data) +} + +// Expired returns number of expired items in cache +func (s *Cache) Expired() int { + if s == nil { + return 0 + } + + items := 0 + now := time.Now().UnixNano() + + s.mu.Lock() + + for _, expiration := range s.expiry { + if now > expiration { + items++ + } + } + + s.mu.Unlock() + + return items +} + +// Set adds or updates item in cache +func (s *Cache) Set(key string, data any) bool { + if s == nil { + return false + } + + s.mu.Lock() + + s.expiry[key] = time.Now().Add(s.expiration).UnixNano() + s.data[key] = data + + s.mu.Unlock() + + return true +} + +// Get returns item from cache or nil +func (s *Cache) Get(key string) any { + if s == nil { + return nil + } + + s.mu.RLock() + + expiration, ok := s.expiry[key] + + if !ok { + s.mu.RUnlock() + return nil + } + + if time.Now().UnixNano() > expiration { + s.mu.RUnlock() + + if !s.isJanitorWorks { + s.Delete(key) + } + + return nil + } + + item := s.data[key] + + s.mu.RUnlock() + + return item +} + +// GetWithExpiration returns item from cache and expiration date or nil +func (s *Cache) GetWithExpiration(key string) (any, time.Time) { + if s == nil { + return nil, time.Time{} + } + + s.mu.RLock() + + expiration, ok := s.expiry[key] + + if !ok { + s.mu.RUnlock() + return nil, time.Time{} + } + + if time.Now().UnixNano() > expiration { + s.mu.RUnlock() + + if !s.isJanitorWorks { + s.Delete(key) + } + + return nil, time.Time{} + } + + item := s.data[key] + + s.mu.RUnlock() + + return item, time.Unix(0, expiration) +} + +// Delete removes item from cache +func (s *Cache) Delete(key string) bool { + if s == nil { + return false + } + + s.mu.Lock() + + delete(s.data, key) + delete(s.expiry, key) + + s.mu.Unlock() + + return true +} + +// Flush removes all data from cache +func (s *Cache) Flush() bool { + if s == nil { + return false + } + + s.mu.Lock() + + s.data = make(map[string]any) + s.expiry = make(map[string]int64) + + s.mu.Unlock() + + return true +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +func (s *Cache) janitor(interval time.Duration) { + for range time.NewTicker(interval).C { + if len(s.data) == 0 { + continue + } + + now := time.Now().UnixNano() + + s.mu.Lock() + + for key, expiration := range s.expiry { + if now > expiration { + delete(s.data, key) + delete(s.expiry, key) + } + } + + s.mu.Unlock() + } +} diff --git a/cache/cache_test.go b/cache/memory/memory_test.go similarity index 99% rename from cache/cache_test.go rename to cache/memory/memory_test.go index 9fb7b048..5e2cce42 100644 --- a/cache/cache_test.go +++ b/cache/memory/memory_test.go @@ -1,4 +1,4 @@ -package cache +package memory // ////////////////////////////////////////////////////////////////////////////////// // // // From 2ddc547ce73f2020f7997a89f46f687ffd0616a8 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 26 Jul 2024 12:45:43 +0300 Subject: [PATCH 06/11] [cache] Code refactoring --- .scripts/packages.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scripts/packages.list b/.scripts/packages.list index 8fe7b5b4..53314018 100644 --- a/.scripts/packages.list +++ b/.scripts/packages.list @@ -1,5 +1,5 @@ * + ansi -* + cache +* + cache/memory * + color * + cron * + csv From 7006b4d1f5f925b9c80563a1e9371cd7a64adc82 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Fri, 26 Jul 2024 15:04:56 +0300 Subject: [PATCH 07/11] [cache/memory] Code refactoring --- cache/cache.go | 8 +- cache/memory/examples_test.go | 46 ++++++-- cache/memory/memory.go | 215 ++++++++++++++++++++++------------ cache/memory/memory_test.go | 40 ++++++- 4 files changed, 221 insertions(+), 88 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index 3516d00e..fe777fa8 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -24,7 +24,13 @@ type Cache interface { Expired() int // Set adds or updates item in cache - Set(key string, data any) bool + Set(key string, data any, expiration ...time.Duration) bool + + // GetWithExpiration returns item from cache + Get(key string) any + + // GetWithExpiration returns item expiration date + GetExpiration(key string) time.Time // GetWithExpiration returns item from cache and expiration date or nil GetWithExpiration(key string) (any, time.Time) diff --git a/cache/memory/examples_test.go b/cache/memory/examples_test.go index 0451f503..26919406 100644 --- a/cache/memory/examples_test.go +++ b/cache/memory/examples_test.go @@ -15,7 +15,10 @@ import ( // ////////////////////////////////////////////////////////////////////////////////// // func ExampleNew() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") @@ -24,16 +27,23 @@ func ExampleNew() { } func ExampleCache_Set() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") + cache.Set("test", "ABCD", 15*time.Minute) fmt.Println(cache.Get("test")) // Output: ABCD } func ExampleCache_Has() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") @@ -42,7 +52,10 @@ func ExampleCache_Has() { } func ExampleCache_Get() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") @@ -51,7 +64,10 @@ func ExampleCache_Get() { } func ExampleCache_Size() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") @@ -60,7 +76,10 @@ func ExampleCache_Size() { } func ExampleCache_Expired() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") @@ -69,7 +88,10 @@ func ExampleCache_Expired() { } func ExampleCache_GetWithExpiration() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") @@ -79,7 +101,10 @@ func ExampleCache_GetWithExpiration() { } func ExampleCache_Delete() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") cache.Delete("test") @@ -89,7 +114,10 @@ func ExampleCache_Delete() { } func ExampleCache_Flush() { - cache := New(time.Second, time.Minute) + cache, _ := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Minute, + }) cache.Set("test", "ABCD") cache.Flush() diff --git a/cache/memory/memory.go b/cache/memory/memory.go index d2963568..045d2237 100644 --- a/cache/memory/memory.go +++ b/cache/memory/memory.go @@ -1,4 +1,4 @@ -// Package cache provides in-memory cache +// Package memory provides cache with in-memory storage package memory // ////////////////////////////////////////////////////////////////////////////////// // @@ -9,237 +9,306 @@ package memory // ////////////////////////////////////////////////////////////////////////////////// // import ( + "errors" "sync" "time" + + "github.com/essentialkaos/ek/v13/cache" ) // ////////////////////////////////////////////////////////////////////////////////// // +// MIN_EXPIRATION is minimal expiration duration +const MIN_EXPIRATION = time.Millisecond + +// MIN_CLEANUP_INTERVAL is minimal cleanup interval +const MIN_CLEANUP_INTERVAL = time.Millisecond + +// ////////////////////////////////////////////////////////////////////////////////// // + +// validate storage interface +var _ cache.Cache = (*Cache)(nil) + +// ////////////////////////////////////////////////////////////////////////////////// // + // Cache is in-memory cache instance type Cache struct { - expiration time.Duration data map[string]any expiry map[string]int64 mu *sync.RWMutex + expiration time.Duration isJanitorWorks bool } +// Config is cache configuration +type Config struct { + DefaultExpiration time.Duration + CleanupInterval time.Duration +} + // ////////////////////////////////////////////////////////////////////////////////// // // New creates new cache instance -func New(defaultExpiration, cleanupInterval time.Duration) *Cache { - s := &Cache{ - expiration: defaultExpiration, +func New(config Config) (*Cache, error) { + err := config.Validate() + + if err != nil { + return nil, err + } + + c := &Cache{ + expiration: config.DefaultExpiration, data: make(map[string]any), expiry: make(map[string]int64), mu: &sync.RWMutex{}, } - if cleanupInterval != 0 { - s.isJanitorWorks = true - go s.janitor(cleanupInterval) + if config.CleanupInterval != 0 { + c.isJanitorWorks = true + go c.janitor(config.CleanupInterval) } - return s + return c, nil } // ////////////////////////////////////////////////////////////////////////////////// // // Has returns true if cache contains data for given key -func (s *Cache) Has(key string) bool { - if s == nil { +func (c *Cache) Has(key string) bool { + if c == nil { return false } - s.mu.RLock() + c.mu.RLock() - expiration, ok := s.expiry[key] + expiration, ok := c.expiry[key] if !ok { - s.mu.RUnlock() + c.mu.RUnlock() return false } if time.Now().UnixNano() > expiration { - s.mu.RUnlock() + c.mu.RUnlock() - if !s.isJanitorWorks { - s.Delete(key) + if !c.isJanitorWorks { + c.Delete(key) } return false } - s.mu.RUnlock() + c.mu.RUnlock() return ok } // Size returns number of items in cache -func (s *Cache) Size() int { - if s == nil { +func (c *Cache) Size() int { + if c == nil { return 0 } - s.mu.RLock() - defer s.mu.RUnlock() + c.mu.RLock() + defer c.mu.RUnlock() - return len(s.data) + return len(c.data) } // Expired returns number of expired items in cache -func (s *Cache) Expired() int { - if s == nil { +func (c *Cache) Expired() int { + if c == nil { return 0 } items := 0 now := time.Now().UnixNano() - s.mu.Lock() + c.mu.Lock() - for _, expiration := range s.expiry { + for _, expiration := range c.expiry { if now > expiration { items++ } } - s.mu.Unlock() + c.mu.Unlock() return items } // Set adds or updates item in cache -func (s *Cache) Set(key string, data any) bool { - if s == nil { +func (c *Cache) Set(key string, data any, expiration ...time.Duration) bool { + if c == nil { return false } - s.mu.Lock() + c.mu.Lock() + + if len(expiration) > 0 && expiration[0] > MIN_EXPIRATION { + c.expiry[key] = time.Now().Add(expiration[0]).UnixNano() + } else { + c.expiry[key] = time.Now().Add(c.expiration).UnixNano() + } - s.expiry[key] = time.Now().Add(s.expiration).UnixNano() - s.data[key] = data + c.data[key] = data - s.mu.Unlock() + c.mu.Unlock() return true } // Get returns item from cache or nil -func (s *Cache) Get(key string) any { - if s == nil { +func (c *Cache) Get(key string) any { + if c == nil { return nil } - s.mu.RLock() + c.mu.RLock() - expiration, ok := s.expiry[key] + expiration, ok := c.expiry[key] if !ok { - s.mu.RUnlock() + c.mu.RUnlock() return nil } if time.Now().UnixNano() > expiration { - s.mu.RUnlock() + c.mu.RUnlock() - if !s.isJanitorWorks { - s.Delete(key) + if !c.isJanitorWorks { + c.Delete(key) } return nil } - item := s.data[key] + item := c.data[key] - s.mu.RUnlock() + c.mu.RUnlock() return item } +// GetWithExpiration returns item expiration date +func (c *Cache) GetExpiration(key string) time.Time { + if c == nil { + return time.Time{} + } + + c.mu.RLock() + + expiration, ok := c.expiry[key] + + if !ok { + c.mu.RUnlock() + return time.Time{} + } + + c.mu.RUnlock() + + return time.Unix(0, expiration) +} + // GetWithExpiration returns item from cache and expiration date or nil -func (s *Cache) GetWithExpiration(key string) (any, time.Time) { - if s == nil { +func (c *Cache) GetWithExpiration(key string) (any, time.Time) { + if c == nil { return nil, time.Time{} } - s.mu.RLock() + c.mu.RLock() - expiration, ok := s.expiry[key] + expiration, ok := c.expiry[key] if !ok { - s.mu.RUnlock() + c.mu.RUnlock() return nil, time.Time{} } if time.Now().UnixNano() > expiration { - s.mu.RUnlock() + c.mu.RUnlock() - if !s.isJanitorWorks { - s.Delete(key) + if !c.isJanitorWorks { + c.Delete(key) } return nil, time.Time{} } - item := s.data[key] + item := c.data[key] - s.mu.RUnlock() + c.mu.RUnlock() return item, time.Unix(0, expiration) } // Delete removes item from cache -func (s *Cache) Delete(key string) bool { - if s == nil { +func (c *Cache) Delete(key string) bool { + if c == nil { return false } - s.mu.Lock() + c.mu.Lock() - delete(s.data, key) - delete(s.expiry, key) + delete(c.data, key) + delete(c.expiry, key) - s.mu.Unlock() + c.mu.Unlock() return true } // Flush removes all data from cache -func (s *Cache) Flush() bool { - if s == nil { +func (c *Cache) Flush() bool { + if c == nil { return false } - s.mu.Lock() + c.mu.Lock() - s.data = make(map[string]any) - s.expiry = make(map[string]int64) + c.data = make(map[string]any) + c.expiry = make(map[string]int64) - s.mu.Unlock() + c.mu.Unlock() return true } // ////////////////////////////////////////////////////////////////////////////////// // -func (s *Cache) janitor(interval time.Duration) { +// Validate validates cache configuration +func (c Config) Validate() error { + if c.DefaultExpiration < MIN_EXPIRATION { + return errors.New("Expiration is too short (< 1ms)") + } + + if c.CleanupInterval != 0 && c.CleanupInterval < MIN_CLEANUP_INTERVAL { + return errors.New("Cleanup interval is too short (< 1ms)") + } + + return nil +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// janitor is cache cleanup job +func (c *Cache) janitor(interval time.Duration) { for range time.NewTicker(interval).C { - if len(s.data) == 0 { + if len(c.data) == 0 { continue } now := time.Now().UnixNano() - s.mu.Lock() + c.mu.Lock() - for key, expiration := range s.expiry { + for key, expiration := range c.expiry { if now > expiration { - delete(s.data, key) - delete(s.expiry, key) + delete(c.data, key) + delete(c.expiry, key) } } - s.mu.Unlock() + c.mu.Unlock() } } diff --git a/cache/memory/memory_test.go b/cache/memory/memory_test.go index 5e2cce42..e2690d7b 100644 --- a/cache/memory/memory_test.go +++ b/cache/memory/memory_test.go @@ -29,18 +29,25 @@ var _ = Suite(&CacheSuite{}) // ////////////////////////////////////////////////////////////////////////////////// // func (s *CacheSuite) TestCache(c *C) { - cache := New(time.Second/16, time.Second/32) + cache, err := New(Config{ + DefaultExpiration: time.Second / 16, + CleanupInterval: time.Second / 32, + }) + + c.Assert(err, IsNil) + c.Assert(cache, NotNil) cache.Set("1", "TEST") cache.Set("2", "TEST") + cache.Set("3", "TEST", time.Minute) - c.Assert(cache.Size(), Equals, 2) + c.Assert(cache.Size(), Equals, 3) c.Assert(cache.Get("1"), Equals, "TEST") c.Assert(cache.Get("2"), Equals, "TEST") c.Assert(cache.Has("2"), Equals, true) - c.Assert(cache.Has("3"), Equals, false) + c.Assert(cache.Has("4"), Equals, false) item, exp := cache.GetWithExpiration("1") @@ -50,6 +57,7 @@ func (s *CacheSuite) TestCache(c *C) { cache.Delete("1") c.Assert(cache.Get("1"), Equals, nil) + c.Assert(cache.GetExpiration("3").IsZero(), Not(Equals), true) time.Sleep(time.Second / 8) @@ -62,7 +70,10 @@ func (s *CacheSuite) TestCache(c *C) { } func (s *CacheSuite) TestCacheWithoutJanitor(c *C) { - cache := New(time.Second/32, 0) + cache, err := New(Config{DefaultExpiration: time.Second / 32}) + + c.Assert(err, IsNil) + c.Assert(cache, NotNil) cache.Set("1", "TEST") cache.Set("2", "TEST") @@ -83,7 +94,13 @@ func (s *CacheSuite) TestCacheWithoutJanitor(c *C) { } func (s *CacheSuite) TestExpiration(c *C) { - cache := New(time.Second/16, time.Minute) + cache, err := New(Config{ + DefaultExpiration: time.Second / 16, + CleanupInterval: time.Minute, + }) + + c.Assert(err, IsNil) + c.Assert(cache, NotNil) cache.Set("1", "TEST") @@ -92,6 +109,8 @@ func (s *CacheSuite) TestExpiration(c *C) { item, _ := cache.GetWithExpiration("1") c.Assert(item, Equals, nil) + c.Assert(cache.GetExpiration("2").IsZero(), Equals, true) + c.Assert(cache.Get("1"), Equals, nil) } @@ -106,8 +125,19 @@ func (s *CacheSuite) TestNil(c *C) { c.Assert(cache.Expired(), Equals, 0) c.Assert(cache.Get("1"), Equals, nil) c.Assert(cache.Has("1"), Equals, false) + c.Assert(cache.GetExpiration("1").IsZero(), Equals, true) item, exp := cache.GetWithExpiration("1") c.Assert(item, Equals, nil) c.Assert(exp.IsZero(), Equals, true) } + +func (s *CacheSuite) TestConfig(c *C) { + _, err := New(Config{DefaultExpiration: 1}) + + c.Assert(err.Error(), Equals, "Expiration is too short (< 1ms)") + + _, err = New(Config{DefaultExpiration: time.Minute, CleanupInterval: 1}) + + c.Assert(err.Error(), Equals, "Cleanup interval is too short (< 1ms)") +} From ce21b06430a81ce17ab6e8a554d2306d561e20a0 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Tue, 30 Jul 2024 14:04:53 +0300 Subject: [PATCH 08/11] [cache/memory] Improvements --- cache/memory/memory.go | 6 +++--- cache/memory/memory_test.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cache/memory/memory.go b/cache/memory/memory.go index 045d2237..28a9e8f4 100644 --- a/cache/memory/memory.go +++ b/cache/memory/memory.go @@ -139,7 +139,7 @@ func (c *Cache) Expired() int { // Set adds or updates item in cache func (c *Cache) Set(key string, data any, expiration ...time.Duration) bool { - if c == nil { + if c == nil || data == nil { return false } @@ -279,11 +279,11 @@ func (c *Cache) Flush() bool { // Validate validates cache configuration func (c Config) Validate() error { if c.DefaultExpiration < MIN_EXPIRATION { - return errors.New("Expiration is too short (< 1ms)") + return errors.New("Invalid configuration: Expiration is too short (< 1ms)") } if c.CleanupInterval != 0 && c.CleanupInterval < MIN_CLEANUP_INTERVAL { - return errors.New("Cleanup interval is too short (< 1ms)") + return errors.New("Invalid configuration: Cleanup interval is too short (< 1ms)") } return nil diff --git a/cache/memory/memory_test.go b/cache/memory/memory_test.go index e2690d7b..351d6840 100644 --- a/cache/memory/memory_test.go +++ b/cache/memory/memory_test.go @@ -66,7 +66,7 @@ func (s *CacheSuite) TestCache(c *C) { c.Assert(cache.Get("2"), Equals, nil) c.Assert(item, Equals, nil) - cache.Flush() + c.Assert(cache.Flush(), Equals, true) } func (s *CacheSuite) TestCacheWithoutJanitor(c *C) { @@ -135,9 +135,9 @@ func (s *CacheSuite) TestNil(c *C) { func (s *CacheSuite) TestConfig(c *C) { _, err := New(Config{DefaultExpiration: 1}) - c.Assert(err.Error(), Equals, "Expiration is too short (< 1ms)") + c.Assert(err.Error(), Equals, "Invalid configuration: Expiration is too short (< 1ms)") _, err = New(Config{DefaultExpiration: time.Minute, CleanupInterval: 1}) - c.Assert(err.Error(), Equals, "Cleanup interval is too short (< 1ms)") + c.Assert(err.Error(), Equals, "Invalid configuration: Cleanup interval is too short (< 1ms)") } From 7c0cf62c5f8d7c729c6604f88fb018f8468cead1 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Tue, 30 Jul 2024 14:15:20 +0300 Subject: [PATCH 09/11] [cache/fs] New sub-package --- .scripts/packages.list | 1 + CHANGELOG.md | 2 + cache/fs/examples_test.go | 128 +++++++++++++++++ cache/fs/fs.go | 291 ++++++++++++++++++++++++++++++++++++++ cache/fs/fs_stubs.go | 90 ++++++++++++ cache/fs/fs_test.go | 115 +++++++++++++++ 6 files changed, 627 insertions(+) create mode 100644 cache/fs/examples_test.go create mode 100644 cache/fs/fs.go create mode 100644 cache/fs/fs_stubs.go create mode 100644 cache/fs/fs_test.go diff --git a/.scripts/packages.list b/.scripts/packages.list index 53314018..60e19087 100644 --- a/.scripts/packages.list +++ b/.scripts/packages.list @@ -1,5 +1,6 @@ * + ansi * + cache/memory +* + cache/fs * + color * + cron * + csv diff --git a/CHANGELOG.md b/CHANGELOG.md index 6829e239..23202e60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### [13.3.0](https://kaos.sh/ek/13.3.0) +- `[cache/fs]` Added cache with file system storage +- `[cache]` In-memory cache moved to `cache/memory` - `[sliceutil]` Added method `Join` ### [13.2.1](https://kaos.sh/ek/13.2.1) diff --git a/cache/fs/examples_test.go b/cache/fs/examples_test.go new file mode 100644 index 00000000..44fafa6d --- /dev/null +++ b/cache/fs/examples_test.go @@ -0,0 +1,128 @@ +package fs + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "fmt" + "time" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +func ExampleNew() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + + fmt.Println(cache.Get("test")) +} + +func ExampleCache_Set() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + cache.Set("test", "ABCD", 15*time.Minute) + + fmt.Println(cache.Get("test")) +} + +func ExampleCache_Has() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + + fmt.Println(cache.Has("test")) +} + +func ExampleCache_Get() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + + fmt.Println(cache.Get("test")) +} + +func ExampleCache_Size() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + + fmt.Println(cache.Size()) +} + +func ExampleCache_Expired() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + + fmt.Println(cache.Expired()) +} + +func ExampleCache_GetWithExpiration() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + + item, exp := cache.GetWithExpiration("test") + + fmt.Println(item, exp.String()) +} + +func ExampleCache_Delete() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + cache.Delete("test") + + fmt.Println(cache.Get("test")) +} + +func ExampleCache_Flush() { + cache, _ := New(Config{ + Dir: "/path/to/cache", + DefaultExpiration: time.Minute, + CleanupInterval: time.Minute, + }) + + cache.Set("test", "ABCD") + cache.Flush() + + fmt.Println(cache.Get("test")) +} diff --git a/cache/fs/fs.go b/cache/fs/fs.go new file mode 100644 index 00000000..a2ac2e8c --- /dev/null +++ b/cache/fs/fs.go @@ -0,0 +1,291 @@ +//go:build !windows +// +build !windows + +// Package fs provides cache with file system storage +package fs + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "crypto/sha1" + "encoding/gob" + "fmt" + "hash" + "os" + "path" + "time" + + "github.com/essentialkaos/ek/v13/cache" + "github.com/essentialkaos/ek/v13/fsutil" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// MIN_EXPIRATION is minimal expiration duration +const MIN_EXPIRATION = time.Second + +// MIN_CLEANUP_INTERVAL is minimal cleanup interval +const MIN_CLEANUP_INTERVAL = time.Second + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Cache is fs cache instance +type Cache struct { + dir string + hasher hash.Hash + expiration time.Duration + isJanitorWorks bool +} + +// Config is cache configuration +type Config struct { + Dir string + DefaultExpiration time.Duration + CleanupInterval time.Duration +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// cacheItem is cache item +type cacheItem struct { + Data any +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// validate storage interface +var _ cache.Cache = (*Cache)(nil) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// New creates new cache instance +func New(config Config) (*Cache, error) { + err := config.Validate() + + if err != nil { + return nil, fmt.Errorf("Invalid configuration: %w", err) + } + + c := &Cache{ + dir: config.Dir, + expiration: config.DefaultExpiration, + hasher: sha1.New(), + } + + if config.CleanupInterval != 0 { + c.isJanitorWorks = true + go c.janitor(config.CleanupInterval) + } + + return c, nil +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Has returns true if cache contains data for given key +func (c *Cache) Has(key string) bool { + if c == nil || key == "" { + return false + } + + return fsutil.IsExist(c.getItemPath(key, false)) +} + +// Size returns number of items in cache +func (c *Cache) Size() int { + if c == nil { + return 0 + } + + return len(fsutil.List(c.dir, true)) +} + +// Expired returns number of expired items in cache +func (c *Cache) Expired() int { + if c == nil { + return 0 + } + + expired := 0 + now := time.Now() + items := fsutil.List(c.dir, true) + + fsutil.ListToAbsolute(c.dir, items) + + for _, item := range items { + mtime, _ := fsutil.GetMTime(item) + + if mtime.Before(now) { + expired++ + } + } + + return expired +} + +// Set adds or updates item in cache +func (c *Cache) Set(key string, data any, expiration ...time.Duration) bool { + if c == nil || data == nil || key == "" { + return false + } + + tmpFile := c.getItemPath(key, true) + fd, err := os.OpenFile(tmpFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + + if err != nil { + return false + } + + err = gob.NewEncoder(fd).Encode(&cacheItem{data}) + + fd.Close() + + if err != nil { + os.Remove(tmpFile) + return false + } + + itemFile := c.getItemPath(key, false) + + err = os.Rename(tmpFile, itemFile) + + if err != nil { + os.Remove(tmpFile) + return false + } + + expr := c.expiration + + if len(expiration) > 0 { + expr = expiration[0] + } + + return os.Chtimes(itemFile, time.Time{}, time.Now().Add(expr)) == err +} + +// GetWithExpiration returns item from cache +func (c *Cache) Get(key string) any { + if c == nil || key == "" { + return nil + } + + fd, err := os.Open(c.getItemPath(key, false)) + + if err != nil { + return nil + } + + item := &cacheItem{} + err = gob.NewDecoder(fd).Decode(item) + + if err != nil { + return nil + } + + return item.Data +} + +// GetWithExpiration returns item expiration date +func (c *Cache) GetExpiration(key string) time.Time { + if c == nil || key == "" { + return time.Time{} + } + + mt, _ := fsutil.GetMTime(c.getItemPath(key, false)) + + return mt +} + +// GetWithExpiration returns item from cache and expiration date or nil +func (c *Cache) GetWithExpiration(key string) (any, time.Time) { + if c == nil || key == "" { + return nil, time.Time{} + } + + return c.Get(key), c.GetExpiration(key) +} + +// Delete removes item from cache +func (c *Cache) Delete(key string) bool { + if c == nil { + return false + } + + return os.Remove(c.getItemPath(key, false)) == nil +} + +// Flush removes all data from cache +func (c *Cache) Flush() bool { + if c == nil { + return false + } + + items := fsutil.List(c.dir, true) + fsutil.ListToAbsolute(c.dir, items) + + for _, item := range items { + os.Remove(item) + } + + return true +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Validate validates cache configuration +func (c Config) Validate() error { + if c.DefaultExpiration < MIN_EXPIRATION { + return fmt.Errorf("Expiration is too short (< 1s)") + } + + if c.CleanupInterval != 0 && c.CleanupInterval < MIN_CLEANUP_INTERVAL { + return fmt.Errorf("Cleanup interval is too short (< 1s)") + } + + err := fsutil.ValidatePerms("DRWX", c.Dir) + + if err != nil { + return fmt.Errorf("Can't use given directory for cache: %w", err) + } + + return nil +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// getItemPath returns path to cache item +func (c *Cache) getItemPath(key string, temporary bool) string { + if temporary { + return path.Join(c.dir, "."+c.hashKey(key)) + } + + return path.Join(c.dir, c.hashKey(key)) +} + +// hashKey generates SHA-1 hash for given key +func (c *Cache) hashKey(key string) string { + return fmt.Sprintf("%64x", c.hasher.Sum([]byte(key))) +} + +// janitor is cache cleanup job +func (c *Cache) janitor(interval time.Duration) { + for range time.NewTicker(interval).C { + now := time.Now() + + items := fsutil.List(c.dir, true) + fsutil.ListToAbsolute(c.dir, items) + + for _, item := range items { + mtime, _ := fsutil.GetMTime(item) + + if mtime.Before(now) { + os.Remove(item) + } + } + } +} diff --git a/cache/fs/fs_stubs.go b/cache/fs/fs_stubs.go new file mode 100644 index 00000000..0bee6100 --- /dev/null +++ b/cache/fs/fs_stubs.go @@ -0,0 +1,90 @@ +//go:build windows +// +build windows + +// Package fs provides cache with file system storage +package fs + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "time" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// ❗ MIN_EXPIRATION is minimal expiration duration +const MIN_EXPIRATION = time.Second + +// ❗ MIN_CLEANUP_INTERVAL is minimal cleanup interval +const MIN_CLEANUP_INTERVAL = time.Second + +// ////////////////////////////////////////////////////////////////////////////////// // + +// ❗ Cache is fs cache instance +type Cache struct{} + +// ❗ Config is cache configuration +type Config struct { + Dir string + DefaultExpiration time.Duration + CleanupInterval time.Duration +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// ❗ New creates new cache instance +func New(config Config) (*Cache, error) { + panic("UNSUPPORTED") +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// ❗ Has returns true if cache contains data for given key +func (c *Cache) Has(key string) bool { + panic("UNSUPPORTED") +} + +// ❗ Size returns number of items in cache +func (c *Cache) Size() int { + panic("UNSUPPORTED") +} + +// ❗ Expired returns number of expired items in cache +func (c *Cache) Expired() int { + panic("UNSUPPORTED") +} + +// ❗ Set adds or updates item in cache +func (c *Cache) Set(key string, data any, expiration ...time.Duration) bool { + panic("UNSUPPORTED") +} + +// ❗ GetWithExpiration returns item from cache +func (c *Cache) Get(key string) any { + panic("UNSUPPORTED") +} + +// ❗ GetWithExpiration returns item expiration date +func (c *Cache) GetExpiration(key string) time.Time { + panic("UNSUPPORTED") +} + +// ❗ GetWithExpiration returns item from cache and expiration date or nil +func (c *Cache) GetWithExpiration(key string) (any, time.Time) { + panic("UNSUPPORTED") +} + +// ❗ Delete removes item from cache +func (c *Cache) Delete(key string) bool { + panic("UNSUPPORTED") +} + +// ❗ Flush removes all data from cache +func (c *Cache) Flush() bool { + panic("UNSUPPORTED") +} diff --git a/cache/fs/fs_test.go b/cache/fs/fs_test.go new file mode 100644 index 00000000..faba74c3 --- /dev/null +++ b/cache/fs/fs_test.go @@ -0,0 +1,115 @@ +package fs + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "testing" + "time" + + . "github.com/essentialkaos/check" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +type CacheSuite struct{} + +// ////////////////////////////////////////////////////////////////////////////////// // + +func Test(t *testing.T) { TestingT(t) } + +// ////////////////////////////////////////////////////////////////////////////////// // + +var _ = Suite(&CacheSuite{}) + +// ////////////////////////////////////////////////////////////////////////////////// // + +func (s *CacheSuite) TestCache(c *C) { + cache, err := New(Config{ + DefaultExpiration: time.Second, + CleanupInterval: time.Second, + Dir: c.MkDir(), + }) + + c.Assert(err, IsNil) + c.Assert(cache, NotNil) + + cache.Set("1", "TEST") + cache.Set("2", "TEST") + cache.Set("3", "TEST", time.Minute) + + c.Assert(cache.Size(), Equals, 3) + + c.Assert(cache.Get("1"), Equals, "TEST") + c.Assert(cache.Get("2"), Equals, "TEST") + + c.Assert(cache.Has("2"), Equals, true) + c.Assert(cache.Has("4"), Equals, false) + + item, exp := cache.GetWithExpiration("1") + + c.Assert(item, Equals, "TEST") + c.Assert(exp.IsZero(), Not(Equals), true) + + cache.Delete("1") + + c.Assert(cache.Get("1"), Equals, nil) + c.Assert(cache.GetExpiration("3").IsZero(), Not(Equals), true) + + cache2, err := New(Config{ + DefaultExpiration: time.Second, + Dir: c.MkDir(), + }) + + cache2.Set("1", "TEST") + cache2.Set("2", "TEST", time.Minute) + + time.Sleep(2 * time.Second) + + item, _ = cache.GetWithExpiration("2") + + c.Assert(cache.Get("2"), Equals, nil) + c.Assert(item, Equals, nil) + + c.Assert(cache.Expired(), Equals, 0) + c.Assert(cache2.Expired(), Equals, 1) + + c.Assert(cache.Flush(), Equals, true) + +} + +func (s *CacheSuite) TestNil(c *C) { + var cache *Cache + + c.Assert(func() { cache.Set("1", "TEST") }, NotPanics) + c.Assert(func() { cache.Delete("1") }, NotPanics) + c.Assert(func() { cache.Flush() }, NotPanics) + + c.Assert(cache.Size(), Equals, 0) + c.Assert(cache.Expired(), Equals, 0) + c.Assert(cache.Get("1"), Equals, nil) + c.Assert(cache.Has("1"), Equals, false) + c.Assert(cache.GetExpiration("1").IsZero(), Equals, true) + + item, exp := cache.GetWithExpiration("1") + c.Assert(item, Equals, nil) + c.Assert(exp.IsZero(), Equals, true) +} + +func (s *CacheSuite) TestConfig(c *C) { + _, err := New(Config{DefaultExpiration: 1}) + + c.Assert(err.Error(), Equals, "Invalid configuration: Expiration is too short (< 1s)") + + _, err = New(Config{DefaultExpiration: time.Minute, CleanupInterval: 1}) + + c.Assert(err.Error(), Equals, "Invalid configuration: Cleanup interval is too short (< 1s)") + + _, err = New(Config{DefaultExpiration: time.Minute, CleanupInterval: time.Minute, Dir: "_unknown_"}) + + c.Assert(err.Error(), Equals, "Invalid configuration: Can't use given directory for cache: Directory _unknown_ doesn't exist or not accessible") +} From 0eb45607fae8cf4d60145d2a8d690e908e51d49b Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Tue, 30 Jul 2024 14:18:53 +0300 Subject: [PATCH 10/11] Improve README --- .scripts/packages.list | 4 ++-- README.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.scripts/packages.list b/.scripts/packages.list index 60e19087..42bcb48c 100644 --- a/.scripts/packages.list +++ b/.scripts/packages.list @@ -1,15 +1,15 @@ * + ansi -* + cache/memory * + cache/fs +* + cache/memory * + color * + cron * + csv +* + directio * + easing * + emoji * + env * + errutil * + events -* + directio * + fmtc * + fmtutil * + fmtutil/barcode diff --git a/README.md b/README.md index 80225257..b38dfb3e 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ Currently we support Linux and macOS (_except some packages_). All packages have ### Sub-packages - [`ansi`](https://kaos.sh/g/ek.v13/ansi) — Package provides methods for working with ANSI/VT100 control sequences -- [`cache`](https://kaos.sh/g/ek.v13/cache) — Package provides a simple in-memory key/value cache +- [`cache`](https://kaos.sh/g/ek.v13/cache/fs) — Package provides a cache with file system storage +- [`cache`](https://kaos.sh/g/ek.v13/cache/memory) — Package provides a cache with memory storage - [`color`](https://kaos.sh/g/ek.v13/color) — Package provides methods for working with colors - [`cron`](https://kaos.sh/g/ek.v13/cron) — Package provides methods for working with cron expressions - [`csv`](https://kaos.sh/g/ek.v13/csv) — Package with simple CSV parser compatible with default Go parser From c26c58e5fcbe526a94f3c7b331c7711ff7c3f6cc Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Tue, 30 Jul 2024 14:39:04 +0300 Subject: [PATCH 11/11] =?UTF-8?q?[cache/fs]=20SHA-1=20=E2=86=92=20SHA-256?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cache/fs/fs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cache/fs/fs.go b/cache/fs/fs.go index a2ac2e8c..fdb16fb1 100644 --- a/cache/fs/fs.go +++ b/cache/fs/fs.go @@ -12,7 +12,7 @@ package fs // ////////////////////////////////////////////////////////////////////////////////// // import ( - "crypto/sha1" + "crypto/sha256" "encoding/gob" "fmt" "hash" @@ -74,7 +74,7 @@ func New(config Config) (*Cache, error) { c := &Cache{ dir: config.Dir, expiration: config.DefaultExpiration, - hasher: sha1.New(), + hasher: sha256.New(), } if config.CleanupInterval != 0 {