Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions examples/error-handling.roc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
## Handle common filesystem errors with tag-pattern matching.
## Handle errors with tag-pattern matching.
app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.21.0-rc4/FvCh4vdqm3nBY6DWEfZ8RuGCVfjuMY43HA8KSNk9qVDn.tar.zst" }

import pf.OsStr
import pf.Stdout
import pf.Stderr
import pf.Path

main! : List(OsStr) => Try({}, _)
Expand All @@ -15,20 +16,22 @@ main! = |_args| {
missing_file : Path
missing_file = "nonexistent-file.txt"

# For professional software, use error tags (like `PathErr(NotFound)`) internally and convert
# them to a message for the user at the edge of your program. This also makes it easy to provide
# error messages in different languages.
match missing_file.read_utf8!() {
Ok(content) => Err(UnexpectedReadSuccess(content))?
Err(PathErr(NotFound)) => Stdout.line!("Expected error: Path not found (NotFound)")?
Err(PathErr(PermissionDenied)) => Stdout.line!("Error: Permission denied")?
Err(PathErr(Other(msg))) => Stdout.line!("Error: ${msg}")?
Err(_) => Stdout.line!("Error: Other file error")?
Err(PathErr(NotFound)) => Stderr.line!("Expected error: Path not found (NotFound)")?
Err(PathErr(PermissionDenied)) => Stderr.line!("Error: Permission denied")?
Err(PathErr(Other(msg))) => Stderr.line!("Error: ${msg}")?
Err(_) => Stderr.line!("Error: Other file error")?
}

# Filesystem kind mismatches are portable typed errors.
directory : Path
directory = "examples"

match directory.read_bytes!() {
Err(PathErr(IsADirectory)) => Stdout.line!("Expected error: Path is a directory (IsADirectory)")?
Err(PathErr(IsADirectory)) => Stderr.line!("Expected error: Path is a directory (IsADirectory)")?
Ok(_) => Err(UnexpectedDirectoryReadSuccess)?
Err(err) => Err(UnexpectedDirectoryReadError(err))?
}
Expand All @@ -37,7 +40,7 @@ main! = |_args| {
regular_file = "LICENSE"

match regular_file.list!() {
Err(PathErr(NotADirectory)) => Stdout.line!("Expected error: Path is not a directory (NotADirectory)")?
Err(PathErr(NotADirectory)) => Stderr.line!("Expected error: Path is not a directory (NotADirectory)")?
Ok(_) => Err(UnexpectedFileListSuccess)?
Err(err) => Err(UnexpectedFileListError(err))?
}
Expand Down
4 changes: 4 additions & 0 deletions examples/file-read-buffered.roc
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## Read a file incrementally with a buffer instead of loading it all at once.
##
## This can be useful to process large files without using a lot of RAM or
## requiring the user to wait until the complete file is processed when they
## only wanted to look at the first page.
app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.21.0-rc4/FvCh4vdqm3nBY6DWEfZ8RuGCVfjuMY43HA8KSNk9qVDn.tar.zst" }

import pf.OsStr
Expand Down
7 changes: 0 additions & 7 deletions examples/http.roc → examples/http-simple.roc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import pf.OsStr
import pf.Http
import pf.Stdout
import http.Request
import http.Response

main! : List(OsStr) => Try({}, _)
main! = |_args| {
Expand All @@ -27,11 +26,5 @@ main! = |_args| {
Stdout.line!("Response body:")?
Stdout.line!(body)?

response_2 = Http.send!(Request.from_method(GET).with_uri("http://127.0.0.1:9000/html")) ? |err| SendSecondHtmlFailed(err)
body_2 = Str.from_utf8(Response.body(response_2)) ? |err| SecondHtmlBodyUtf8Failed(err)

Stdout.line!("Response body 2:")?
Stdout.line!(body_2)?

Ok({})
}
4 changes: 2 additions & 2 deletions examples/path.roc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Inspect a path's filename, extension, representation, and filesystem type.
## Inspect a path's filename, extension, string representation, and type (file/dir/symlink).
app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.21.0-rc4/FvCh4vdqm3nBY6DWEfZ8RuGCVfjuMY43HA8KSNk9qVDn.tar.zst" }

import pf.OsStr exposing [OsStr]
Expand All @@ -8,7 +8,7 @@ import pf.Path
main! : List(OsStr) => Try({}, _)
main! = |args| {
path = path_argument(args)?
filename = Path.filename(path).map_ok(Path.display) ?? "<none>"
filename = Path.filename(path).map_ok(Path.display) ?? "Path is not a file."
extension = Path.ext(path).map_ok(Path.display) ?? "<none>"

Stdout.line!(
Expand Down
12 changes: 8 additions & 4 deletions examples/print.roc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Write lines, text, and lists to standard output and standard error.
## Write text, lines and lists to standard output and standard error.
app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.21.0-rc4/FvCh4vdqm3nBY6DWEfZ8RuGCVfjuMY43HA8KSNk9qVDn.tar.zst" }

import pf.OsStr
Expand All @@ -7,22 +7,26 @@ import pf.Stderr

main! : List(OsStr) => Try({}, _)
main! = |_args| {
# Print a string to stdout
# Print a string to stdout with a newline
Stdout.line!("Hello, world!")?

# Print without a newline
Stdout.write!("No newline after me.")?

# Print a string to stderr
# Print a string to stderr with a newline
Stderr.line!("Hello, error!")?

# Print a string to stderr without a newline
Stderr.write!("Err with no newline after.")?

# Print a list to stdout
for str in ["Foo", "Bar", "Baz"] {
lst = ["Foo", "Bar", "Baz"]
for str in lst {
Stdout.line!(str)?
}

# Use inspect to convert a value into a nice string representation for debugging
Stdout.line!(Str.inspect(lst))?

Ok({})
}
2 changes: 2 additions & 0 deletions examples/random.roc
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ main! = |_args| {
random_u32 = Random.seed_u32!()?
Stdout.line!("Random U32 seed is: ${random_u32.to_str()}")?

# TODO link to example showing how to use the seed values to generate random numbers

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

roc-random is mature now... we could use that package here


Ok({})
}
5 changes: 3 additions & 2 deletions examples/sqlite-basic.roc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Query a SQLite database and decode rows into application records.
## Query a SQLite database and decode rows into records.
app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.21.0-rc4/FvCh4vdqm3nBY6DWEfZ8RuGCVfjuMY43HA8KSNk9qVDn.tar.zst" }

import pf.OsStr
Expand Down Expand Up @@ -50,6 +50,7 @@ print_line! : Str => Try({}, _)
print_line! = |line| Stdout.line!(line)

query_todos_by_status! = |db_path, status|
# `many` when you expect multiple rows to be returned.
Sqlite.query_many!({
path: db_path,
query: "SELECT id, task, status FROM todos WHERE status = :status;",
Expand All @@ -58,7 +59,7 @@ query_todos_by_status! = |db_path, status|
})

# A row decoder is `List(Str) -> (Stmt => Try(a, err))`; the new compiler does not
# support the record-builder (`<-`) sugar, so we combine the leaf decoders by hand.
# support the old record-builder (`<-`) sugar, so we combine the leaf decoders by hand.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't mention the old syntax at all. I think most people will not be familiar with that, and the people that are will not need this explained.

decode_todo = |cols|
|stmt| {
id = Sqlite.i64("id")(cols)(stmt)?
Expand Down
10 changes: 7 additions & 3 deletions examples/sqlite-everything.roc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Exercise SQLite queries, decoders, nullable values, and prepared writes.
## Shows SQLite queries, decoders, nullable values, and prepared writes.
app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.21.0-rc4/FvCh4vdqm3nBY6DWEfZ8RuGCVfjuMY43HA8KSNk9qVDn.tar.zst" }

import pf.OsStr
Expand Down Expand Up @@ -85,6 +85,9 @@ run! = || {
", ",
)

# Bindings map each named placeholder (like :task0) in the query to its actual
# value. Passing values this way lets SQLite handle escaping and type conversion,
# which avoids SQL injection and quoting bugs from building the query as a string.
binding_groups = List.map_with_index(
todos_list,
|t, indx| {
Expand Down Expand Up @@ -148,7 +151,8 @@ run! = || {
}) ? |err| PrepareUpdateTodoFailed(err)

prepared_update.execute!([{ name: ":id", value: Integer(1) }]) ? |err| ExecutePreparedUpdateFailed(err)
# Reuse verifies that execution resets the statement before returning.
# You can run the same prepared statement again with different values.
# Each execution starts fresh, so the previous run's values don't carry over.
prepared_update.execute!([{ name: ":id", value: Integer(2) }]) ? |err| ReusePreparedUpdateFailed(err)

prepared_count = Sqlite.prepare!({
Expand All @@ -165,7 +169,7 @@ run! = || {
}) ? |err| PrepareSortedTodosFailed(err)

todos_sorted = prepared_query.query_many!([], decode_task_status) ? |err| QuerySortedTodosFailed(err)
# Reuse verifies that querying resets the statement before returning.
# Note that the result is not cached if we run the same prepared query again.
todos_sorted_again = prepared_query.query_many!([], decode_task_status) ? |err| ReuseSortedTodosFailed(err)
expect todos_sorted_again.len() == todos_sorted.len()

Expand Down
2 changes: 1 addition & 1 deletion scripts/test_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@
]
},
{
"path": "examples/http.roc",
"path": "examples/http-simple.roc",
"cases": [
{
"name": "server-available",
Expand Down
Loading