2024-05-13 15:37:53 +02:00
|
|
|
use crate::repl::tests::{fail_test, run_test, run_test_contains, TestResult};
|
2022-07-26 03:11:46 +02:00
|
|
|
use nu_test_support::nu;
|
2023-04-08 20:52:37 +02:00
|
|
|
use pretty_assertions::assert_eq;
|
2021-12-25 20:39:42 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_scope_leak1() -> TestResult {
|
|
|
|
fail_test(
|
2022-03-03 01:55:03 +01:00
|
|
|
"if false { let $x = 10 } else { let $x = 20 }; $x",
|
2021-12-25 20:39:42 +01:00
|
|
|
"Variable not found",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_scope_leak2() -> TestResult {
|
|
|
|
fail_test(
|
|
|
|
"def foo [] { $x }; def bar [] { let $x = 10; foo }; bar",
|
|
|
|
"Variable not found",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_scope_leak3() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
"def foo [$x] { $x }; def bar [] { let $x = 10; foo 20}; bar",
|
|
|
|
"20",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_scope_leak4() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
"def foo [$x] { $x }; def bar [] { let $x = 10; (foo 20) + $x}; bar",
|
|
|
|
"30",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn custom_rest_var() -> TestResult {
|
|
|
|
run_test("def foo [...x] { $x.0 + $x.1 }; foo 10 80", "90")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn def_twice_should_fail() -> TestResult {
|
|
|
|
fail_test(
|
|
|
|
r#"def foo [] { "foo" }; def foo [] { "bar" }"#,
|
|
|
|
"defined more than once",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_parameters() -> TestResult {
|
2021-12-27 20:13:52 +01:00
|
|
|
fail_test(r#"def foo {}"#, "Missing required positional")
|
2021-12-25 20:39:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn flag_param_value() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def foo [--bob: int] { $bob + 100 }; foo --bob 55"#,
|
|
|
|
"155",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn do_rest_args() -> TestResult {
|
|
|
|
run_test(r#"(do { |...rest| $rest } 1 2).1 + 10"#, "12")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn custom_switch1() -> TestResult {
|
|
|
|
run_test(
|
2022-07-27 04:08:54 +02:00
|
|
|
r#"def florb [ --dry-run ] { if ($dry_run) { "foo" } else { "bar" } }; florb --dry-run"#,
|
2021-12-25 20:39:42 +01:00
|
|
|
"foo",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-12-20 11:07:19 +01:00
|
|
|
#[test]
|
|
|
|
fn custom_flag_with_type_checking() -> TestResult {
|
|
|
|
fail_test(
|
|
|
|
r#"def florb [--dry-run: int] { $dry_run }; let y = "3"; florb --dry-run=$y"#,
|
|
|
|
"type_mismatch",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-12-25 20:39:42 +01:00
|
|
|
#[test]
|
2023-09-23 10:20:48 +02:00
|
|
|
fn custom_switch2() -> TestResult {
|
2021-12-25 20:39:42 +01:00
|
|
|
run_test(
|
2022-07-27 04:08:54 +02:00
|
|
|
r#"def florb [ --dry-run ] { if ($dry_run) { "foo" } else { "bar" } }; florb"#,
|
2021-12-25 20:39:42 +01:00
|
|
|
"bar",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-11-14 13:46:05 +01:00
|
|
|
#[test]
|
|
|
|
fn custom_switch3() -> TestResult {
|
2023-11-22 23:57:37 +01:00
|
|
|
run_test(
|
|
|
|
r#"def florb [ --dry-run ] { $dry_run }; florb --dry-run=false"#,
|
|
|
|
"false",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn custom_switch4() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def florb [ --dry-run ] { $dry_run }; florb --dry-run=true"#,
|
|
|
|
"true",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn custom_switch5() -> TestResult {
|
|
|
|
run_test(r#"def florb [ --dry-run ] { $dry_run }; florb"#, "false")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn custom_switch6() -> TestResult {
|
|
|
|
run_test(
|
|
|
|
r#"def florb [ --dry-run ] { $dry_run }; florb --dry-run"#,
|
|
|
|
"true",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn custom_flag1() -> TestResult {
|
2023-11-14 13:46:05 +01:00
|
|
|
run_test(
|
|
|
|
r#"def florb [
|
|
|
|
--age: int = 0
|
|
|
|
--name = "foobar"
|
2023-12-20 11:07:19 +01:00
|
|
|
] {
|
2023-11-14 13:46:05 +01:00
|
|
|
($age | into string) + $name
|
|
|
|
}
|
|
|
|
florb"#,
|
|
|
|
"0foobar",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2023-11-22 23:57:37 +01:00
|
|
|
fn custom_flag2() -> TestResult {
|
2023-11-14 13:46:05 +01:00
|
|
|
run_test(
|
|
|
|
r#"def florb [
|
|
|
|
--age: int
|
|
|
|
--name = "foobar"
|
|
|
|
] {
|
|
|
|
($age | into string) + $name
|
|
|
|
}
|
|
|
|
florb --age 3"#,
|
|
|
|
"3foobar",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
Deprecate `--flag: bool` in custom command (#11365)
# Description
While #11057 is merged, it's hard to tell the difference between
`--flag: bool` and `--flag`, and it makes user hard to read custom
commands' signature, and hard to use them correctly.
After discussion, I think we can deprecate `--flag: bool` usage, and
encourage using `--flag` instead.
# User-Facing Changes
The following code will raise warning message, but don't stop from
running.
```nushell
❯ def florb [--dry-run: bool, --another-flag] { "aaa" }; florb
Error: × Deprecated: --flag: bool
╭─[entry #7:1:1]
1 │ def florb [--dry-run: bool, --another-flag] { "aaa" }; florb
· ──┬─
· ╰── `--flag: bool` is deprecated. Please use `--flag` instead, more info: https://www.nushell.sh/book/custom_commands.html
╰────
aaa
```
cc @kubouch
# Tests + Formatting
Done
# After Submitting
- [ ] Add more information under
https://www.nushell.sh/book/custom_commands.html to indicate `--dry-run:
bool` is not allowed,
- [ ] remove `: bool` from custom commands between 0.89 and 0.90
---------
Co-authored-by: Antoine Stevan <44101798+amtoine@users.noreply.github.com>
2023-12-21 10:07:08 +01:00
|
|
|
#[test]
|
|
|
|
fn deprecated_boolean_flag() {
|
|
|
|
let actual = nu!(r#"def florb [--dry-run: bool, --another-flag] { "aaa" }; florb"#);
|
2024-01-25 07:16:49 +01:00
|
|
|
assert!(actual.err.contains("not allowed"));
|
Deprecate `--flag: bool` in custom command (#11365)
# Description
While #11057 is merged, it's hard to tell the difference between
`--flag: bool` and `--flag`, and it makes user hard to read custom
commands' signature, and hard to use them correctly.
After discussion, I think we can deprecate `--flag: bool` usage, and
encourage using `--flag` instead.
# User-Facing Changes
The following code will raise warning message, but don't stop from
running.
```nushell
❯ def florb [--dry-run: bool, --another-flag] { "aaa" }; florb
Error: × Deprecated: --flag: bool
╭─[entry #7:1:1]
1 │ def florb [--dry-run: bool, --another-flag] { "aaa" }; florb
· ──┬─
· ╰── `--flag: bool` is deprecated. Please use `--flag` instead, more info: https://www.nushell.sh/book/custom_commands.html
╰────
aaa
```
cc @kubouch
# Tests + Formatting
Done
# After Submitting
- [ ] Add more information under
https://www.nushell.sh/book/custom_commands.html to indicate `--dry-run:
bool` is not allowed,
- [ ] remove `: bool` from custom commands between 0.89 and 0.90
---------
Co-authored-by: Antoine Stevan <44101798+amtoine@users.noreply.github.com>
2023-12-21 10:07:08 +01:00
|
|
|
}
|
|
|
|
|
2021-12-25 20:39:42 +01:00
|
|
|
#[test]
|
|
|
|
fn simple_var_closing() -> TestResult {
|
|
|
|
run_test("let $x = 10; def foo [] { $x }; foo", "10")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn predecl_check() -> TestResult {
|
|
|
|
run_test("def bob [] { sam }; def sam [] { 3 }; bob", "3")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn def_with_no_dollar() -> TestResult {
|
|
|
|
run_test("def bob [x] { $x + 3 }; bob 4", "7")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn allow_missing_optional_params() -> TestResult {
|
|
|
|
run_test(
|
2022-12-22 21:30:10 +01:00
|
|
|
"def foo [x?:int] { if $x != null { $x + 10 } else { 5 } }; foo",
|
2021-12-25 20:39:42 +01:00
|
|
|
"5",
|
|
|
|
)
|
|
|
|
}
|
2022-05-29 15:14:15 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn help_present_in_def() -> TestResult {
|
2022-10-26 18:36:42 +02:00
|
|
|
run_test_contains(
|
|
|
|
"def foo [] {}; help foo;",
|
|
|
|
"Display the help message for this command",
|
|
|
|
)
|
2022-05-29 15:14:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn help_not_present_in_extern() -> TestResult {
|
|
|
|
run_test(
|
Make parsing for unknown args in known externals like normal external calls (#13414)
# Description
This corrects the parsing of unknown arguments provided to known
externals to behave exactly like external arguments passed to normal
external calls.
I've done this by adding a `SyntaxShape::ExternalArgument` which
triggers the same parsing rules.
Because I didn't like how the highlighting looked, I modified the
flattener to emit `ExternalArg` flat shapes for arguments that have that
syntax shape and are plain strings/globs. This is the same behavior that
external calls have.
Aside from passing the tests, I've also checked manually that the
completer seems to work adequately. I can confirm that specified
positional arguments get completion according to their specified type
(including custom completions), and then anything remaining gets
filepath style completion, as you'd expect from an external command.
Thanks to @OJarrisonn for originally finding this issue.
# User-Facing Changes
- Unknown args are now parsed according to their specified syntax shape,
rather than `Any`. This may be a breaking change, though I think it's
extremely unlikely in practice.
- The unspecified arguments of known externals are now highlighted /
flattened identically to normal external arguments, which makes it more
clear how they're being interpreted, and should help the completer
function properly.
- Known externals now have an implicit rest arg if not specified named
`args`, with a syntax shape of `ExternalArgument`.
# Tests + Formatting
Tests added for the new behaviour. Some old tests had to be corrected to
match.
- :green_circle: `toolkit fmt`
- :green_circle: `toolkit clippy`
- :green_circle: `toolkit test`
- :green_circle: `toolkit test stdlib`
# After Submitting
- [ ] release notes (bugfix, and debatable whether it's a breaking
change)
2024-07-21 10:32:36 +02:00
|
|
|
r#"
|
|
|
|
module test {export extern "git fetch" []};
|
|
|
|
use test `git fetch`;
|
|
|
|
help git fetch | find help | to text | ansi strip
|
|
|
|
"#,
|
|
|
|
"",
|
2022-05-29 15:14:15 +02:00
|
|
|
)
|
|
|
|
}
|
2022-07-26 01:41:30 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn override_table() -> TestResult {
|
2023-08-08 12:47:23 +02:00
|
|
|
run_test(r#"def table [-e] { "hi" }; table"#, "hi")
|
2022-07-26 01:41:30 +02:00
|
|
|
}
|
2022-07-26 03:11:46 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn override_table_eval_file() {
|
2023-08-08 12:47:23 +02:00
|
|
|
let actual = nu!(r#"def table [-e] { "hi" }; table"#);
|
2022-07-26 03:11:46 +02:00
|
|
|
assert_eq!(actual.out, "hi");
|
|
|
|
}
|
2023-01-05 03:38:50 +01:00
|
|
|
|
|
|
|
// This test is disabled on Windows because they cause a stack overflow in CI (but not locally!).
|
|
|
|
// For reasons we don't understand, the Windows CI runners are prone to stack overflow.
|
|
|
|
// TODO: investigate so we can enable on Windows
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
#[test]
|
|
|
|
fn infinite_recursion_does_not_panic() {
|
2023-07-17 18:43:51 +02:00
|
|
|
let actual = nu!(r#"
|
2023-01-05 03:38:50 +01:00
|
|
|
def bang [] { bang }; bang
|
2023-07-17 18:43:51 +02:00
|
|
|
"#);
|
2023-01-05 03:38:50 +01:00
|
|
|
assert!(actual.err.contains("Recursion limit (50) reached"));
|
|
|
|
}
|
2024-01-12 16:48:53 +01:00
|
|
|
|
2024-02-07 23:42:24 +01:00
|
|
|
// This test is disabled on Windows because they cause a stack overflow in CI (but not locally!).
|
|
|
|
// For reasons we don't understand, the Windows CI runners are prone to stack overflow.
|
|
|
|
// TODO: investigate so we can enable on Windows
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
#[test]
|
|
|
|
fn infinite_mutual_recursion_does_not_panic() {
|
|
|
|
let actual = nu!(r#"
|
|
|
|
def bang [] { def boom [] { bang }; boom }; bang
|
|
|
|
"#);
|
|
|
|
assert!(actual.err.contains("Recursion limit (50) reached"));
|
|
|
|
}
|
|
|
|
|
2024-01-12 16:48:53 +01:00
|
|
|
#[test]
|
|
|
|
fn type_check_for_during_eval() -> TestResult {
|
|
|
|
fail_test(
|
|
|
|
r#"def spam [foo: string] { $foo | describe }; def outer [--foo: string] { spam $foo }; outer"#,
|
|
|
|
"can't convert nothing to string",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn type_check_for_during_eval2() -> TestResult {
|
|
|
|
fail_test(
|
|
|
|
r#"def spam [foo: string] { $foo | describe }; def outer [--foo: any] { spam $foo }; outer"#,
|
|
|
|
"can't convert nothing to string",
|
|
|
|
)
|
|
|
|
}
|
do not attempt to glob expand if the file path is wrapped in quotes (#11569)
# Description
Fixes: #11455
### For arguments which is annotated with `:path/:directory/:glob`
To fix the issue, we need to have a way to know if a path is originally
quoted during runtime. So the information needed to be added at several
levels:
* parse time (from user input to expression)
We need to add quoted information into `Expr::Filepath`,
`Expr::Directory`, `Expr::GlobPattern`
* eval time
When convert from `Expr::Filepath`, `Expr::Directory`,
`Expr::GlobPattern` to `Value::String` during runtime, we won't auto
expanded the path if it's quoted
### For `ls`
It's really special, because it accepts a `String` as a pattern, and it
generates `glob` expression inside the command itself.
So the idea behind the change is introducing a special SyntaxShape to
ls: `SyntaxShape::LsGlobPattern`. So we can track if the pattern is
originally quoted easier, and we don't auto expand the path either.
Then when constructing a glob pattern inside ls, we check if input
pattern is quoted, if so: we escape the input pattern, so we can run `ls
a[123]b`, because it's already escaped.
Finally, to accomplish the checking process, we also need to introduce a
new value type called `Value::QuotedString` to differ from
`Value::String`, it's used to generate an enum called `NuPath`, which is
finally used in `ls` function. `ls` learned from `NuPath` to know if
user input is quoted.
# User-Facing Changes
Actually it contains several changes
### For arguments which is annotated with `:path/:directory/:glob`
#### Before
```nushell
> def foo [p: path] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
> def foo [p: directory] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
> def foo [p: glob] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
```
#### After
```nushell
> def foo [p: path] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
> def foo [p: directory] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
> def foo [p: glob] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
```
### For ls command
`touch '[uwu]'`
#### Before
```
❯ ls -D "[uwu]"
Error: × No matches found for [uwu]
╭─[entry #6:1:1]
1 │ ls -D "[uwu]"
· ───┬───
· ╰── Pattern, file or folder not found
╰────
help: no matches found
```
#### After
```
❯ ls -D "[uwu]"
╭───┬───────┬──────┬──────┬──────────╮
│ # │ name │ type │ size │ modified │
├───┼───────┼──────┼──────┼──────────┤
│ 0 │ [uwu] │ file │ 0 B │ now │
╰───┴───────┴──────┴──────┴──────────╯
```
# Tests + Formatting
Done
# After Submitting
NaN
2024-01-21 16:22:25 +01:00
|
|
|
|
2024-01-26 15:24:17 +01:00
|
|
|
#[test]
|
|
|
|
fn empty_list_matches_list_type() -> TestResult {
|
|
|
|
let _ = run_test(
|
|
|
|
r#"def spam [foo: list<int>] { echo $foo }; spam [] | length"#,
|
|
|
|
"0",
|
|
|
|
);
|
|
|
|
run_test(
|
|
|
|
r#"def spam [foo: list<string>] { echo $foo }; spam [] | length"#,
|
|
|
|
"0",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
do not attempt to glob expand if the file path is wrapped in quotes (#11569)
# Description
Fixes: #11455
### For arguments which is annotated with `:path/:directory/:glob`
To fix the issue, we need to have a way to know if a path is originally
quoted during runtime. So the information needed to be added at several
levels:
* parse time (from user input to expression)
We need to add quoted information into `Expr::Filepath`,
`Expr::Directory`, `Expr::GlobPattern`
* eval time
When convert from `Expr::Filepath`, `Expr::Directory`,
`Expr::GlobPattern` to `Value::String` during runtime, we won't auto
expanded the path if it's quoted
### For `ls`
It's really special, because it accepts a `String` as a pattern, and it
generates `glob` expression inside the command itself.
So the idea behind the change is introducing a special SyntaxShape to
ls: `SyntaxShape::LsGlobPattern`. So we can track if the pattern is
originally quoted easier, and we don't auto expand the path either.
Then when constructing a glob pattern inside ls, we check if input
pattern is quoted, if so: we escape the input pattern, so we can run `ls
a[123]b`, because it's already escaped.
Finally, to accomplish the checking process, we also need to introduce a
new value type called `Value::QuotedString` to differ from
`Value::String`, it's used to generate an enum called `NuPath`, which is
finally used in `ls` function. `ls` learned from `NuPath` to know if
user input is quoted.
# User-Facing Changes
Actually it contains several changes
### For arguments which is annotated with `:path/:directory/:glob`
#### Before
```nushell
> def foo [p: path] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
> def foo [p: directory] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
> def foo [p: glob] { echo $p }; print (foo "~/a"); print (foo '~/a')
/home/windsoilder/a
/home/windsoilder/a
```
#### After
```nushell
> def foo [p: path] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
> def foo [p: directory] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
> def foo [p: glob] { echo $p }; print (foo "~/a"); print (foo '~/a')
~/a
~/a
```
### For ls command
`touch '[uwu]'`
#### Before
```
❯ ls -D "[uwu]"
Error: × No matches found for [uwu]
╭─[entry #6:1:1]
1 │ ls -D "[uwu]"
· ───┬───
· ╰── Pattern, file or folder not found
╰────
help: no matches found
```
#### After
```
❯ ls -D "[uwu]"
╭───┬───────┬──────┬──────┬──────────╮
│ # │ name │ type │ size │ modified │
├───┼───────┼──────┼──────┼──────────┤
│ 0 │ [uwu] │ file │ 0 B │ now │
╰───┴───────┴──────┴──────┴──────────╯
```
# Tests + Formatting
Done
# After Submitting
NaN
2024-01-21 16:22:25 +01:00
|
|
|
#[test]
|
|
|
|
fn path_argument_dont_auto_expand_if_single_quoted() -> TestResult {
|
|
|
|
run_test("def spam [foo: path] { echo $foo }; spam '~/aa'", "~/aa")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn path_argument_dont_auto_expand_if_double_quoted() -> TestResult {
|
|
|
|
run_test(r#"def spam [foo: path] { echo $foo }; spam "~/aa""#, "~/aa")
|
|
|
|
}
|
2024-02-28 16:05:35 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn dont_allow_implicit_casting_between_glob_and_string() -> TestResult {
|
|
|
|
let _ = fail_test(
|
|
|
|
r#"def spam [foo: string] { echo $foo }; let f: glob = 'aa'; spam $f"#,
|
|
|
|
"expected string",
|
|
|
|
);
|
|
|
|
fail_test(
|
|
|
|
r#"def spam [foo: glob] { echo $foo }; let f = 'aa'; spam $f"#,
|
|
|
|
"can't convert",
|
|
|
|
)
|
|
|
|
}
|
2024-05-16 10:50:29 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn allow_pass_negative_float() -> TestResult {
|
|
|
|
run_test("def spam [val: float] { $val }; spam -1.4", "-1.4")?;
|
|
|
|
run_test("def spam [val: float] { $val }; spam -2", "-2")
|
|
|
|
}
|