Skip to content

Commit 583c8d6

Browse files
authored
Merge pull request #6 from ZaparooProject/feature/ignore-device-paths
Add device path ignore functionality to DetectAll
2 parents bb2af19 + e7fb178 commit 583c8d6

File tree

6 files changed

+224
-1
lines changed

6 files changed

+224
-1
lines changed

detection/blocklist.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package detection
2222

2323
import (
24+
"path/filepath"
2425
"strings"
2526
)
2627

@@ -125,3 +126,42 @@ func isHex(s string) bool {
125126
}
126127
return true
127128
}
129+
130+
// IsPathIgnored checks if a device path should be ignored.
131+
// Supports exact path matching and normalized path comparison.
132+
func IsPathIgnored(devicePath string, ignorePaths []string) bool {
133+
if devicePath == "" || len(ignorePaths) == 0 {
134+
return false
135+
}
136+
137+
// Normalize the device path for comparison
138+
normalizedDevice := normalizedPath(devicePath)
139+
140+
for _, ignorePath := range ignorePaths {
141+
if ignorePath == "" {
142+
continue
143+
}
144+
145+
normalizedIgnore := normalizedPath(ignorePath)
146+
147+
// Exact match
148+
if normalizedDevice == normalizedIgnore {
149+
return true
150+
}
151+
152+
// Also check original paths for exact match
153+
if devicePath == ignorePath {
154+
return true
155+
}
156+
}
157+
return false
158+
}
159+
160+
// normalizedPath normalizes a device path for comparison
161+
func normalizedPath(path string) string {
162+
// Clean the path to resolve any relative components
163+
cleaned := filepath.Clean(path)
164+
165+
// Convert to lowercase for case-insensitive comparison on Windows
166+
return strings.ToLower(cleaned)
167+
}

