# One-Liners A cookbook of idiomatic Deft one-liners. Each recipe shows the preferred modern form. Use these as a reference when scripting. ## Process and Shell
 # Capture stdout of a command
def files ls -la

# Run a subprocess and check exit
def r [os/run "git" "status"]
if ($r~exit == 0) { echo "clean" }

# Run a shell-style pipeline
def lines [os/shell "find . -name '*.dft' | wc -l"]

# Get the script's own filename
def argv [os/args]
def script-name $argv(first)

# Iterate shell output lines
for line ps aux(lines) { echo $line }

# Iterate shell output words
for word ls(words) { echo $word } 
## Files
 # Read whole file as string
def text [fs/read "config.json"]

# Read lines into a list
def lines [fs/read-lines "log.txt"]

# Write a file
fs/write "out.txt" $content

# Append to a file
fs/append "log.txt" "newline\n"

# Check existence
if [fs/exists? "config.json"] { ... }

# List directory entries
for entry [fs/list "."] { echo $entry~name }

# Glob recursively
for path [fs/glob "src/**/*.dft"] { echo $path }

# Stat a file
def info [fs/stat "file.dft"]
echo $info~size
echo $info~mtime

# Make a directory
fs/mkdir "build/out"

# Path manipulation
fs/join "a" "b" "c.dft"                     # => "a/b/c.dft"
fs/base "/path/to/file.dft"                 # => "file.dft"
fs/dir "/path/to/file.dft"                  # => "/path/to"
fs/ext "file.dft"                           # => "dft"

# Temp paths
def tmp [fs/tmp-path ".json"]               # /tmp/abc123.json 
## Strings
 # Length
"hello"(len)                                 # => 5

# Upper / lower
"hello"(upper)                               # => HELLO
"WORLD"(lower)                               # => world

# Trim
"  hi  "(trim)                               # => "hi"

# Split and join
"a,b,c"(split ",")                           # => @{a b c}
@{a b c}(join "|")                           # => "a|b|c"

# Replace
"hello world"(replace "o" "0")               # => "hell0 w0rld"

# Test prefix / suffix / substring
"hello"(starts-with? "he")                   # => true
"hello"(ends-with? "lo")                     # => true
"hello"(contains "ell")                      # => true

# Substring
"hello"(0 .. 3)                              # => "hel"
"hello"(nth 1)                               # => "e"

# Pad
"5"(pad-left 3 "0")                          # => "005"

# Printf-style formatting
[format "%d times %#x is %e" 10 10 100]      # => "10 times 0xa is 1.000000e+02"
[format "%05.2f" 3.14]                       # => "03.14"

# Lines / words
"a\nb\nc"(lines)                             # => @{a b c}
"a b c"(words)                               # => @{a b c}

# Iterate chars
for ch in "abc" { echo $ch } 
## Numbers and Math
 # Arithmetic
(1 + 2)
((3 * 4) - 5)
(10 mod 3)                                    # => 1

# Comparison
($a < $b)
($x == 0)

# Rounding
(3.7)(floor)                                  # => 3
(3.2)(ceil)                                   # => 4
(3.5)(round)                                  # => 4

# Min / max / clamp
[min 3 7]                                     # => 3
[max 3 7]                                     # => 7
[clamp 15 0 10]                               # => 10

# Absolute value
[abs -5]                                      # => 5

# Specialised math
[math/sqrt 16]                                # => 4
[math/pow 2 10]                               # => 1024
[math/sin [math/pi]]
[math/sum @{1 2 3 4}]                         # => 10
[math/avg @{10 20 30}]                        # => 20

# Random
[random]                                      # => 0..max-int
[random 100]                                  # => 0..99
[math/random-range 10 20]                     # => 10..19 
## Lists and Sequences
 # Length / edges
@{1 2 3}(len)                                 # => 3
@{1 2 3}(first)                               # => 1
@{1 2 3}(last)                                # => 3

