meu 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,169 @@
1
+ # This file is autogenerated by maturin v1.11.5
2
+ # To update, run
3
+ #
4
+ # maturin generate-ci github
5
+ #
6
+ # Note this pipeline has been modified to only run on tag pushes.
7
+ name: Deploy
8
+
9
+ on:
10
+ push:
11
+ tags:
12
+ - '*'
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ linux:
19
+ runs-on: ${{ matrix.platform.runner }}
20
+ strategy:
21
+ matrix:
22
+ platform:
23
+ - runner: ubuntu-22.04
24
+ target: x86_64
25
+ - runner: ubuntu-22.04
26
+ target: x86
27
+ - runner: ubuntu-22.04
28
+ target: aarch64
29
+ - runner: ubuntu-22.04
30
+ target: armv7
31
+ - runner: ubuntu-22.04
32
+ target: s390x
33
+ - runner: ubuntu-22.04
34
+ target: ppc64le
35
+ steps:
36
+ - uses: actions/checkout@v6
37
+ - name: Build wheels
38
+ uses: PyO3/maturin-action@v1
39
+ with:
40
+ target: ${{ matrix.platform.target }}
41
+ args: --release --out dist
42
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
43
+ manylinux: auto
44
+ - name: Upload wheels
45
+ uses: actions/upload-artifact@v5
46
+ with:
47
+ name: wheels-linux-${{ matrix.platform.target }}
48
+ path: dist
49
+
50
+ musllinux:
51
+ runs-on: ${{ matrix.platform.runner }}
52
+ strategy:
53
+ matrix:
54
+ platform:
55
+ - runner: ubuntu-22.04
56
+ target: x86_64
57
+ - runner: ubuntu-22.04
58
+ target: x86
59
+ - runner: ubuntu-22.04
60
+ target: aarch64
61
+ - runner: ubuntu-22.04
62
+ target: armv7
63
+ steps:
64
+ - uses: actions/checkout@v6
65
+ - name: Build wheels
66
+ uses: PyO3/maturin-action@v1
67
+ with:
68
+ target: ${{ matrix.platform.target }}
69
+ args: --release --out dist
70
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
71
+ manylinux: musllinux_1_2
72
+ - name: Upload wheels
73
+ uses: actions/upload-artifact@v5
74
+ with:
75
+ name: wheels-musllinux-${{ matrix.platform.target }}
76
+ path: dist
77
+
78
+ windows:
79
+ runs-on: ${{ matrix.platform.runner }}
80
+ strategy:
81
+ matrix:
82
+ platform:
83
+ - runner: windows-latest
84
+ target: x64
85
+ python_arch: x64
86
+ - runner: windows-latest
87
+ target: x86
88
+ python_arch: x86
89
+ - runner: windows-11-arm
90
+ target: aarch64
91
+ python_arch: arm64
92
+ steps:
93
+ - uses: actions/checkout@v6
94
+ - name: Build wheels
95
+ uses: PyO3/maturin-action@v1
96
+ with:
97
+ target: ${{ matrix.platform.target }}
98
+ args: --release --out dist
99
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
100
+ - name: Upload wheels
101
+ uses: actions/upload-artifact@v5
102
+ with:
103
+ name: wheels-windows-${{ matrix.platform.target }}
104
+ path: dist
105
+
106
+ macos:
107
+ runs-on: ${{ matrix.platform.runner }}
108
+ strategy:
109
+ matrix:
110
+ platform:
111
+ - runner: macos-15-intel
112
+ target: x86_64
113
+ - runner: macos-latest
114
+ target: aarch64
115
+ steps:
116
+ - uses: actions/checkout@v6
117
+ - name: Build wheels
118
+ uses: PyO3/maturin-action@v1
119
+ with:
120
+ target: ${{ matrix.platform.target }}
121
+ args: --release --out dist
122
+ sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
123
+ - name: Upload wheels
124
+ uses: actions/upload-artifact@v5
125
+ with:
126
+ name: wheels-macos-${{ matrix.platform.target }}
127
+ path: dist
128
+
129
+ sdist:
130
+ runs-on: ubuntu-latest
131
+ steps:
132
+ - uses: actions/checkout@v6
133
+ - name: Build sdist
134
+ uses: PyO3/maturin-action@v1
135
+ with:
136
+ command: sdist
137
+ args: --out dist
138
+ - name: Upload sdist
139
+ uses: actions/upload-artifact@v5
140
+ with:
141
+ name: wheels-sdist
142
+ path: dist
143
+
144
+ release:
145
+ name: Release
146
+ runs-on: ubuntu-latest
147
+ if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
148
+ needs: [linux, musllinux, windows, macos, sdist]
149
+ permissions:
150
+ # Use to sign the release artifacts
151
+ id-token: write
152
+ # Used to upload release artifacts
153
+ contents: write
154
+ # Used to generate artifact attestation
155
+ attestations: write
156
+ steps:
157
+ - uses: actions/download-artifact@v6
158
+ - name: Generate artifact attestation
159
+ uses: actions/attest-build-provenance@v3
160
+ with:
161
+ subject-path: 'wheels-*/*'
162
+ - name: Install uv
163
+ if: ${{ startsWith(github.ref, 'refs/tags/') }}
164
+ uses: astral-sh/setup-uv@v7
165
+ - name: Publish to PyPI
166
+ if: ${{ startsWith(github.ref, 'refs/tags/') }}
167
+ run: uv publish 'wheels-*/*'
168
+ env:
169
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,31 @@
1
+ name: Develop
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main" ]
8
+
9
+ env:
10
+ CARGO_TERM_COLOR: always
11
+
12
+ jobs:
13
+ validate-rust:
14
+ name: Validate Rust
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - name: Checkout repository
19
+ uses: actions/checkout@v5
20
+
21
+ - name: Build
22
+ run: cargo build --verbose
23
+
24
+ - name: Run tests
25
+ run: cargo test --verbose
26
+
27
+ - name: Check formatting
28
+ run: cargo fmt -- --check
29
+
30
+ - name: Lint
31
+ run: cargo clippy -- -D warnings -W clippy::pedantic
meu-0.1.0/.gitignore ADDED
@@ -0,0 +1,72 @@
1
+ /target
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ .pytest_cache/
6
+ *.py[cod]
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ .venv/
14
+ env/
15
+ bin/
16
+ build/
17
+ develop-eggs/
18
+ dist/
19
+ eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ include/
26
+ man/
27
+ venv/
28
+ *.egg-info/
29
+ .installed.cfg
30
+ *.egg
31
+
32
+ # Installer logs
33
+ pip-log.txt
34
+ pip-delete-this-directory.txt
35
+ pip-selfcheck.json
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .coverage
41
+ .cache
42
+ nosetests.xml
43
+ coverage.xml
44
+
45
+ # Translations
46
+ *.mo
47
+
48
+ # Mr Developer
49
+ .mr.developer.cfg
50
+ .project
51
+ .pydevproject
52
+
53
+ # Rope
54
+ .ropeproject
55
+
56
+ # Django stuff:
57
+ *.log
58
+ *.pot
59
+
60
+ .DS_Store
61
+
62
+ # Sphinx documentation
63
+ docs/_build/
64
+
65
+ # PyCharm
66
+ .idea/
67
+
68
+ # VSCode
69
+ .vscode/
70
+
71
+ # Pyenv
72
+ .python-version
@@ -0,0 +1,36 @@
1
+ # Contributing to meu
2
+
3
+ ## Prerequisites
4
+
5
+ Meu is written in Rust. You'll need to install the [Rust toolchain](https://www.rust-lang.org/tools/install) to build and contribute.
6
+
7
+ ### Code Quality
8
+
9
+ To lint Rust code, run:
10
+
11
+ ```bash
12
+ cargo clippy --fix --allow-dirty -- -W clippy::pedantic
13
+ ```
14
+
15
+ To format Rust code, run:
16
+
17
+ ```bash
18
+ cargo fmt
19
+ ```
20
+
21
+ ### Unit Tests
22
+
23
+ To run unit tests, run:
24
+
25
+ ```bash
26
+ cargo test
27
+ ```
28
+
29
+ ### Publishing
30
+
31
+ Publishing is done using [`maturin`](https://www.maturin.rs/) automatically in the [deploy.yaml](.github/workflows/deploy.yaml) GitHub Actions workflow.
32
+ This workflow is triggered when a new tag is pushed to the repository.
33
+
34
+ ### Versioning
35
+
36
+ Meu uses [Semantic Versioning](https://semver.org/).
meu-0.1.0/Cargo.lock ADDED
@@ -0,0 +1,245 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "aho-corasick"
7
+ version = "1.1.4"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
10
+ dependencies = [
11
+ "memchr",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "anstream"
16
+ version = "0.6.21"
17
+ source = "registry+https://github.com/rust-lang/crates.io-index"
18
+ checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
19
+ dependencies = [
20
+ "anstyle",
21
+ "anstyle-parse",
22
+ "anstyle-query",
23
+ "anstyle-wincon",
24
+ "colorchoice",
25
+ "is_terminal_polyfill",
26
+ "utf8parse",
27
+ ]
28
+
29
+ [[package]]
30
+ name = "anstyle"
31
+ version = "1.0.13"
32
+ source = "registry+https://github.com/rust-lang/crates.io-index"
33
+ checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
34
+
35
+ [[package]]
36
+ name = "anstyle-parse"
37
+ version = "0.2.7"
38
+ source = "registry+https://github.com/rust-lang/crates.io-index"
39
+ checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
40
+ dependencies = [
41
+ "utf8parse",
42
+ ]
43
+
44
+ [[package]]
45
+ name = "anstyle-query"
46
+ version = "1.1.5"
47
+ source = "registry+https://github.com/rust-lang/crates.io-index"
48
+ checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
49
+ dependencies = [
50
+ "windows-sys",
51
+ ]
52
+
53
+ [[package]]
54
+ name = "anstyle-wincon"
55
+ version = "3.0.11"
56
+ source = "registry+https://github.com/rust-lang/crates.io-index"
57
+ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
58
+ dependencies = [
59
+ "anstyle",
60
+ "once_cell_polyfill",
61
+ "windows-sys",
62
+ ]
63
+
64
+ [[package]]
65
+ name = "anyhow"
66
+ version = "1.0.101"
67
+ source = "registry+https://github.com/rust-lang/crates.io-index"
68
+ checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
69
+
70
+ [[package]]
71
+ name = "clap"
72
+ version = "4.5.58"
73
+ source = "registry+https://github.com/rust-lang/crates.io-index"
74
+ checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
75
+ dependencies = [
76
+ "clap_builder",
77
+ "clap_derive",
78
+ ]
79
+
80
+ [[package]]
81
+ name = "clap_builder"
82
+ version = "4.5.58"
83
+ source = "registry+https://github.com/rust-lang/crates.io-index"
84
+ checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
85
+ dependencies = [
86
+ "anstream",
87
+ "anstyle",
88
+ "clap_lex",
89
+ "strsim",
90
+ ]
91
+
92
+ [[package]]
93
+ name = "clap_derive"
94
+ version = "4.5.55"
95
+ source = "registry+https://github.com/rust-lang/crates.io-index"
96
+ checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
97
+ dependencies = [
98
+ "heck",
99
+ "proc-macro2",
100
+ "quote",
101
+ "syn",
102
+ ]
103
+
104
+ [[package]]
105
+ name = "clap_lex"
106
+ version = "1.0.0"
107
+ source = "registry+https://github.com/rust-lang/crates.io-index"
108
+ checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
109
+
110
+ [[package]]
111
+ name = "colorchoice"
112
+ version = "1.0.4"
113
+ source = "registry+https://github.com/rust-lang/crates.io-index"
114
+ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
115
+
116
+ [[package]]
117
+ name = "glob"
118
+ version = "0.3.3"
119
+ source = "registry+https://github.com/rust-lang/crates.io-index"
120
+ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
121
+
122
+ [[package]]
123
+ name = "heck"
124
+ version = "0.5.0"
125
+ source = "registry+https://github.com/rust-lang/crates.io-index"
126
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
127
+
128
+ [[package]]
129
+ name = "is_terminal_polyfill"
130
+ version = "1.70.2"
131
+ source = "registry+https://github.com/rust-lang/crates.io-index"
132
+ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
133
+
134
+ [[package]]
135
+ name = "memchr"
136
+ version = "2.8.0"
137
+ source = "registry+https://github.com/rust-lang/crates.io-index"
138
+ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
139
+
140
+ [[package]]
141
+ name = "meu"
142
+ version = "0.1.0"
143
+ dependencies = [
144
+ "anyhow",
145
+ "clap",
146
+ "glob",
147
+ "regex",
148
+ ]
149
+
150
+ [[package]]
151
+ name = "once_cell_polyfill"
152
+ version = "1.70.2"
153
+ source = "registry+https://github.com/rust-lang/crates.io-index"
154
+ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
155
+
156
+ [[package]]
157
+ name = "proc-macro2"
158
+ version = "1.0.106"
159
+ source = "registry+https://github.com/rust-lang/crates.io-index"
160
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
161
+ dependencies = [
162
+ "unicode-ident",
163
+ ]
164
+
165
+ [[package]]
166
+ name = "quote"
167
+ version = "1.0.44"
168
+ source = "registry+https://github.com/rust-lang/crates.io-index"
169
+ checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
170
+ dependencies = [
171
+ "proc-macro2",
172
+ ]
173
+
174
+ [[package]]
175
+ name = "regex"
176
+ version = "1.12.3"
177
+ source = "registry+https://github.com/rust-lang/crates.io-index"
178
+ checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
179
+ dependencies = [
180
+ "aho-corasick",
181
+ "memchr",
182
+ "regex-automata",
183
+ "regex-syntax",
184
+ ]
185
+
186
+ [[package]]
187
+ name = "regex-automata"
188
+ version = "0.4.14"
189
+ source = "registry+https://github.com/rust-lang/crates.io-index"
190
+ checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
191
+ dependencies = [
192
+ "aho-corasick",
193
+ "memchr",
194
+ "regex-syntax",
195
+ ]
196
+
197
+ [[package]]
198
+ name = "regex-syntax"
199
+ version = "0.8.9"
200
+ source = "registry+https://github.com/rust-lang/crates.io-index"
201
+ checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
202
+
203
+ [[package]]
204
+ name = "strsim"
205
+ version = "0.11.1"
206
+ source = "registry+https://github.com/rust-lang/crates.io-index"
207
+ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
208
+
209
+ [[package]]
210
+ name = "syn"
211
+ version = "2.0.115"
212
+ source = "registry+https://github.com/rust-lang/crates.io-index"
213
+ checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12"
214
+ dependencies = [
215
+ "proc-macro2",
216
+ "quote",
217
+ "unicode-ident",
218
+ ]
219
+
220
+ [[package]]
221
+ name = "unicode-ident"
222
+ version = "1.0.23"
223
+ source = "registry+https://github.com/rust-lang/crates.io-index"
224
+ checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
225
+
226
+ [[package]]
227
+ name = "utf8parse"
228
+ version = "0.2.2"
229
+ source = "registry+https://github.com/rust-lang/crates.io-index"
230
+ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
231
+
232
+ [[package]]
233
+ name = "windows-link"
234
+ version = "0.2.1"
235
+ source = "registry+https://github.com/rust-lang/crates.io-index"
236
+ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
237
+
238
+ [[package]]
239
+ name = "windows-sys"
240
+ version = "0.61.2"
241
+ source = "registry+https://github.com/rust-lang/crates.io-index"
242
+ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
243
+ dependencies = [
244
+ "windows-link",
245
+ ]
meu-0.1.0/Cargo.toml ADDED
@@ -0,0 +1,13 @@
1
+ [package]
2
+ name = "meu"
3
+ version = "0.1.0"
4
+ edition = "2024"
5
+ readme = "README.md"
6
+
7
+ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
+
9
+ [dependencies]
10
+ anyhow = "1.0"
11
+ clap = { version = "4.5", features = ["derive"] }
12
+ glob = "0.3"
13
+ regex = "1.12"
meu-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Campbell Brown
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
meu-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.4
2
+ Name: meu
3
+ Version: 0.1.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
7
+ License-File: LICENSE
8
+ Summary: A Rust-powered set of convenient command-line tools for everyday tasks.
9
+ Keywords: rust,ssh
10
+ Author-email: Campbell Brown <campbellbrown093@gmail.com>
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
13
+ Project-URL: homepage, https://github.com/campbellmbrown/meu
14
+ Project-URL: issues, https://github.com/campbellmbrown/meu/issues
15
+ Project-URL: source, https://github.com/campbellmbrown/meu
16
+
17
+ # Meu
18
+
19
+ [![PyPI - Version](https://img.shields.io/pypi/v/meu?style=for-the-badge)](https://pypi.org/project/meu/)
20
+ [![License](https://img.shields.io/github/license/campbellmbrown/meu?style=for-the-badge)](LICENSE)
21
+ [![Deploy](https://img.shields.io/github/actions/workflow/status/campbellmbrown/meu/deploy.yaml?style=for-the-badge&logo=github&label=Deploy)](https://github.com/campbellmbrown/meu/actions/workflows/deploy.yaml)
22
+ [![Develop](https://img.shields.io/github/actions/workflow/status/campbellmbrown/meu/develop.yaml?branch=main&style=for-the-badge&logo=github&label=Develop)](https://github.com/campbellmbrown/meu/actions/workflows/develop.yaml)
23
+
24
+ ![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=for-the-badge&logo=rust&logoColor=white)
25
+
26
+ ## Overview
27
+
28
+ Meu is a set of convenient command-line tools for everyday tasks, written in Rust.
29
+
30
+ ## Installation
31
+
32
+ Meu is available on [PyPI](https://pypi.org/project/meu/).
33
+
34
+ You can install it using your preferred Python package manager. For example, using `uv`:
35
+
36
+ ```bash
37
+ uv tool install meu
38
+ ```
39
+
40
+ To upgrade:
41
+
42
+ ```bash
43
+ uv tool update meu
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ### `scan`
49
+
50
+ Scan for pattern matches in files using either a single file or files in a directory.
51
+
52
+ To scan a single file:
53
+
54
+ ```bash
55
+ meu scan --file <FILE> --pattern <PATTERN> [--first-match]
56
+ ```
57
+
58
+ * `--file <FILE>`: The file to scan
59
+ * `--pattern <PATTERN>`: The pattern to search for (supports regular expressions)
60
+ * `--first-match`: Stop on first match in the file
61
+
62
+ To scan files in a directory:
63
+
64
+ ```bash
65
+ meu scan --dir <DIR> --pattern <PATTERN> [--glob <GLOB>] [--first-match]
66
+ ```
67
+
68
+ * `--dir <DIR>`: The directory to scan
69
+ * `--pattern <PATTERN>`: The pattern to search for (supports regular expressions)
70
+ * `--glob <GLOB>`: The glob pattern to match files (e.g. `**/*.txt`). Default is `*`
71
+ * `--first-match`: Stop on first match in any file
72
+
73
+ ## Contributing
74
+
75
+ Contributions are welcome! Please see the [contributing guide](CONTRIBUTING.md) for details.
76
+
meu-0.1.0/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # Meu
2
+
3
+ [![PyPI - Version](https://img.shields.io/pypi/v/meu?style=for-the-badge)](https://pypi.org/project/meu/)
4
+ [![License](https://img.shields.io/github/license/campbellmbrown/meu?style=for-the-badge)](LICENSE)
5
+ [![Deploy](https://img.shields.io/github/actions/workflow/status/campbellmbrown/meu/deploy.yaml?style=for-the-badge&logo=github&label=Deploy)](https://github.com/campbellmbrown/meu/actions/workflows/deploy.yaml)
6
+ [![Develop](https://img.shields.io/github/actions/workflow/status/campbellmbrown/meu/develop.yaml?branch=main&style=for-the-badge&logo=github&label=Develop)](https://github.com/campbellmbrown/meu/actions/workflows/develop.yaml)
7
+
8
+ ![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=for-the-badge&logo=rust&logoColor=white)
9
+
10
+ ## Overview
11
+
12
+ Meu is a set of convenient command-line tools for everyday tasks, written in Rust.
13
+
14
+ ## Installation
15
+
16
+ Meu is available on [PyPI](https://pypi.org/project/meu/).
17
+
18
+ You can install it using your preferred Python package manager. For example, using `uv`:
19
+
20
+ ```bash
21
+ uv tool install meu
22
+ ```
23
+
24
+ To upgrade:
25
+
26
+ ```bash
27
+ uv tool update meu
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### `scan`
33
+
34
+ Scan for pattern matches in files using either a single file or files in a directory.
35
+
36
+ To scan a single file:
37
+
38
+ ```bash
39
+ meu scan --file <FILE> --pattern <PATTERN> [--first-match]
40
+ ```
41
+
42
+ * `--file <FILE>`: The file to scan
43
+ * `--pattern <PATTERN>`: The pattern to search for (supports regular expressions)
44
+ * `--first-match`: Stop on first match in the file
45
+
46
+ To scan files in a directory:
47
+
48
+ ```bash
49
+ meu scan --dir <DIR> --pattern <PATTERN> [--glob <GLOB>] [--first-match]
50
+ ```
51
+
52
+ * `--dir <DIR>`: The directory to scan
53
+ * `--pattern <PATTERN>`: The pattern to search for (supports regular expressions)
54
+ * `--glob <GLOB>`: The glob pattern to match files (e.g. `**/*.txt`). Default is `*`
55
+ * `--first-match`: Stop on first match in any file
56
+
57
+ ## Contributing
58
+
59
+ Contributions are welcome! Please see the [contributing guide](CONTRIBUTING.md) for details.
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.11,<2.0"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "meu"
7
+ description = "A Rust-powered set of convenient command-line tools for everyday tasks."
8
+ authors = [{ name = "Campbell Brown", email = "campbellbrown093@gmail.com" }]
9
+ readme = "README.md"
10
+ license-files = ["LICENSE"]
11
+ keywords = ["rust", "ssh"]
12
+ requires-python = ">=3.8"
13
+ classifiers = [
14
+ "Programming Language :: Rust",
15
+ "Programming Language :: Python :: Implementation :: CPython",
16
+ "Programming Language :: Python :: Implementation :: PyPy",
17
+ ]
18
+ dynamic = ["version"]
19
+
20
+ [project.urls]
21
+ homepage = "https://github.com/campbellmbrown/meu"
22
+ source = "https://github.com/campbellmbrown/meu"
23
+ issues = "https://github.com/campbellmbrown/meu/issues"
meu-0.1.0/src/main.rs ADDED
@@ -0,0 +1,83 @@
1
+ mod scan;
2
+
3
+ use anyhow::Result;
4
+ use clap::{Parser, Subcommand};
5
+
6
+ #[derive(Parser)]
7
+ #[clap(name = "meu")]
8
+ struct Cli {
9
+ #[clap(subcommand)]
10
+ command: Commands,
11
+ }
12
+
13
+ #[derive(Subcommand)]
14
+ enum Commands {
15
+ /// Scan files for a regex pattern
16
+ #[clap(group(
17
+ clap::ArgGroup::new("target")
18
+ .required(true)
19
+ .args(&["dir", "file"])
20
+ ))]
21
+ Scan {
22
+ /// Directory to search. Mutually exclusive with --file
23
+ #[clap(short, long, group = "target")]
24
+ dir: Option<String>,
25
+
26
+ /// File to search. Mutually exclusive with --dir
27
+ #[clap(short, long, group = "target")]
28
+ file: Option<String>,
29
+
30
+ /// Regex pattern to search for
31
+ #[clap(short, long)]
32
+ pattern: String,
33
+
34
+ /// File glob pattern. Can only be used with --dir
35
+ #[clap(
36
+ short,
37
+ long,
38
+ requires = "dir",
39
+ conflicts_with = "file",
40
+ default_value = "*"
41
+ )]
42
+ glob: String,
43
+
44
+ /// Stop on first match in any file
45
+ #[clap(short = 'F', long)]
46
+ first_match: bool,
47
+ },
48
+ }
49
+
50
+ fn main() -> Result<()> {
51
+ let cli = Cli::parse();
52
+
53
+ match cli.command {
54
+ Commands::Scan {
55
+ dir,
56
+ file,
57
+ pattern,
58
+ glob,
59
+ first_match,
60
+ } => {
61
+ let target = if let Some(file) = file {
62
+ scan::ScanTarget::File { path: file }
63
+ } else if let Some(dir) = dir {
64
+ scan::ScanTarget::Dir {
65
+ dir,
66
+ file_glob: glob,
67
+ }
68
+ } else {
69
+ panic!("Either --dir or --file must be provided");
70
+ };
71
+
72
+ let args = scan::ScanArgs {
73
+ target,
74
+ pattern,
75
+ stop_on_first_match: first_match,
76
+ };
77
+
78
+ scan::run(&args)?;
79
+ }
80
+ }
81
+
82
+ Ok(())
83
+ }
meu-0.1.0/src/scan.rs ADDED
@@ -0,0 +1,51 @@
1
+ use anyhow::Result;
2
+ use glob::glob;
3
+ use regex::Regex;
4
+ use std::{fs, path::Path};
5
+
6
+ pub enum ScanTarget {
7
+ Dir { dir: String, file_glob: String },
8
+ File { path: String },
9
+ }
10
+
11
+ pub struct ScanArgs {
12
+ pub target: ScanTarget,
13
+ pub pattern: String,
14
+ pub stop_on_first_match: bool,
15
+ }
16
+
17
+ pub fn run(args: &ScanArgs) -> Result<()> {
18
+ let re = Regex::new(&args.pattern)?;
19
+ match &args.target {
20
+ ScanTarget::Dir { dir, file_glob } => {
21
+ let search_pattern = format!("{dir}/{file_glob}");
22
+ for entry in glob(&search_pattern)? {
23
+ let path = entry?;
24
+ if path.is_file() && process_file(&path, &re, args.stop_on_first_match)? {
25
+ return Ok(());
26
+ }
27
+ }
28
+ }
29
+ ScanTarget::File { path } => {
30
+ let path = Path::new(path);
31
+ if !path.is_file() {
32
+ anyhow::bail!("{} is not a valid file", path.display());
33
+ }
34
+ process_file(path, &re, args.stop_on_first_match)?;
35
+ }
36
+ }
37
+ Ok(())
38
+ }
39
+
40
+ fn process_file(path: &Path, re: &Regex, stop_on_first_match: bool) -> Result<bool> {
41
+ let content = fs::read_to_string(path)?;
42
+ for (i, line) in content.lines().enumerate() {
43
+ if re.is_match(line) {
44
+ println!("{}:{}: {}", path.display(), i + 1, line);
45
+ if stop_on_first_match {
46
+ return Ok(true);
47
+ }
48
+ }
49
+ }
50
+ Ok(false)
51
+ }