detection/detector.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ func (d DeviceInfo) String() string {
8383
type Options struct {
8484
// USB VID:PID pairs to skip (e.g., ["1234:5678", "ABCD:EF01"])
8585
Blocklist []string
86+
// Device paths to explicitly ignore (e.g., ["/dev/ttyUSB0", "COM2"])
87+
IgnorePaths []string
8688
// Which transports to check (empty = all)
8789
Transports []string
8890
// Cache TTL duration

detection/i2c/detect_linux.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ func detectBusDevices(ctx context.Context, bus i2cBusInfo, opts *detection.Optio
8484
func createDeviceInfo(ctx context.Context, busPath string, addr uint8, opts *detection.Options) (
8585
detection.DeviceInfo, bool,
8686
) {
87+
devicePath := fmt.Sprintf("%s:0x%02X", busPath, addr)
88+
89+
// Skip explicitly ignored device paths
90+
if detection.IsPathIgnored(devicePath, opts.IgnorePaths) {
91+
return detection.DeviceInfo{}, true
92+
}
93+
8794
// Check if this could be a PN532
8895
if addr != DefaultPN532Address && opts.Mode == detection.Passive {
8996
// In passive mode, only consider default PN532 address
@@ -92,7 +99,7 @@ func createDeviceInfo(ctx context.Context, busPath string, addr uint8, opts *det
9299

93100
device := detection.DeviceInfo{
94101
Transport: "i2c",
95-
Path: fmt.Sprintf("%s:0x%02X", busPath, addr),
102+
Path: devicePath,
96103
Name: fmt.Sprintf("I2C device at %s address 0x%02X", busPath, addr),
97104
Metadata: map[string]string{
98105
"bus": busPath,

detection/ignore_paths_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// go-pn532
2+
// Copyright (c) 2025 The Zaparoo Project Contributors.
3+
// SPDX-License-Identifier: LGPL-3.0-or-later
4+
//
5+
// This file is part of go-pn532.
6+
//
7+
// go-pn532 is free software; you can redistribute it and/or
8+
// modify it under the terms of the GNU Lesser General Public
9+
// License as published by the Free Software Foundation; either
10+
// version 3 of the License, or (at your option) any later version.
11+
//
12+
// go-pn532 is distributed in the hope that it will be useful,
13+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
// Lesser General Public License for more details.
16+
//
17+
// You should have received a copy of the GNU Lesser General Public License
18+
// along with go-pn532; if not, write to the Free Software Foundation,
19+
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20+
21+
package detection
22+
23+
import (
24+
"testing"
25+
)
26+
27+
func TestIsPathIgnored(t *testing.T) {
28+
t.Parallel()
29+
30+
tests := getPathIgnoredTests()
31+
32+
for _, tt := range tests {
33+
t.Run(tt.name, func(t *testing.T) {
34+
t.Parallel()
35+
result := IsPathIgnored(tt.devicePath, tt.ignorePaths)
36+
if result != tt.expected {
37+
t.Errorf("IsPathIgnored(%q, %v) = %v, want %v",
38+
tt.devicePath, tt.ignorePaths, result, tt.expected)
39+
}
40+
})
41+
}
42+
}
43+
44+
type pathIgnoredTest struct {
45+
name string
46+
devicePath string
47+
ignorePaths []string
48+
expected bool
49+
}
50+
51+
//nolint:funlen // Test data function, acceptable to be longer
52+
func getPathIgnoredTests() []pathIgnoredTest {
53+
basicTests := []pathIgnoredTest{
54+
{
55+
name: "empty ignore list",
56+
devicePath: "/dev/ttyUSB0",
57+
ignorePaths: []string{},
58+
expected: false,
59+
},
60+
{
61+
name: "empty device path",
62+
devicePath: "",
63+
ignorePaths: []string{"/dev/ttyUSB0"},
64+
expected: false,
65+
},
66+
{
67+
name: "exact match unix path",
68+
devicePath: "/dev/ttyUSB0",
69+
ignorePaths: []string{"/dev/ttyUSB0"},
70+
expected: true,
71+
},
72+
{
73+
name: "exact match windows path",
74+
devicePath: "COM2",
75+
ignorePaths: []string{"COM2"},
76+
expected: true,
77+
},
78+
}
79+
80+
caseTests := []pathIgnoredTest{
81+
{
82+
name: "case insensitive match",
83+
devicePath: "/dev/ttyUSB0",
84+
ignorePaths: []string{"/DEV/TTYUSB0"},
85+
expected: true,
86+
},
87+
{
88+
name: "windows case insensitive",
89+
devicePath: "com2",
90+
ignorePaths: []string{"COM2"},
91+
expected: true,
92+
},
93+
}
94+
95+
multipleTests := []pathIgnoredTest{
96+
{
97+
name: "no match",
98+
devicePath: "/dev/ttyUSB1",
99+
ignorePaths: []string{"/dev/ttyUSB0"},
100+
expected: false,
101+
},
102+
{
103+
name: "multiple paths with match",
104+
devicePath: "/dev/ttyUSB1",
105+
ignorePaths: []string{"/dev/ttyUSB0", "/dev/ttyUSB1", "COM2"},
106+
expected: true,
107+
},
108+
{
109+
name: "multiple paths no match",
110+
devicePath: "/dev/ttyUSB2",
111+
ignorePaths: []string{"/dev/ttyUSB0", "/dev/ttyUSB1", "COM2"},
112+
expected: false,
113+
},
114+
}
115+
116+
specialTests := []pathIgnoredTest{
117+
{
118+
name: "i2c path format",
119+
devicePath: "/dev/i2c-1:0x24",
120+
ignorePaths: []string{"/dev/i2c-1:0x24"},
121+
expected: true,
122+
},
123+
{
124+
name: "spi path format",
125+
devicePath: "/dev/spidev0.0",
126+
ignorePaths: []string{"/dev/spidev0.0"},
127+
expected: true,
128+
},
129+
{
130+
name: "path with relative components",
131+
devicePath: "/dev/../dev/ttyUSB0",
132+
ignorePaths: []string{"/dev/ttyUSB0"},
133+
expected: true,
134+
},
135+
{
136+
name: "empty strings in ignore list",
137+
devicePath: "/dev/ttyUSB0",
138+
ignorePaths: []string{"", "/dev/ttyUSB0", ""},
139+
expected: true,
140+
},
141+
}
142+
143+
result := make([]pathIgnoredTest, 0, len(basicTests)+len(caseTests)+len(multipleTests)+len(specialTests))
144+
result = append(result, basicTests...)
145+
result = append(result, caseTests...)
146+
result = append(result, multipleTests...)
147+
result = append(result, specialTests...)
148+
return result
149+
}
150+
151+
func TestOptionsWithIgnorePaths(t *testing.T) {
152+
t.Parallel()
153+
154+
opts := DefaultOptions()
155+
if opts.IgnorePaths != nil {
156+
t.Errorf("DefaultOptions().IgnorePaths should be nil, got %v", opts.IgnorePaths)
157+
}
158+
159+
// Test that we can set ignore paths
160+
opts.IgnorePaths = []string{"/dev/ttyUSB0", "COM2"}
161+
if len(opts.IgnorePaths) != 2 {
162+
t.Errorf("Expected 2 ignore paths, got %d", len(opts.IgnorePaths))
163+
}
164+
}

detection/spi/detector.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ func (*detector) Detect(ctx context.Context, opts *detection.Options) ([]detecti
159159
default:
160160
}
161161

162+
// Skip explicitly ignored device paths
163+
if detection.IsPathIgnored(config.Device, opts.IgnorePaths) {
164+
continue
165+
}
166+
162167
device := createDeviceInfo(config)
163168

164169
if probeAndUpdateDevice(ctx, config, &device, opts) {

detection/uart/detector.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ func (d *detector) filterPorts(ports []serialPort, opts *detection.Options) []se
8989
continue
9090
}
9191

92+
// Skip explicitly ignored device paths
93+
if detection.IsPathIgnored(port.Path, opts.IgnorePaths) {
94+
continue
95+
}
96+
9297
// Copy the loop variable to avoid memory aliasing
9398
portCopy := port
9499
// Apply platform-specific positive filtering

0 commit comments

Comments
 (0)