Skip to content

Commit a5125be

Browse files
committed
added docs for resources
1 parent ecab5fe commit a5125be

4 files changed

Lines changed: 286 additions & 2 deletions

File tree

content/docs/aesh/completers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ The generators introspect the command model and produce:
256256
- **Option aliases** (e.g., `--ea` for `--enableassertions`)
257257
- **Negatable options** (e.g., `--no-verbose` for `--verbose`)
258258
- **Default value completions** (offered when completing option values)
259-
- **File path completion** for `File`, `Path`, and `Resource`-typed options
259+
- **File path completion** for `File`, `Path`, and [`Resource`](../resources)-typed options
260260
- **Subcommand names** for group commands
261261
- **Positional argument completion** (file completion for `@Argument`/`@Arguments` with file types)
262262

content/docs/aesh/converters.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ The `ConverterInvocation` provides:
2828
- `float` / `Float`, `double` / `Double`
2929
- `boolean` / `Boolean` (accepts `true`, `false`, `yes`, `no`)
3030
- `char` / `Character`
31-
- `File`, `Path`
31+
- `File`, `Path`, [`Resource`](../resources)
3232
- Any `Enum` type (matched by name, case-insensitive)
3333

3434
## Enum Conversion

content/docs/aesh/options.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,23 @@ public static class PathConverter implements Converter<Path> {
977977
}
978978
```
979979

980+
## File-Typed Options
981+
982+
When an option field is typed as `Resource`, `File`, or `Path`, Aesh automatically provides file path tab completion and type conversion -- no completer or converter needed:
983+
984+
```java
985+
@Option(description = "Output file")
986+
private Resource output; // automatic file completion + Resource API
987+
988+
@Option(description = "Config file")
989+
private File config; // automatic file completion + java.io.File
990+
991+
@Option(description = "Data directory")
992+
private Path dataDir; // automatic file completion + java.nio.file.Path
993+
```
994+
995+
`Resource` is recommended when your command needs to read, write, or inspect files, as it provides a richer API with glob expansion, directory filtering, and I/O streams. See [File and Resource Handling](../resources) for the full API and filtering options.
996+
980997
## Short Names
981998

982999
The `shortName` defines the single-character option:

content/docs/aesh/resources.md

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
---
2+
date: '2026-04-29T14:00:00+02:00'
3+
draft: false
4+
title: 'File and Resource Handling'
5+
weight: 12
6+
---
7+
8+
When an `@Option`, `@Argument`, or `@Arguments` field is typed as `Resource`, `File`, or `Path`, Aesh automatically provides file path completion, string-to-file conversion, and `<file>` usage hints in help text -- with no extra configuration needed.
9+
10+
## Quick Comparison
11+
12+
```java
13+
// Plain string — no completion, no path resolution
14+
@Option(description = "Output path")
15+
private String output;
16+
17+
// Resource — automatic file completion + path resolution + I/O API
18+
@Option(description = "Output path")
19+
private Resource output;
20+
21+
// File — automatic file completion + conversion
22+
@Option(description = "Output path")
23+
private File output;
24+
25+
// Path — automatic file completion + conversion
26+
@Option(description = "Output path")
27+
private Path output;
28+
```
29+
30+
All three file-typed variants (`Resource`, `File`, `Path`) gain:
31+
32+
| Feature | String | Resource / File / Path |
33+
|---|---|---|
34+
| Tab completion | None | File paths from filesystem |
35+
| Type conversion | None | Automatic string-to-type |
36+
| Help text | `<output>` | `<file>` |
37+
| Shell redirect completion | No | Yes (after `>`, `>>`, `<`) |
38+
39+
## Resource Interface
40+
41+
`Resource` is Aesh's filesystem abstraction. It provides a richer API than `File` or `Path` with built-in glob expansion, filtering, and I/O streams:
42+
43+
```java
44+
@CommandDefinition(name = "cat", description = "Display file contents")
45+
public class CatCommand implements Command<CommandInvocation> {
46+
47+
@Argument(description = "File to display", required = true)
48+
private Resource file;
49+
50+
@Override
51+
public CommandResult execute(CommandInvocation invocation) throws CommandException {
52+
if (!file.exists()) {
53+
invocation.println("File not found: " + file.getName());
54+
return CommandResult.FAILURE;
55+
}
56+
try (BufferedReader reader = new BufferedReader(
57+
new InputStreamReader(file.read()))) {
58+
String line;
59+
while ((line = reader.readLine()) != null) {
60+
invocation.println(line);
61+
}
62+
} catch (IOException e) {
63+
throw new CommandException("Failed to read file", e);
64+
}
65+
return CommandResult.SUCCESS;
66+
}
67+
}
68+
```
69+
70+
Tab completion works immediately:
71+
72+
```
73+
myshell$ cat sr<TAB>
74+
src/
75+
myshell$ cat src/main<TAB>
76+
src/main/java/ src/main/resources/
77+
```
78+
79+
### API Reference
80+
81+
**Path and name:**
82+
83+
| Method | Returns | Description |
84+
|---|---|---|
85+
| `getName()` | `String` | Filename only (no directory) |
86+
| `getAbsolutePath()` | `String` | Full absolute path |
87+
| `getParent()` | `Resource` | Parent directory |
88+
| `newInstance(String)` | `Resource` | Factory -- creates a new Resource from a path |
89+
90+
**Type checks:**
91+
92+
| Method | Returns | Description |
93+
|---|---|---|
94+
| `exists()` | `boolean` | Does the file/directory exist? |
95+
| `isLeaf()` | `boolean` | Is it a file (not a directory)? |
96+
| `isDirectory()` | `boolean` | Is it a directory? |
97+
| `isSymbolicLink()` | `boolean` | Is it a symbolic link? |
98+
| `readSymbolicLink()` | `Resource` | Resolves symlink target |
99+
100+
**Directory listing:**
101+
102+
| Method | Returns | Description |
103+
|---|---|---|
104+
| `list()` | `List<Resource>` | Lists directory contents |
105+
| `list(ResourceFilter)` | `List<Resource>` | Lists with filtering |
106+
| `listRoots()` | `List<Resource>` | Filesystem roots |
107+
| `resolve(Resource cwd)` | `List<Resource>` | Resolves path with glob expansion (`~`, `*`, `?`) |
108+
109+
**I/O:**
110+
111+
| Method | Returns | Description |
112+
|---|---|---|
113+
| `read()` | `InputStream` | Opens file for reading |
114+
| `write(boolean append)` | `OutputStream` | Opens file for writing (append or overwrite) |
115+
116+
**File operations:**
117+
118+
| Method | Returns | Description |
119+
|---|---|---|
120+
| `mkdirs()` | `boolean` | Creates directory and parents |
121+
| `delete()` | `boolean` | Deletes file or empty directory |
122+
| `move(Resource)` | `void` | Moves/renames file |
123+
| `copy(Resource)` | `Resource` | Copies file or directory |
124+
125+
**Metadata:**
126+
127+
| Method | Returns | Description |
128+
|---|---|---|
129+
| `lastModified()` | `long` | Last modified time (ms) |
130+
| `setLastModified(long)` | `boolean` | Sets last modified time |
131+
| `lastAccessed()` | `long` | Last accessed time (ms) |
132+
133+
## FileResource
134+
135+
`FileResource` is the default `Resource` implementation backed by `java.io.File`. When a command field is typed as `Resource`, the input string is automatically converted to a `FileResource` resolved relative to the current working directory.
136+
137+
```java
138+
@Option(description = "Config file")
139+
private Resource config;
140+
141+
// In execute():
142+
if (config.isLeaf() && config.exists()) {
143+
// read the file
144+
InputStream in = config.read();
145+
}
146+
147+
// Access the underlying File if needed:
148+
File javaFile = ((FileResource) config).getFile();
149+
```
150+
151+
You can also construct `FileResource` directly:
152+
153+
```java
154+
Resource home = new FileResource(System.getProperty("user.home"));
155+
Resource config = new FileResource("/etc/myapp/config.yml");
156+
Resource relative = new FileResource("data/output.csv");
157+
```
158+
159+
## Resource Filters
160+
161+
Resource filters control which files appear in directory listings and tab completion. Four built-in filters are available:
162+
163+
| Filter | Accepts |
164+
|---|---|
165+
| `AllResourceFilter` | Everything (default) |
166+
| `DirectoryResourceFilter` | Directories only |
167+
| `LeafResourceFilter` | Files only (not directories) |
168+
| `NoDotNamesFilter` | Files not starting with `.` |
169+
170+
### Filtering Completion Candidates
171+
172+
To restrict tab completion to directories only, provide a custom completer using `FileOptionCompleter` with a filter:
173+
174+
```java
175+
@CommandDefinition(name = "cd", description = "Change directory")
176+
public class CdCommand implements Command<CommandInvocation> {
177+
178+
@Argument(description = "Target directory",
179+
completer = DirectoryCompleter.class)
180+
private Resource target;
181+
182+
@Override
183+
public CommandResult execute(CommandInvocation invocation) {
184+
if (target != null && target.isDirectory()) {
185+
invocation.getAeshContext().setCurrentWorkingDirectory(target);
186+
}
187+
return CommandResult.SUCCESS;
188+
}
189+
}
190+
191+
public class DirectoryCompleter extends FileOptionCompleter {
192+
public DirectoryCompleter() {
193+
super(new DirectoryResourceFilter());
194+
}
195+
}
196+
```
197+
198+
Now tab completion only suggests directories:
199+
200+
```
201+
myshell$ cd sr<TAB>
202+
src/
203+
myshell$ cd src/<TAB>
204+
src/main/ src/test/
205+
```
206+
207+
### Filtering Directory Listings
208+
209+
Filters work with `Resource.list()` too:
210+
211+
```java
212+
// List only non-hidden files
213+
List<Resource> visible = directory.list(new NoDotNamesFilter());
214+
215+
// List only subdirectories
216+
List<Resource> dirs = directory.list(new DirectoryResourceFilter());
217+
```
218+
219+
### Custom Filters
220+
221+
Implement `ResourceFilter` for custom logic:
222+
223+
```java
224+
public class JavaFileFilter implements ResourceFilter {
225+
@Override
226+
public boolean accept(Resource resource) {
227+
return resource.isDirectory() || resource.getName().endsWith(".java");
228+
}
229+
}
230+
```
231+
232+
## Glob Expansion
233+
234+
`Resource.resolve()` supports shell-style globs:
235+
236+
```java
237+
Resource cwd = invocation.getAeshContext().getCurrentWorkingDirectory();
238+
Resource pattern = new FileResource("src/**/*.java");
239+
List<Resource> matches = pattern.resolve(cwd);
240+
```
241+
242+
The `~` character is expanded to the user's home directory:
243+
244+
```java
245+
Resource home = new FileResource("~/documents");
246+
List<Resource> resolved = home.resolve(cwd);
247+
```
248+
249+
## Resource vs File vs Path
250+
251+
| | `Resource` | `File` | `Path` |
252+
|---|---|---|---|
253+
| **Tab completion** | Automatic | Automatic | Automatic |
254+
| **API richness** | Full (listing, glob, I/O, copy/move) | Java standard | Java NIO |
255+
| **Glob expansion** | Built-in via `resolve()` | Manual | Via `PathMatcher` |
256+
| **Filtering** | `ResourceFilter` integration | Manual | Manual |
257+
| **Abstraction** | Pluggable (file, pipeline, remote) | Local filesystem only | Local filesystem only |
258+
| **Recommendation** | Use for commands that work with files | Use when you need `java.io.File` APIs | Use when you need `java.nio.file` APIs |
259+
260+
Use `Resource` when your command needs filesystem operations and you want the richest API. Use `File` or `Path` if you need compatibility with existing Java APIs that expect those types.
261+
262+
## See Also
263+
264+
- [Options](../options) -- Defining command options
265+
- [Arguments](../arguments) -- Positional arguments
266+
- [Completers](../completers) -- Custom tab completion
267+
- [Converters](../converters) -- Built-in and custom type conversion

0 commit comments

Comments
 (0)