REBOL [
    Title: "HTTP Console Color"
    Purpose: "HTTP TTY-style Console"
    Date:  8-Sep-2010
    File: %httpc-color.r
    Version: 0.1.1
    Author: "Christopher Ross-Gill"
]

do http://reb4.me/r/rest
do http://reb4.me/r/altwebform

use [
    command request response
    root path query
    commands styles
    emit prompt summarize
][
    root: any [
        all [
            url? system/script/args
            parse/all url ["http" opt "s" "://" to end]
            system/script/args
        ]
        http://www.rebol.com/
    ]

    path: "/"

    styles: context [
        reset: "^[[0m^[[4D"
        bold: context [on: "^[[1m^[[4D" off: "^[[22m^[[5D"]
        italic: context [on: "^[[3m^[[4D" off: "^[[23m^[[5D"]
        underline: context [on: "^[[4m^[[4D" off: "^[[24m^[[5D"]
        black: context [back: "^[[40m^[[5D" text: "^[[30m^[[5D"]
        red: context [back: "^[[41m^[[5D" text: "^[[31m^[[5D"]
        green: context [back: "^[[42m^[[5D" text: "^[[32m^[[5D"]
        yellow: context [back: "^[[43m^[[5D" text: "^[[33m^[[5D"]
        blue: context [back: "^[[44m^[[5D" text: "^[[34m^[[5D"]
        white: context [back: "^[[47m^[[5D" text: "^[[37m^[[5D"]
    ]

    emit: func [data [block! string!]][
        data: bind compose [(data)] styles
        print rejoin data
    ]

    prompt: func [data [block! string!]][
        data: bind compose [(data) " "] styles
        prin rejoin data
        ask ""
    ]

    request: context [
        action: content: type: headers: none
    ]

    send: func [spec][
        spec: read/custom head change root/:path "rest" third spec
        set request none
        emit [
            case [
                spec/status < 299 [join green/back black/text]
                spec/status < 399 [join blue/back white/text]
                spec/status < 499 [join red/back white/text]
                spec/status < 599 [join yellow/back black/text]
            ]
            " " spec/status " " reset " " round (length? spec/http-headers) / 2 " Headers"
        ]
        spec
    ]

    summarize: func [content type /local mk][
        content: copy/part content 100
        either find/match type "text/" [
            content: to-string content
        ][
            content: trim enbase/base content 16
            mk: 0
            forskip content 3 [
                mk: mk + 1
                unless tail? skip content 2 [
                    insert skip content 2 either equal? mk // 24 0 ["^/"][" "]
                ]
            ]
        ]
        content
    ]

    commands: use [args output][[
          "address " args: "http://" to end (root: to-url args)
        | "cd " copy path to end
        | copy args ["get" | "post" | "head" | "put" | "delete"] end (
            request/action: :args
            response: send request
        )
        | "header" opt "s" (
            all [
                response
                foreach [header content] response/http-headers [
                    emit [blue/text form header ": " reset content]
                ]
            ]
        )
        | "body" [end (
            all [
                response
                emit [
                    summarize response/content response/http-headers/(to-set-word "Content-Type")
                    "..."
                ]
            ]
        ) | " flat" (all [response emit [trim copy/part enbase/base response/content 16 100]])]
        | "content " args "[" (
            all [args: attempt [load/next args]
            request/content: try args/1]
        )
        | to end (emit ["=== Unknown Command"])
    ]]

    while [not find ["q" "quit"] command: prompt [red/text root/:path reset ">"]][
        parse/all trim/head/tail command commands
        do []
    ]
]