|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+# README hw6-t2
|
|
|
2
|
+- [Einleitung](#einleitung)
|
|
|
3
|
+- [Implementierung](#implementierung)
|
|
|
4
|
+ - [*Shell* Datenstruktur](#shell-datenstruktur)
|
|
|
5
|
+ - [Public Methode *new()*](#public-methode-new)
|
|
|
6
|
+ - [Public Methode *start() -> Result*](#public-methode-start---result)
|
|
|
7
|
+ - [Private Methode *shell_loop() -> Result*](#private-methode-shellloop---result)
|
|
|
8
|
+ - [Private Methode *prompt() -> Result<Option<String>, ...>*](#private-methode-prompt---resultoptionstring)
|
|
|
9
|
+ - [Private Methode *run()*](#private-methode-run)
|
|
|
10
|
+ - ['Command Datenstruktur'](#command-datenstruktur)
|
|
|
11
|
+- [Externe Crates](#externe-crates)
|
|
|
12
|
+- [Module und Tests](#module-und-tests)
|
|
|
13
|
+- [Dokumentation](#dokumentation)
|
|
|
14
|
+
|
|
|
15
|
+## Einleitung
|
|
|
16
|
+
|
|
|
17
|
+In dieser Aufgabe werden Sie beginnen, eine einfache, aber robuste Shell zu erstellen. In den nächsten Aufgaben werden Sie diese Shell dann weiter ausbauen.
|
|
|
18
|
+
|
|
|
19
|
+Dafür werden zu Beginn 2 Objekte (Structs) benötigt:
|
|
|
20
|
+
|
|
|
21
|
+- Shell
|
|
|
22
|
+- Command
|
|
|
23
|
+
|
|
|
24
|
+Die Unit Tests dieser Objekte schreiben Sie bitte wieder in eigene Dateien, siehe folgendes Diagramm:
|
|
|
25
|
+
|
|
|
26
|
+
|
|
|
27
|
+
|
|
|
28
|
+Die Methoden der Datenstruktur Shell beinhalten das Setup und die Loop einer Shell. In der Loop wird der Prompt ausgegeben und auf die Eingaben des Benutzers gewartet. Schließt der Benutzer seine Eingabe mit RETURN ab, so wertet eine Methode die Eingabezeile aus und erstellt daraus die vom Benutzer eingegeben Kommandos. Diese Loop kann der Benutzer mit dem Kommando `exit` beenden.
|
|
|
29
|
+
|
|
|
30
|
+Das Handling der Kommandos übernehmen die Methoden der Datenstruktur Command. Beachten Sie, Ihre derzeitige Shell, mit der Sie selbst im Labor arbeiten (labshell), wird nicht einfach beendet wenn ein Kommando fehlerhaft ist oder nicht ausgeführt werden kann. Ebenso soll Ihre Shell nur bei dem Kommando `exit` beendet werden.
|
|
|
31
|
+
|
|
|
32
|
+Die main() Funktion selbst hat somit wenig zu tun, sie muss lediglich eine Instanz des Datentyps Shell starten und das Ergebnis auswerten. Ist das Ergebnis 'Ok' so wird das Programm mit dem Exitcode 0 beendet. Tritt intern bei der Benutzung einer Funktionalität Ihrer Shell ein Fehler auf, welcher nicht von Ihnen behandelt werden kann, so wird die Shell mit dem Exitcode 1 beendet.
|
|
|
33
|
+
|
|
|
34
|
+```Rust
|
|
|
35
|
+...
|
|
|
36
|
+let mut s = Shell::new(..);
|
|
|
37
|
+ match s.start() {
|
|
|
38
|
+ Ok(_) => process::exit(0),
|
|
|
39
|
+ Err(_) => process::exit(1),
|
|
|
40
|
+ }
|
|
|
41
|
+...
|
|
|
42
|
+```
|
|
|
43
|
+
|
|
|
44
|
+## Implementierung
|
|
|
45
|
+
|
|
|
46
|
+### *Shell* Datenstruktur
|
|
|
47
|
+
|
|
|
48
|
+Die Datenstruktur 'Shell' hat folgende Felder:
|
|
|
49
|
+
|
|
|
50
|
+```Rust
|
|
|
51
|
+struct Shell<R, W> {
|
|
|
52
|
+ pub reader: R,
|
|
|
53
|
+ pub writer: W,
|
|
|
54
|
+ pub should_exit: bool,
|
|
|
55
|
+ pub name: String,
|
|
|
56
|
+}
|
|
|
57
|
+```
|
|
|
58
|
+
|
|
|
59
|
+'reader' ist der Input Kanal Ihrer Shell und 'writer' der Output Kanal. Das Flag 'should_exit' signalisiert Ihrer Loop (siehe unten), dass die Loop beendet werden soll. 'name' ist der Name der Shell, den diese immer am Anfang des Prompts ausgibt, damit diese sich von Ihrer normalen Shell unterscheidet.
|
|
|
60
|
+
|
|
|
61
|
+Für Ihre Implementierung verwenden Sie folgende Traitbounds:
|
|
|
62
|
+
|
|
|
63
|
+- R: BufRead
|
|
|
64
|
+- W: Write
|
|
|
65
|
+
|
|
|
66
|
+Um das Verhalten einer echten Shell zu erhalten benötigen Sie für den Reader die Standard-Eingabe und für den Writer die Standard-Ausgabe.
|
|
|
67
|
+
|
|
|
68
|
+In den Unit Tests werden andere Typen für R und W benutzt. Schauen Sie sich dazu die Tests in `unit_tests_shell.rs` an. Diese sollen Ihnen als Beispiel für eigene Tests dienen.
|
|
|
69
|
+
|
|
|
70
|
+#### Public Methode *new()*
|
|
|
71
|
+
|
|
|
72
|
+```Rust
|
|
|
73
|
+pub fn new(input: R, output: W, name: String) -> Self
|
|
|
74
|
+```
|
|
|
75
|
+
|
|
|
76
|
+Eine Instanz der Shell wird erstellt.
|
|
|
77
|
+
|
|
|
78
|
+#### Public Methode *start() -> Result*
|
|
|
79
|
+
|
|
|
80
|
+Die public Methode *start()* ruft die private Methode *shell_loop()* auf. Die Funktion *start()* ist für evtl. spätere Erweiterungen gedacht, wenn in der Shell vor dem Starten der Loop noch weitere Initialisierungsarbeiten durchzuführen sind.
|
|
|
81
|
+
|
|
|
82
|
+#### Private Methode *shell_loop() -> Result*
|
|
|
83
|
+
|
|
|
84
|
+Die Basis Loop einer Shell haben wir bereits in der Vorlesung besprochen. Die Loop wartet auf Eingaben des Benutzers, die dieser mit RETURN abschließt. Die Eingaben werden dann ausgewertet und das Kommando, das sich u.U. daraus ergibt ausgeführt. Danach steht die Loop wieder für Eingaben bereit.
|
|
|
85
|
+
|
|
|
86
|
+
|
|
|
87
|
+
|
|
|
88
|
+#### Private Methode *prompt() -> Result<Option<String>, ...>*
|
|
|
89
|
+
|
|
|
90
|
+Die Funktion gibt den Namen der Shell, den aktuellen Pfad (siehe `std::env`) sowie das ' >' Zeichen, gefolgt von einem Leerzeichen aus:
|
|
|
91
|
+
|
|
|
92
|
+```text
|
|
|
93
|
+bsys-shell /Users/username >
|
|
|
94
|
+```
|
|
|
95
|
+
|
|
|
96
|
+Nach der Ausgabe wartet die Funktion auf Eingaben des Benutzers.
|
|
|
97
|
+
|
|
|
98
|
+Benutzen Sie zum Einlesen die *read_line()* Funktion. Liefert *read_line()* kein Zeichen, so gibt die Funktion None zurück. Werden Zeichen von *read_line()* eingelesen, so liefert die Funktion die Eingabe des Benutzers zurück.
|
|
|
99
|
+
|
|
|
100
|
+> Damit bei einem *write!()* Aufruf auf der Konsole auch der String erscheint, muss nach dem *write!()* Aufruf ein *flush()* Aufruf folgen
|
|
|
101
|
+
|
|
|
102
|
+Wie am Funktionskopf zu erkennen ist, liefert diese Funktion im Erfolgsfall den eingelesenen String zurück.
|
|
|
103
|
+
|
|
|
104
|
+#### Private Methode *run()*
|
|
|
105
|
+
|
|
|
106
|
+Diese Methode wird von *shell_loop()* aufgerufen, wenn der Benutzer Eingaben gemacht hat, und daraus ein Kommando geparst werden konnte. Das eigentliche Parsen des Strings geschieht im 'Command' Modul. Das Modul liefert nach einem erfolgreichen Parse-Vorgang ein Kommando zurück, welches dann in run() aufgerufen wird.
|
|
|
107
|
+
|
|
|
108
|
+Sowohl die Funktion zum Parsen des Strings, als auch die Methoden um die eigentlichen Kommandos auszuführen, platzieren Sie bitte in die Datei `command.rs`.
|
|
|
109
|
+
|
|
|
110
|
+### 'Command Datenstruktur'
|
|
|
111
|
+
|
|
|
112
|
+Die Datenstruktur 'Command' in `command.rs` hat zunächst folgende Felder:
|
|
|
113
|
+
|
|
|
114
|
+```Rust
|
|
|
115
|
+enum Command {
|
|
|
116
|
+ Empty,
|
|
|
117
|
+ Exit,
|
|
|
118
|
+ Cd(Option<OsString>),
|
|
|
119
|
+}
|
|
|
120
|
+```
|
|
|
121
|
+
|
|
|
122
|
+Die Kommandos bedeuten:
|
|
|
123
|
+
|
|
|
124
|
+- 'Empty': Keine Eingabe, z.B. hat der Benutzer nur Return, Leerzeichen oder andere Whitespaces getippt.
|
|
|
125
|
+- 'Exit': Dieses Kommando bedeutet, dass sich die Shell vor dem Start der nächsten Loop beendet.
|
|
|
126
|
+- 'Cd': ein Change Directory Befehl soll ausgeführt werden. *Option* enthält den Pfad zu welchem gewechselt werden soll.
|
|
|
127
|
+
|
|
|
128
|
+
|
|
|
129
|
+Im Modul `command.rs` wird die Trait Methode `FromStr` bereit gestellt, so dass das Objekt Command dieses Trait unterstützt.
|
|
|
130
|
+
|
|
|
131
|
+```Rust
|
|
|
132
|
+...
|
|
|
133
|
+fn from_str(s: &str) -> Result<Command, .....> {
|
|
|
134
|
+```
|
|
|
135
|
+
|
|
|
136
|
+Diese Funktion können Sie benutzen, um sich den Input des Users in der Shell analysieren zu lassen. Als Rückgabewert erhalten Sie den entsprechenden `Command`. Somit können Sie im Modul `shell.rs` komfortabel über folgenden Aufruf den Input String in ein Kommando wandeln lassen:
|
|
|
137
|
+
|
|
|
138
|
+```Rust
|
|
|
139
|
+Command::from_str(&line).and_then(|cmd| self.run(cmd))
|
|
|
140
|
+```
|
|
|
141
|
+
|
|
|
142
|
+In der obigen Trait Methode können die einfachen Kommandos wie:
|
|
|
143
|
+
|
|
|
144
|
+- Empty und
|
|
|
145
|
+- Exit
|
|
|
146
|
+
|
|
|
147
|
+direkt - je nach Input des User - zurück gegeben werden. Für komplexere Funktionen wie **cd** bietet es sich an, in der Trait Methode spezifische Methoden des Datentyps Command aufzurufen. Für jedes Kommando sind in der command.rs zwei Methoden zu implementieren:
|
|
|
148
|
+
|
|
|
149
|
+- *parse_<command>()*
|
|
|
150
|
+- *exec_<command>()*
|
|
|
151
|
+
|
|
|
152
|
+Für das **cd** Kommando somit:
|
|
|
153
|
+
|
|
|
154
|
+- *parse_cd()*
|
|
|
155
|
+- *exec_cd()*
|
|
|
156
|
+
|
|
|
157
|
+Die *parse_command()* Methode wird im *from_str()* Trait aufgerufen. Die *exec_<commnad>()>* Methode wird aus dem shell Modul an geeigneter Stelle aufgerufen.
|
|
|
158
|
+
|
|
|
159
|
+> Ihr **cd** Kommando sollte sich so verhalten wie das **cd** Kommando der labshell. Somit kommen Sie beim Aufruf von **cd** ohne Parameter in Ihr Home Verzeichnis (siehe env::var_os("HOME")). Mit **cd ..** in das Verzeichnis 'darüber' usw.
|
|
|
160
|
+
|
|
|
161
|
+
|
|
|
162
|
+## Externe Crates
|
|
|
163
|
+
|
|
|
164
|
+Benutzen Sie für Ihre Implementierung nur die externe Crate `nix`.
|
|
|
165
|
+
|
|
|
166
|
+## Module und Tests
|
|
|
167
|
+
|
|
|
168
|
+Ob und wie Sie den Code in weitere Module aufteilen wollen ist Ihnen überlassen. Schreiben Sie jedoch Ihre Unit-Tests in der Datei `unit_test_shell.rs` oder als eigenständigen Test, der von 'cargo test' aufgerufen wird, siehe auch [Testing][]. Einfache Tests können auch direkt in die Dokumentation 'codiert' werden, siehe [Documentation Tests][].
|
|
|
169
|
+
|
|
|
170
|
+Wichtig: Erstellen Sie ausreichend Unit Tests, um möglichst alle Methoden aus `shell.rs` und `command.rs` ausreichend testen zu können.
|
|
|
171
|
+
|
|
|
172
|
+## Dokumentation
|
|
|
173
|
+
|
|
|
174
|
+Bei dieser Aufgabe ist Ihre Dokumentation wichtig, um Ihren Programmablauf nachvollziehen zu können. Bitte dokumentieren Sie Ihre Funktionen entsprechend umfangreicher und kommentieren Sie spezielle Kniffe im Code, die Sie verwendet haben.
|
|
|
175
|
+
|
|
|
176
|
+[Testing]: https://doc.rust-lang.org/book/first-edition/testing.html
|
|
|
177
|
+[Documentation Tests]: https://doc.rust-lang.org/book/first-edition/testing.html#documentation-tests
|