mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Merge remote-tracking branch 'origin/main' into csv_decoding-server_example
This commit is contained in:
commit
2e5f207283
14 changed files with 271 additions and 59 deletions
1
examples/interactive/.gitignore
vendored
1
examples/interactive/.gitignore
vendored
|
@ -6,3 +6,4 @@ form
|
|||
tui
|
||||
http-get
|
||||
file-io
|
||||
env
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
interface Env
|
||||
exposes [cwd, dict, var]
|
||||
imports [Task.{ Task }, Path.{ Path }, InternalPath, Effect, InternalTask]
|
||||
exposes [cwd, dict, var, decode] imports [Task.{ Task }, Path.{ Path }, InternalPath, Effect, InternalTask, EnvDecoding]
|
||||
|
||||
## Reads the [current working directory](https://en.wikipedia.org/wiki/Working_directory)
|
||||
## from the environment. File operations on relative [Path]s are relative to this directory.
|
||||
|
@ -33,31 +32,40 @@ var = \name ->
|
|||
|> Effect.map (\result -> Result.mapErr result \{} -> VarNotFound)
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
# ## Reads the given environment variable and attempts to decode it.
|
||||
# ##
|
||||
# ## The type being decoded into will be determined by type inference. For example,
|
||||
# ## if this ends up being used like a `Task U16 …` then the environment variable
|
||||
# ## will be decoded as a string representation of a `U16`.
|
||||
# ##
|
||||
# ## getU16Var : Str -> Task U16 [VarNotFound, DecodeErr DecodeError]* [Env]*
|
||||
# ## getU16Var = \var -> Env.decode var
|
||||
# ## # If the environment contains a variable NUM_THINGS=123, then calling
|
||||
# ## # (getU16Var "NUM_THINGS") would return a task which succeeds with the U16 number 123.
|
||||
# ## #
|
||||
# ## # However, if the NUM_THINGS environment variable was set to 1234567, then
|
||||
# ## # (getU16Var "NUM_THINGS") would fail because that number is too big to fit in a U16.
|
||||
# ##
|
||||
# ## Supported types:
|
||||
# ## - strings
|
||||
# ## - numbers, as long as they contain only numeric digits, up to one `.`, and an optional `-` at the front for negative numbers
|
||||
# ## - comma-separated lists (of either strings or numbers), as long as there are no spaces after the commas
|
||||
# ##
|
||||
# ## Trying to decode into any other types will always fail with a `DecodeErr`.
|
||||
# decode : Str -> Task val [VarNotFound, DecodeErr DecodeError]* [Env]*
|
||||
# | val has Decode
|
||||
# decode = \var ->
|
||||
# Effect.envVar var
|
||||
# |> InternalTask.fromEffect
|
||||
## Reads the given environment variable and attempts to decode it.
|
||||
##
|
||||
## The type being decoded into will be determined by type inference. For example,
|
||||
## if this ends up being used like a `Task U16 …` then the environment variable
|
||||
## will be decoded as a string representation of a `U16`.
|
||||
##
|
||||
## getU16Var : Str -> Task U16 [VarNotFound, DecodeErr DecodeError]* [Env]*
|
||||
## getU16Var = \var -> Env.decode var
|
||||
## # If the environment contains a variable NUM_THINGS=123, then calling
|
||||
## # (getU16Var "NUM_THINGS") would return a task which succeeds with the U16 number 123.
|
||||
## #
|
||||
## # However, if the NUM_THINGS environment variable was set to 1234567, then
|
||||
## # (getU16Var "NUM_THINGS") would fail because that number is too big to fit in a U16.
|
||||
##
|
||||
## Supported types:
|
||||
## - strings
|
||||
## - numbers, as long as they contain only numeric digits, up to one `.`, and an optional `-` at the front for negative numbers
|
||||
## - comma-separated lists (of either strings or numbers), as long as there are no spaces after the commas
|
||||
##
|
||||
## Trying to decode into any other types will always fail with a `DecodeErr`.
|
||||
decode : Str -> Task val [VarNotFound, DecodeErr DecodeError]* [Env]* | val has Decoding
|
||||
decode = \name ->
|
||||
Effect.envVar name
|
||||
|> Effect.map
|
||||
(
|
||||
\result ->
|
||||
result
|
||||
|> Result.mapErr (\{} -> VarNotFound)
|
||||
|> Result.try
|
||||
(\varStr ->
|
||||
Decode.fromBytes (Str.toUtf8 varStr) (EnvDecoding.format {})
|
||||
|> Result.mapErr (\_ -> DecodeErr TooShort)))
|
||||
|> InternalTask.fromEffect
|
||||
|
||||
## Reads all the process's environment variables into a [Dict].
|
||||
##
|
||||
## If any key or value contains invalid Unicode, the [Unicode replacement character](https://unicode.org/glossary/#replacement_character)
|
||||
|
|
95
examples/interactive/cli-platform/EnvDecoding.roc
Normal file
95
examples/interactive/cli-platform/EnvDecoding.roc
Normal file
|
@ -0,0 +1,95 @@
|
|||
interface EnvDecoding exposes [EnvFormat, format] imports []
|
||||
|
||||
EnvFormat := {} has [
|
||||
DecoderFormatting {
|
||||
u8: envU8,
|
||||
u16: envU16,
|
||||
u32: envU32,
|
||||
u64: envU64,
|
||||
u128: envU128,
|
||||
i8: envI8,
|
||||
i16: envI16,
|
||||
i32: envI32,
|
||||
i64: envI64,
|
||||
i128: envI128,
|
||||
f32: envF32,
|
||||
f64: envF64,
|
||||
dec: envDec,
|
||||
bool: envBool,
|
||||
string: envString,
|
||||
list: envList,
|
||||
record: envRecord,
|
||||
},
|
||||
]
|
||||
|
||||
format : {} -> EnvFormat
|
||||
format = \{} -> @EnvFormat {}
|
||||
|
||||
decodeBytesToNum = \bytes, transformer ->
|
||||
when Str.fromUtf8 bytes is
|
||||
Ok s ->
|
||||
when transformer s is
|
||||
Ok n -> { result: Ok n, rest: [] }
|
||||
Err _ -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
Err _ -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
envU8 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU8
|
||||
envU16 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU16
|
||||
envU32 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU32
|
||||
envU64 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU64
|
||||
envU128 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toU128
|
||||
envI8 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI8
|
||||
envI16 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI16
|
||||
envI32 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI32
|
||||
envI64 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI64
|
||||
envI128 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toI128
|
||||
envF32 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toF32
|
||||
envF64 = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toF64
|
||||
envDec = Decode.custom \bytes, @EnvFormat {} -> decodeBytesToNum bytes Str.toDec
|
||||
envBool = Decode.custom \bytes, @EnvFormat {} ->
|
||||
when Str.fromUtf8 bytes is
|
||||
Ok "true" -> { result: Ok Bool.true, rest: [] }
|
||||
Ok "false" -> { result: Ok Bool.false, rest: [] }
|
||||
_ -> { result: Err TooShort, rest: bytes }
|
||||
envString = Decode.custom \bytes, @EnvFormat {} ->
|
||||
when Str.fromUtf8 bytes is
|
||||
Ok s -> { result: Ok s, rest: [] }
|
||||
Err _ -> { result: Err TooShort, rest: bytes }
|
||||
|
||||
envList = \decodeElem -> Decode.custom \bytes, @EnvFormat {} ->
|
||||
# Per our supported methods of decoding, this is either a list of strings or
|
||||
# a list of numbers; in either case, the list of bytes must be Utf-8
|
||||
# decodable. So just parse it as a list of strings and pass each chunk to
|
||||
# the element decoder. By construction, our element decoders expect to parse
|
||||
# a whole list of bytes anyway.
|
||||
decodeElems = \allBytes, accum ->
|
||||
{ toParse, remainder } =
|
||||
when List.splitFirst allBytes (Num.toU8 ',') is
|
||||
Ok { before, after } ->
|
||||
{ toParse: before, remainder: Some after }
|
||||
|
||||
Err NotFound ->
|
||||
{ toParse: allBytes, remainder: None }
|
||||
|
||||
when Decode.decodeWith toParse decodeElem (@EnvFormat {}) is
|
||||
{ result, rest } ->
|
||||
when result is
|
||||
Ok val ->
|
||||
when remainder is
|
||||
Some restBytes -> decodeElems restBytes (List.append accum val)
|
||||
None -> Done (List.append accum val)
|
||||
|
||||
Err e -> Errored e rest
|
||||
|
||||
when decodeElems bytes [] is
|
||||
Errored e rest -> { result: Err e, rest }
|
||||
Done vals ->
|
||||
{ result: Ok vals, rest: [] }
|
||||
|
||||
# TODO: we must currently annotate the arrows here so that the lambda sets are
|
||||
# exercised, and the solver can find an ambient lambda set for the
|
||||
# specialization.
|
||||
envRecord : _, (_, _ -> [Keep (Decoder _ _), Skip]), (_ -> _) -> Decoder _ _
|
||||
envRecord = \_initialState, _stepField, _finalizer -> Decode.custom \bytes, @EnvFormat {} ->
|
||||
{ result: Err TooShort, rest: bytes }
|
25
examples/interactive/env.roc
Normal file
25
examples/interactive/env.roc
Normal file
|
@ -0,0 +1,25 @@
|
|||
app "env"
|
||||
packages { pf: "cli-platform/main.roc" }
|
||||
imports [pf.Stdout, pf.Env, pf.Task, pf.Program.{ Program }]
|
||||
provides [main] to pf
|
||||
|
||||
main : Program
|
||||
main =
|
||||
Env.decode "EDITOR"
|
||||
|> Task.await (\editor -> Stdout.line "Your favorite editor is \(editor)!")
|
||||
|> Task.await (\{} -> Env.decode "SHLVL")
|
||||
|> Task.await
|
||||
(\lvl ->
|
||||
when lvl is
|
||||
1u8 -> Stdout.line "You're running this in a root shell!"
|
||||
n ->
|
||||
lvlStr = Num.toStr n
|
||||
|
||||
Stdout.line "Your current shell level is \(lvlStr)!")
|
||||
|> Task.await (\{} -> Env.decode "LETTERS")
|
||||
|> Task.await
|
||||
(\letters ->
|
||||
joinedLetters = Str.joinWith letters " "
|
||||
|
||||
Stdout.line "Your favorite letters are: \(joinedLetters)")
|
||||
|> Program.quick
|
Loading…
Add table
Add a link
Reference in a new issue