# Membership
3 in? @{1 2 3}                                # => true
[in? @{1 2 3} 3]                              # => true (prefix form)

# Slice
@{1 2 3 4 5}(slice 1 3)                       # => @{2 3}

# Reverse / sort / unique
@{3 1 2}(sort)                                # => @{1 2 3}
@{3 1 2}(reverse)                             # => @{2 1 3}
@{1 1 2 3 3}(unique)                          # => @{1 2 3}

# Append (mutating local)
def xs @{}
$xs << 1
$xs << 2                                      # xs is now @{1 2}

# Append (pure)
set ys [append $xs 3]                         # ys is @{1 2 3}, xs unchanged

# Pipelines
@{1 2 3 4 5} |> filter even? |> map { $it * 2 } |> collect
# => @{4 8}

# Reduce
@{1 2 3 4 5} |> reduce 0 { ($acc + $it) }     # => 15

# Find first match
@{1 2 3 4} |> find { ($it > 2) }              # => 3

# Existence
@{1 2 3} |> any? { ($it > 2) }                # => true
@{1 2 3} |> all? { ($it > 0) }                # => true

# Group
@{1 2 3 4} |> group { ($it % 2) == 0 }        # => %{ :true @{2 4} :false @{1 3} }

# Range
[range 0 5]                                   # => @{0 1 2 3 4}
[range 0 10 2]                                # => @{0 2 4 6 8} 
## Maps
 # Construction
def m %{ :a 1 :b 2 :c 3 }

# Lookup
$m~a                                          # => 1
[get $m :a]                                   # => 1
[get $m :missing 0]                           # => 0 (default)
$m(:a)                                        # => 1 (IFn form)

# Add / remove
[assoc $m :d 4]                               # => %{ :a 1 :b 2 :c 3 :d 4 }
[dissoc $m :a]                                # => %{ :b 2 :c 3 }

# Merge
[merge %{ :a 1 } %{ :a 2 :b 3 }]              # => %{ :a 2 :b 3 }

# Keys / values / entries
[keys $m]                                     # => ^{:a :b :c}
[values $m]                                   # => @{1 2 3}
[entries $m]                                  # => @{ @{:a 1} @{:b 2} @{:c 3} }

# Iterate
for entry [entries $m] { echo "$entry~0 = $entry~1" }
$m |> each { echo "$it~0 = $it~1" }

# Nested assoc
[assoc-in %{ :user %{} } @{ :user :name } "Alice"]

# Nested lookup
[get-in $user @{ :profile :name }]                 # => value, or nil if missing
[get-in $user @{ :profile :age } :unknown]         # => :unknown if missing 
## Sets
 def roles ^{:admin :user :guest}

# Membership
:admin in? $roles                            # => true

# Add
($roles << :owner)                               # => ^{:admin :user :guest :owner}

# Set ops
[union ^{1 2} ^{2 3}]                         # => ^{1 2 3}
[intersection ^{1 2 3} ^{2 3 4}]              # => ^{2 3}
[difference ^{1 2 3} ^{2 3 4}]                # => ^{1}

# Relations
[subset? ^{1 2} ^{1 2 3}]                     # => true
[superset? ^{1 2 3} ^{1 2}]                   # => true 
## JSON / YAML
 # Parse
def data [json/parse $raw-text]
def yaml-data [yaml/load "config.yaml"]

# Stringify
[json/stringify $data]
[json/pretty $data]                           # indented

# Round-trip
[json/parse [json/stringify $data]] 
## HTTP
 # GET
set r [http/get "https://api.github.com/repos/ziglang/zig"]
def body [json/parse $r~body]
echo $body~stargazers_count

# POST with JSON
def payload [json/stringify %{ :title "hello" }]
http/post "https://example.com/api" $payload

