Instantly share code, notes, and snippets.

Embed
What would you like to do?
F# code for getting attribute data off discriminated union cases
type FatalError(message: string) =
inherit System.Attribute()
member __.Message = message
type Errors =
| [<FatalError("The value specified is currently not available")>] UnknownValue
| NotAnError
let PrintErrorMessage : Errors -> string =
fun err ->
// Get UnionCaseInfo value from the F# reflection tools
let (case, _args) = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(err, typeof<Errors>)
// Pull all attributes
let attributes = case.GetCustomAttributes()
// Find the attributes that have an error message
let haveError =
attributes
|> Seq.ofArray
// Filter for the FatalError values
|> Seq.filter (fun x -> x :? FatalError)
// Cast each value
|> Seq.map (fun x -> x :?> FatalError)
// Seq.take breaks if there aren't enough, so use Seq.truncate instead
|> Seq.truncate 1
|> List.ofSeq
// If the FatalError attribute wasn't found, give a default message
if haveError.IsEmpty then "(no error message available)"
else haveError.Head.Message
printfn "%s" <| PrintErrorMessage Errors.UnknownValue
printfn "%s" <| PrintErrorMessage Errors.NotAnError
// Alternative version of PrintErrorMessage which returns the attribute
// Allows providing a default or "backup" value
let GetAttribute<'T> : 'T -> obj -> 'T =
fun backup value ->
let (case, _args) = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(value, value.GetType())
let attributeValue =
case.GetCustomAttributes()
|> Seq.ofArray
|> Seq.filter (fun x -> x :? 'T)
|> Seq.truncate 1
|> Seq.map (fun x -> x :?> 'T)
|> List.ofSeq
if attributeValue.IsEmpty then backup
else attributeValue.Head
let GetFatalErrorMessage = GetAttribute <| FatalError("(no error message available)")
let unknownValue = GetFatalErrorMessage Errors.UnknownValue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment