blob: 6fe8d8f029b291eb22b173915a1ba9f2227cf4cf [file] [log] [blame]
-- Copyright 2020 The Fuchsia Authors. All rights reserved.
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
module Mode exposing
( Map
, Mode(..)
, decode
, decodeMap
, defaultInput
, defaultOutput
, empty
, encode
, encodeMap
, fromString
, get
, inputOptions
, inputOutputOptions
, inputs
, insert
, nextInput
, nextOutput
, outputs
, previousInput
, previousOutput
, singleton
, title
)
{-| This module defines the Mode type.
A mode is a format supported by fidlbolt. Some of them, like Fidl and Go,
directly correspond to file types. Others, like BytesPlus and Diff, are more
like abstract parsers/targets.
In fidlbolt, the user selects an input and output mode. Only some input modes
are allowed, and for a given input mode only a some output modes are allowed.
The core functionality of the app is implemented in a set of input-output
transformations. For example, (Fidl, Json) compiles a FIDL file with fidlc;
(Bytes, FidlText) attemps to decode bytes (represented in hex) to FIDL Text.
-}
import Dict exposing (Dict)
import Form exposing (Form)
import Json.Decode as Decode
import Json.Encode as Encode
import Regex
------------- MODE -------------------------------------------------------------
type Mode
= Bytes
| BytesPlus
| C
| Dart
| Diff
| Fidl
| FidlText
| Go
| Hlcpp
| Json
| Llcpp
| Rust
inputs : List Mode
inputs =
[ Fidl, FidlText, Bytes ]
outputs : Mode -> List Mode
outputs input =
case input of
Fidl ->
[ Fidl, Json, C, Llcpp, Hlcpp, Rust, Go, Dart ]
FidlText ->
-- TODO(mkember): Add BytesPlus once implemented.
[ FidlText, Bytes ]
Bytes ->
-- TODO(mkember): Add BytesPlus once implemented.
[ Bytes, Diff, FidlText ]
_ ->
[]
defaultInput : Mode
defaultInput =
Fidl
defaultOutput : Mode -> Mode
defaultOutput input =
case input of
Fidl ->
Json
FidlText ->
Bytes
Bytes ->
Bytes
_ ->
input
nextInput : Mode -> Mode
nextInput input =
Maybe.withDefault input (getNext input inputs)
previousInput : Mode -> Mode
previousInput input =
Maybe.withDefault input (getPrevious input inputs)
nextOutput : Mode -> Mode -> Mode
nextOutput input output =
Maybe.withDefault output (getNext output (outputs input))
previousOutput : Mode -> Mode -> Mode
previousOutput input output =
Maybe.withDefault output (getPrevious output (outputs input))
getNext : a -> List a -> Maybe a
getNext item list =
let
rec restOfList =
case restOfList of
[] ->
Nothing
[ last ] ->
if last == item then
List.head list
else
Nothing
first :: second :: rest ->
if first == item then
Just second
else
rec (second :: rest)
in
rec list
getPrevious : a -> List a -> Maybe a
getPrevious item list =
getNext item (List.reverse list)
{-| Returns a form containing options for the given input. These get passed
along to the server and affect its response.
-}
inputOptions : Mode -> Form
inputOptions input =
let
versionSelectionRegex =
Maybe.withDefault Regex.never <|
Regex.fromString "^\\s*([a-z][a-z0-9_]*:([0-9]+|HEAD)\\s*)*$"
entries =
case input of
Fidl ->
[ Form.text "versionSelection"
"Version"
""
{ validate = Regex.contains versionSelectionRegex
, placeholder = Just "test:HEAD"
, invalidMessage = "Invalid format. Try “test:HEAD” or “fuchsia:7”."
}
]
_ ->
[]
in
Form.define entries
{-| Returns a form containing options for the given input-output transformation.
These get passed along to the server and affect its response.
-}
inputOutputOptions : Mode -> Mode -> Form
inputOutputOptions input output =
let
files default others =
Form.select "file" "File" (default :: others) (Tuple.first default)
entries =
case ( input, output ) of
( Fidl, Fidl ) ->
[ Form.checkbox "lint" "Lint" False ]
( Fidl, Json ) ->
[ files ( "ir", "Library IR" )
[ ( "schema", "Schema" )
, ( "deps", "Dependencies" )
]
]
( Fidl, Hlcpp ) ->
[ files ( "header", "Header" )
[ ( "source", "Source" )
, ( "test", "Test base" )
, ( "tables", "Tables" )
]
]
-- TODO(fxbug.dev/93781): Since LLCPP has so many files, we are
-- using the real filenames rather than coming up with "Sentence
-- case names" for each one. We should change the other ones to
-- be consistent (and eventually not hardcode any of this).
( Fidl, Llcpp ) ->
[ files ( "common_types.h", "common_types.h" )
[ ( "markers.h", "markers.h" )
, ( "wire_types.h", "wire_types.h" )
, ( "wire_types.cc", "wire_types.cc" )
, ( "wire.h", "wire.h" )
, ( "wire_messaging.h", "wire_messaging.h" )
, ( "wire_messaging.cc", "wire_messaging.cc" )
, ( "wire_test_base.h", "wire_test_base.h" )
, ( "natural_types.h", "natural_types.h" )
, ( "natural_types.cc", "natural_types.cc" )
, ( "fidl.h", "fidl.h" )
, ( "natural_messaging.h", "natural_messaging.h" )
, ( "natural_messaging.cc", "natural_messaging.cc" )
, ( "driver/wire.h", "driver/wire.h" )
, ( "driver/wire_messaging.h", "driver/wire_messaging.h" )
, ( "driver/wire_messaging.cc", "driver/wire_messaging.cc" )
, ( "driver/natural_messaging.h", "driver/natural_messaging.h" )
, ( "driver/natural_messaging.cc", "driver/natural_messaging.cc" )
-- TODO(fxbug.dev/93781): For consistency, we're showing
-- "tables.c" in the dropdown, but still passing the
-- identifier "tables" to the backend like all other
-- bindings. Although coding tables aren't produced by
-- fidlgen_llcpp, they are used by LLCPP, and the idea
-- was to include all files related to that binding.
, ( "tables", "tables.c" )
]
]
( Fidl, C ) ->
[ files ( "header", "Header" )
[ ( "client", "Client" )
, ( "server", "Server" )
, ( "tables", "Tables" )
]
]
( Fidl, Dart ) ->
[ files ( "library", "Library" )
[ ( "test", "Test base" ) ]
]
( _, Bytes ) ->
[ Form.number "columns" "Columns" 8 Form.positive
, Form.number "group" "Grouping" 2 Form.positive
, Form.checkbox "offsets" "Line offsets" True
, Form.checkbox "capital" "Capital hex" False
, Form.checkbox "ascii" "ASCII" False
]
( Bytes, Diff ) ->
[ Form.text
"separator"
"Separator"
";"
{ validate = \_ -> True
, placeholder = Nothing
, invalidMessage = ""
}
]
_ ->
[]
in
Form.define entries
------------- MAP --------------------------------------------------------------
{-| A mapping from modes to a. The key type is String rather than Mode because
non-String keys don't work well in Elm (they aren't comparable).
-}
type alias Map a =
Dict String a
empty : Map a
empty =
Dict.empty
singleton : Mode -> a -> Map a
singleton mode value =
Dict.singleton (toString mode) value
get : Mode -> Map a -> Maybe a
get mode =
Dict.get (toString mode)
insert : Mode -> a -> Map a -> Map a
insert mode value map =
Dict.insert (toString mode) value map
------------- ENCODE / DECODE --------------------------------------------------
encode : Mode -> Encode.Value
encode mode =
Encode.string (toString mode)
decode : Decode.Decoder Mode
decode =
Decode.andThen
(\string ->
case fromString string of
Just mode ->
Decode.succeed mode
Nothing ->
Decode.fail ("Invalid mode: " ++ string)
)
Decode.string
encodeMap : (a -> Encode.Value) -> Map a -> Encode.Value
encodeMap encoder map =
Encode.dict identity encoder map
decodeMap : Decode.Decoder a -> Decode.Decoder (Map a)
decodeMap decoder =
Decode.dict decoder
------------- STRING FUNCTIONS -------------------------------------------------
toString : Mode -> String
toString mode =
case mode of
Bytes ->
"bytes"
BytesPlus ->
"bytes+"
C ->
"c"
Dart ->
"dart"
Diff ->
"diff"
Fidl ->
"fidl"
FidlText ->
"fidltext"
Go ->
"go"
Hlcpp ->
"hlcpp"
Json ->
"json"
Llcpp ->
"llcpp"
Rust ->
"rust"
fromString : String -> Maybe Mode
fromString string =
case string of
"bytes" ->
Just Bytes
"bytes+" ->
Just BytesPlus
"c" ->
Just C
"dart" ->
Just Dart
"diff" ->
Just Diff
"fidl" ->
Just Fidl
"fidltext" ->
Just FidlText
"go" ->
Just Go
"hlcpp" ->
Just Hlcpp
"json" ->
Just Json
"llcpp" ->
Just Llcpp
"rust" ->
Just Rust
_ ->
Nothing
title : Mode -> String
title mode =
case mode of
Bytes ->
"Bytes"
BytesPlus ->
"Bytes+"
C ->
"C"
Dart ->
"Dart"
Diff ->
"Diff"
Fidl ->
"FIDL"
FidlText ->
"FIDL Text"
Go ->
"Go"
Hlcpp ->
"HLCPP"
Json ->
"JSON"
Llcpp ->
"LLCPP"
Rust ->
"Rust"