Skip to content

Commit 7dda955

Browse files
committed
doc: disambiguate overlapping module and bridge names
Names like "os" and "json" exist as both a stdlib module (use) and a bridge package (import). Previously, "rugo doc os" always showed the stdlib module with no way to view the bridge package. Now rugo doc detects the conflict and asks you to be explicit: rugo doc use:os # stdlib module rugo doc import:os # bridge package Unambiguous names like "http" or "strings" continue to work without a prefix.
1 parent 2b5b491 commit 7dda955

File tree

4 files changed

+130
-3
lines changed

4 files changed

+130
-3
lines changed

cmd/cmd.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func Execute(version string) {
142142
{
143143
Name: "doc",
144144
Usage: "Show documentation for files, modules, and bridge packages",
145-
ArgsUsage: "<file.rugo|module|package> [symbol]",
145+
ArgsUsage: "<file.rugo|[use:|import:]module|package> [symbol]",
146146
Flags: []cli.Flag{
147147
&cli.BoolFlag{
148148
Name: "all",
@@ -497,6 +497,39 @@ func docAction(ctx context.Context, cmd *cli.Command) error {
497497
symbol = cmd.Args().Get(1)
498498
}
499499

500+
// Parse use:/import: prefix for disambiguation
501+
forceModule, forceBridge := false, false
502+
if strings.HasPrefix(target, "use:") {
503+
forceModule = true
504+
target = strings.TrimPrefix(target, "use:")
505+
} else if strings.HasPrefix(target, "import:") {
506+
forceBridge = true
507+
target = strings.TrimPrefix(target, "import:")
508+
}
509+
510+
// Forced module lookup (use:)
511+
if forceModule {
512+
m, ok := modules.Get(target)
513+
if !ok {
514+
return fmt.Errorf("module %q not found", target)
515+
}
516+
docOutput(rugodoc.FormatModule(m))
517+
return nil
518+
}
519+
520+
// Forced bridge lookup (import:)
521+
if forceBridge {
522+
if pkg, ok := gobridge.LookupByNS(target); ok {
523+
docOutput(rugodoc.FormatBridgePackage(pkg))
524+
return nil
525+
}
526+
if pkg := gobridge.GetPackage(target); pkg != nil {
527+
docOutput(rugodoc.FormatBridgePackage(pkg))
528+
return nil
529+
}
530+
return fmt.Errorf("bridge package %q not found", target)
531+
}
532+
500533
// Mode 1: Rugo source file
501534
if compiler.IsRugoFile(target) {
502535
fd, err := rugodoc.ExtractFile(target)
@@ -520,14 +553,23 @@ func docAction(ctx context.Context, cmd *cli.Command) error {
520553
return docLocalDir(target, symbol)
521554
}
522555

556+
// Check for ambiguity: name exists as both module and bridge package
557+
_, isModule := modules.Get(target)
558+
_, isBridge := gobridge.LookupByNS(target)
559+
if isModule && isBridge {
560+
return fmt.Errorf("%q is both a module and a bridge package, use \"rugo doc use:%s\" or \"rugo doc import:%s\"", target, target, target)
561+
}
562+
523563
// Mode 3: stdlib module (use)
524-
if m, ok := modules.Get(target); ok {
564+
if isModule {
565+
m, _ := modules.Get(target)
525566
docOutput(rugodoc.FormatModule(m))
526567
return nil
527568
}
528569

529570
// Mode 4: bridge package by namespace (import)
530-
if pkg, ok := gobridge.LookupByNS(target); ok {
571+
if isBridge {
572+
pkg, _ := gobridge.LookupByNS(target)
531573
docOutput(rugodoc.FormatBridgePackage(pkg))
532574
return nil
533575
}

docs/language.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,5 +834,7 @@ rugo doc file.rugo # all docs in a file
834834
rugo doc file.rugo factorial # specific symbol
835835
rugo doc http # stdlib module
836836
rugo doc strings # bridge package
837+
rugo doc use:os # force stdlib module (when name is ambiguous)
838+
rugo doc import:os # force bridge package (when name is ambiguous)
837839
rugo doc --all # list everything
838840
```

docs/quickstart/20-doc-comments.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ rugo doc http
3838
rugo doc strings
3939
rugo doc time
4040

41+
# Disambiguate when a name exists as both module and bridge (e.g. os, json)
42+
rugo doc use:os # force stdlib module
43+
rugo doc import:os # force bridge package
44+
4145
# Show docs for a remote module
4246
rugo doc github.com/user/repo
4347

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# RATS: Test rugo doc disambiguation for overlapping module/bridge names
2+
use "test"
3+
use "str"
4+
5+
# Test: rugo doc with ambiguous name (os is both module and bridge) shows error
6+
rats "rugo doc ambiguous name shows disambiguation error"
7+
result = test.run("rugo doc os")
8+
test.assert_neq(result["status"], 0)
9+
test.assert_contains(result["output"], "is both a module and a bridge package")
10+
test.assert_contains(result["output"], "use:os")
11+
test.assert_contains(result["output"], "import:os")
12+
end
13+
14+
# Test: rugo doc use: prefix shows stdlib module
15+
rats "rugo doc use:os shows module docs"
16+
result = test.run("NO_COLOR=1 rugo doc use:os")
17+
test.assert_eq(result["status"], 0)
18+
test.assert_contains(result["output"], "module os")
19+
test.assert_contains(result["output"], "os.exec")
20+
end
21+
22+
# Test: rugo doc import: prefix shows bridge package
23+
rats "rugo doc import:os shows bridge docs"
24+
result = test.run("NO_COLOR=1 rugo doc import:os")
25+
test.assert_eq(result["status"], 0)
26+
test.assert_contains(result["output"], "package os")
27+
test.assert_contains(result["output"], "Go: os")
28+
end
29+
30+
# Test: rugo doc json is also ambiguous
31+
rats "rugo doc json shows disambiguation error"
32+
result = test.run("rugo doc json")
33+
test.assert_neq(result["status"], 0)
34+
test.assert_contains(result["output"], "is both a module and a bridge package")
35+
test.assert_contains(result["output"], "use:json")
36+
test.assert_contains(result["output"], "import:json")
37+
end
38+
39+
# Test: rugo doc use:json shows module
40+
rats "rugo doc use:json shows module docs"
41+
result = test.run("NO_COLOR=1 rugo doc use:json")
42+
test.assert_eq(result["status"], 0)
43+
test.assert_contains(result["output"], "module json")
44+
end
45+
46+
# Test: rugo doc import:json shows bridge
47+
rats "rugo doc import:json shows bridge docs"
48+
result = test.run("NO_COLOR=1 rugo doc import:json")
49+
test.assert_eq(result["status"], 0)
50+
test.assert_contains(result["output"], "package json")
51+
end
52+
53+
# Test: rugo doc use: with nonexistent module fails
54+
rats "rugo doc use:nonexistent fails"
55+
result = test.run("rugo doc use:nonexistent")
56+
test.assert_neq(result["status"], 0)
57+
test.assert_contains(result["output"], "module \"nonexistent\" not found")
58+
end
59+
60+
# Test: rugo doc import: with nonexistent bridge fails
61+
rats "rugo doc import:nonexistent fails"
62+
result = test.run("rugo doc import:nonexistent")
63+
test.assert_neq(result["status"], 0)
64+
test.assert_contains(result["output"], "bridge package \"nonexistent\" not found")
65+
end
66+
67+
# Test: unambiguous names still auto-resolve without prefix
68+
rats "rugo doc str auto-resolves to module"
69+
result = test.run("NO_COLOR=1 rugo doc str")
70+
test.assert_eq(result["status"], 0)
71+
test.assert_contains(result["output"], "module str")
72+
end
73+
74+
# Test: unambiguous bridge auto-resolves
75+
rats "rugo doc strings auto-resolves to bridge"
76+
result = test.run("NO_COLOR=1 rugo doc strings")
77+
test.assert_eq(result["status"], 0)
78+
test.assert_contains(result["output"], "package strings")
79+
end

0 commit comments

Comments
 (0)