ianmackenzie/elm-iso-10303/6.0.0

NOTE: the latest version is 8.0.1

Formerly ianmackenzie/elm-step-file. Search finds this package by either name.

Step.Decode

This module lets you decode data from STEP files in a similar way to how you decode JSON.

Decoder types

type Decoder i a

A Decoder describes how to attempt to decode some input of type i (an entire file, an individual entity, a specific attribute) and produce some output of type a.

type File

Represents an entire STEP file composed of a header and a list of entities. The only way to extract data from a File is by using a decoder, so you'll likely never need to refer to or use this type directly.

type Error
= ParseError String
| NonexistentEntity Int
| CircularReference (List Int)
| DecodeError String

Different kinds of errors that may be encountered when loading a STEP file:

  • ParseError: the file could not be parsed as a valid STEP file.
  • NonexistentEntity: one entity in the STEP file refers to another by integer ID, but no entity with that ID exists in the file.
  • CircularReference: There exists a circular chain of entities in the file that refer to each other; the List Int is the list of entity IDs in the chain.
  • DecodeError: the file was parsed OK and all entity references were resolved successfully, but an error occurred when running the given decoder.

Decoding a file

file : Decoder File a -> String -> Result Error a

Decode a STEP file given as a String using the given decoder. For example, to extract the file header and all CARTESIAN_POINT entities from a given file, you might write:

import Step.Types as Step
import Step.Decode as Decode exposing (Decoder)


type alias FileData =
    { header : Step.Header
    , points : List ( Float, Float, Float )
    }


pointDecoder : Decoder Step.Entity ( Float, Float, Float )
pointDecoder =
    Decode.entity "CARTESIAN_POINT" <|
        Decode.attribute 1 (Decode.tuple3 Decode.float)


fileDecoder : Decoder Step.File FileData
fileDecoder =
    Decode.map2 FileData
        Decode.header
        (Decode.all pointDecoder)


fileContents : String
fileContents =
    "ISO-10303-21;...END-ISO-10303-21;"


decoded : Result Decode.Error FileData
decoded =
    Decode.file fileDecoder fileContents

Attempt to find exactly one entity in a file that matches the given decoder. If there are no matching entities or more than one matching entity, the decoder will fail.

all : Decoder Entity a -> Decoder File (List a)

Find all entities in a file matching the given decoder.

Top-level entities

It may be useful in some cases to only decode top-level entities (entities that are not referenced by any other entities). These otherwise work just like single and all.

Attempt to find exactly one top-level entity in a file that matches the given decoder.

Find all top-level entities in a file matching the given decoder.

Decoding an entity

entity : String -> Decoder (List Attribute) a -> Decoder Entity a

Decode an entity of the given type name, using the given decoder on the entity's list of attributes.

The decoder will skip any entities with a different type name. This means that, for example,

Decode.all <|
    Decode.entity "SOME_TYPE" <|
        Decode.attribute 0 attributeDecoder

will attempt to decode all entities of type SOME_TYPE and skip the rest.

Get the integer ID of a particular entity in a file. Note that these are not guaranteed to be consistent between two different versions of a given STEP file.

Decoding attributes

Decode a specific attribute by index (starting from 0) in an attribute list. To decode data from multiple attributes in a list, you can use one of the mapN functions, for example:

type alias MyAttributes =
    { name : String
    , age : Int
    , height : Float
    }


myAttributesDecoder : Decoder (List Attribute) MyAttributes
myAttributesDecoder =
    Decode.map3 MyAttributes
        (Decode.attribute 0 Decode.string)
        (Decode.attribute 1 Decode.int)
        (Decode.attribute 2 Decode.float)

Decode a single attribute as a Bool (from the special STEP enum values .T. and .F.).

Decode a single attribute as an Int.

Decode a single attribute as a Float. Note that unlike JSON, STEP has different encodings for Int and Float values so you cannot use this function to decode integer values; if you want to decode an integer value as a Float you will need to use

Decode.map toFloat Decode.int

Decode a single attribute as a String.

enum : List ( String, a ) -> Decoder Attribute a

Decode a single enum attribute, by passing a list of enum cases as their STEP type name and corresponding Elm value. For example, given a STEP enum with values RED, YELLOW and GREEN you might write:

type LightColor
    = Red
    | Yellow
    | Green


lightColorDecoder : Decoder Attribute LightColor
lightColorDecoder =
    Decode.enum
        [ ( "RED", Red )
        , ( "YELLOW", Yellow )
        , ( "GREEN", Green )
        ]

(Note that the given strings will be normalized, so case does not matter.)

Decode an attribute which is a reference to another entity, by providing the decoder to use for that entity.

binaryData : Decoder a -> Decoder Attribute a

Decode a single attribute as a blob of binary data, using the given binary decoder.

Decode the special 'null' attribute ($) as the given value.

Decode an attribute that may be null, returning Nothing if it is.

Decode an attribute as a list, passing the decoder to be used for each list item.