# Concurrent — task bodies see globals (snapshotted), not captured locals
from "deft/stdext/async" import await-all
def api "https://api.example.com"
def futures @{
    [deftask { [http/get "$api/a"] }]
    [deftask { [http/get "$api/b"] }]
}
def results [await-all $futures] 
## SQLite
 # Open
def db [sqlite/open ":memory:"]

# Schema
sqlite/exec $db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)"

# Insert with params
sqlite/exec $db "INSERT INTO users (name, age) VALUES (?, ?)" @{ "Alice" 30 }

# Query
def rows [sqlite/query $db "SELECT * FROM users"]
for row $rows { echo "$row~name" }

# Scalar
def count [sqlite/scalar $db "SELECT COUNT(*) FROM users"]

# Close
sqlite/close $db 
## Pattern Matching
 # Dispatch
match $status {
    200 => "ok"
    404 => "missing"
    _   => "other"
}

# Destructure
match $entry {
    %{ :name $n :age $a } => "$n is $a"
    _                      => "unknown"
}

# Tagged map
match $result {
    %Ok{ :value $v }    => $v
    %Err{ :message $m } => [throw $m]
}

# Range
match $score {
    90 .. 100 => "A"
    80 .. 89  => "B"
    _         => "C"
} 
## Transducers
 # Build a reusable pipeline
def xf [comp
    [filter { ($it % 2) == 0 }]
    [map { $it * $it }]
    [take 5]]

# Apply to different sinks/sources
[into @{} xf [range 0 1000]]
[into ^{} xf $other-source]
[transduce xf + 0 [range 0 1000]]            # sum 
## Concurrency
 # Async value — deftask runs the body on a real OS thread
set f [deftask { [slow-op] }]
# ... do other work ...
def result [await $f]

# Concurrent map — bodies must be self-contained (no captured locals)
from "deft/stdext/async" import await-all
def f1 [deftask { [http/get "https://a.example.com"] }]
def f2 [deftask { [http/get "https://b.example.com"] }]
def results [await-all @{$f1 $f2}]

# One-shot timer
after 5000 { echo "five seconds" }

# Sleep
sleep 100 
## Git
 def branch git rev-parse --abbrev-ref HEAD
def hash   git rev-parse HEAD
def dirty  git status --porcelain

if not(empty? git status --porcelain) {
    echo "dirty tree"
}

# Files changed since main
def changed git diff --name-only main(lines) 
## Type Checks
 [type 42]                                     # => "number"
[type "hi"]                                   # => "string"
[type %User{...}]                             # => "User"

[is? "number" 42]                             # => true
[nil? $x]
[empty? $xs]
[satisfies? $val "Drawable"] 
## Time
 def now [clock/seconds]
def iso [clock/format $now "%Y-%m-%dT%H:%M:%S%z"]

def later [clock/add $now 1 :hour]
def parsed [clock/scan "2024-05-01" %{ :format "%Y-%m-%d" :timezone "UTC" }]

def ms [clock/millis]
def elapsed ($end - $start) 
## Environment
 def home [os/env "HOME"]
def path [os/env "PATH"]
def cwd [os/cwd]

# Args
def args [os/args]
def argv $args(rest) 
## Crypto
 def hash [crypto/sha256 "hello"]
def mac  [crypto/hmac :sha256 "key" "data"]
def salt [crypto/rand-hex 16]
def token [crypto/rand 32]

def encoded [crypto/b64-encode $data]
def decoded [crypto/b64-decode $encoded] 
## Compress
 compress/gzip "file.txt" "file.txt.gz"
compress/gunzip "file.txt.gz" "file.txt"

compress/tgz-create @{"src"} "src.tar.gz"
compress/tgz-extract "src.tar.gz" "out/"
compress/list-tgz "src.tar.gz" 
## See Also - [Shell Scripting](shell-scripting) — narrative tutorial - [Stdlib](stdlib) — full standard library catalogue - [Pattern Matching](pattern-matching) — full pattern reference