Decode a list of exactly two elements, passing the decoder to be used for the two elements.

Decode a list of exactly three elements, passing the decoder to be used for the three elements.

Decode the special 'derived value' attribute (*) as the given value.

Typed attributes

boolAs : String -> Decoder Attribute Bool

Decode a Bool wrapped as the given type.

intAs : String -> Decoder Attribute Int

Decode an Int wrapped as the given type.

floatAs : String -> Decoder Attribute Float

Decode a Float wrapped as the given type.

stringAs : String -> Decoder Attribute String

Decode a String wrapped as the given type.

enumAs : String -> List ( String, a ) -> Decoder Attribute a

Decode an enum wrapped as the given type.

binaryDataAs : String -> Decoder a -> Decoder Attribute a

Decode binary data wrapped as the given type.

Note that while the STEP format supports binary data with an arbitrary number of bits, Elm only supports byte-aligned binary data (where the number of bits is a multiple of eight). This means that the Bytes value passed to the decoder may be zero-padded. For example, if a STEP file contained a binary data attribute with the value 011001 (six bits), the Elm Bytes value that would actually end up being decoded would be 00011001 (eight bits/one byte).

listAs : String -> Decoder Attribute a -> Decoder Attribute (List a)

Decode a list wrapped as the given type.

Working with decoders

succeed : a -> Decoder i a

A special decoder that always succeeds with the given value. Primarily useful with andThen.

fail : String -> Decoder i a

A special decoder that always fails with the given error message.

map : (a -> b) -> Decoder i a -> Decoder i b

Map the value produced by a decoder.

map2 : (a -> b -> c) -> Decoder i a -> Decoder i b -> Decoder i c

Map over two decoders.

(a -> b -> c -> d)
-> Decoder i a
-> Decoder i b
-> Decoder i c
-> Decoder i d

Map over three decoders.

(a -> b -> c -> d -> e)
-> Decoder i a
-> Decoder i b
-> Decoder i c
-> Decoder i d
-> Decoder i e

Map over four decoders.

(a -> b -> c -> d -> e -> f)
-> Decoder i a
-> Decoder i b
-> Decoder i c
-> Decoder i d
-> Decoder i e
-> Decoder i f

Map over five decoders.

(a -> b -> c -> d -> e -> f -> g)
-> Decoder i a
-> Decoder i b
-> Decoder i c
-> Decoder i d
-> Decoder i e
-> Decoder i f
-> Decoder i g

Map over six decoders.

(a -> b -> c -> d -> e -> f -> g -> h)
-> Decoder i a
-> Decoder i b
-> Decoder i c
-> Decoder i d
-> Decoder i e
-> Decoder i f
-> Decoder i g
-> Decoder i h

Map over seven decoders.

(a -> b -> c -> d -> e -> f -> g -> h -> j)
-> Decoder i a
-> Decoder i b
-> Decoder i c
-> Decoder i d
-> Decoder i e
-> Decoder i f
-> Decoder i g
-> Decoder i h
-> Decoder i j

Map over eight decoders.

resolve : Decoder i (Result String a) -> Decoder i a

It is often useful to call functions like Decode.map2 but pass a mapping function that may fail (a function that returns a Result String a instead of just a value of type a). For example, you might try to decode the ratio of two numbers, failing if the denominator is zero:

Decode.map2
    (\numerator denominator ->
        if denominator == 0 then
            Err "Denominator cannot be zero"


        else
            Ok (numerator / denominator)
    )
    (Decode.attribute 0 Decode.float)
    (Decode.attribute 1 Decode.float)

However, this is now a Decoder (List Attribute) (Result String Float) instead of just a Decoder (List Attribute) Float. Using resolve will convert the Ok case to a successful decode and the Err case to a decode error, giving you the decoder that you want:

ratioDecoder : Decoder (List Attribute) Float
ratioDecoder =
    Decode.resolve <|
        Decode.map2
            (\numerator denominator ->
                if denominator == 0 then
                    Err "Denominator cannot be zero"


                else
                    Ok (numerator / denominator)
            )
            (Decode.attribute 0 Decode.float)
            (Decode.attribute 1 Decode.float)
andThen : (a -> Decoder i b) -> Decoder i a -> Decoder i b

Run one decoder on some input, and based on the result of the first decoder decide what decoder to apply next to the same input.

flatten : Decoder i (Decoder i a) -> Decoder i a

You may run into cases where you end up with a decoder that returns another decoder, perhaps if you construct a decoder within a call to Decode.map2 or similar. This function will 'flatten' the resulting decoder;

Decode.flatten

is equivalent to

Decode.andThen identity

Construct an entity decoder that may match one of many entity types. The first decoder with a matching type will be used. If none of the entity types match, then the current entity is skipped.

lazy : (() -> Decoder i a) -> Decoder i a

Define a decoder lazily such that it is only constructed if needed. This is primarily used to break circular reference chains between decoders.

A special decoder that just returns its input unmodified. This can be useful if you want to get a raw Entity or Attribute value and inspect it.