/src/FSharpx.TypeProviders.Documents/Inference.fs

https://github.com/mausch/fsharpx · F# · 176 lines · 124 code · 40 blank · 12 comment · 4 complexity · 2d65ef2b7571bb774b70563f67d59d2c MD5 · raw file

  1. // ----------------------------------------------------------------------------
  2. // Original Xml type provider
  3. // (c) Tomas Petricek - tomasP.net, Available under Apache 2.0 license.
  4. // ----------------------------------------------------------------------------
  5. module internal FSharpx.TypeProviders.Inference
  6. open System
  7. open System.Xml
  8. open System.Xml.Linq
  9. open FSharpx.TypeProviders.Helper
  10. open System.Collections.Generic
  11. open System.Globalization
  12. open FSharpx.Strings
  13. // ------------------------------------------------------------------------------------------------
  14. // Representation about inferred structure
  15. // ------------------------------------------------------------------------------------------------
  16. type SimpleProperty = SimpleProperty of string * Type * bool
  17. type CompoundProperty = CompoundProperty of string * bool * CompoundProperty seq * SimpleProperty seq
  18. open System.IO
  19. open Samples.FSharp.ProvidedTypes
  20. open Microsoft.FSharp.Quotations
  21. open Microsoft.FSharp.Core.CompilerServices
  22. /// Generate property for every inferred property
  23. let generateProperties (ownerType:ProvidedTypeDefinition) accessExpr checkIfOptional setterExpr optionalSetterExpr elementProperties =
  24. for SimpleProperty(propertyName,propertyType,optional) in elementProperties do
  25. let property =
  26. if optional then
  27. let newType = optionType propertyType
  28. // For optional elements, we return Option value
  29. let cases = Reflection.FSharpType.GetUnionCases newType
  30. let some = cases |> Seq.find (fun c -> c.Name = "Some")
  31. let none = cases |> Seq.find (fun c -> c.Name = "None")
  32. let optionalAccessExpr =
  33. (fun args ->
  34. Expr.IfThenElse
  35. (checkIfOptional propertyName args,
  36. Expr.NewUnionCase(some, [accessExpr propertyName propertyType args]),
  37. Expr.NewUnionCase(none, [])))
  38. ProvidedProperty(
  39. propertyName = niceName propertyName,
  40. propertyType = newType,
  41. GetterCode = optionalAccessExpr,
  42. SetterCode = optionalSetterExpr propertyName propertyType)
  43. else
  44. ProvidedProperty(
  45. propertyName = niceName propertyName,
  46. propertyType = propertyType,
  47. GetterCode = accessExpr propertyName propertyType,
  48. SetterCode = setterExpr propertyName propertyType)
  49. property.AddXmlDoc(sprintf "Gets the %s attribute" propertyName)
  50. ownerType.AddMember property
  51. /// Iterates over all the sub elements, generates types for them
  52. /// and adds member for accessing them to the parent.
  53. let generateSublements (ownerType:ProvidedTypeDefinition) parentType multiAccessExpr addChildExpr newChildExpr singleAccessExpr generateTypeF children =
  54. for CompoundProperty(childName,multi,_,_) as child in children do
  55. let childType = generateTypeF parentType child
  56. if multi then
  57. let newType = seqType childType
  58. let niceChildName = childName |> niceName |> singularize
  59. let getChildrenMethod =
  60. ProvidedMethod(
  61. methodName = "Get" + pluralize niceChildName,
  62. parameters = [],
  63. returnType = newType,
  64. InvokeCode = multiAccessExpr childName)
  65. getChildrenMethod.AddXmlDoc (sprintf @"Gets the %s elements" childName)
  66. ownerType.AddMember getChildrenMethod
  67. let newChildMethod =
  68. ProvidedMethod(
  69. methodName = "New" + niceChildName,
  70. parameters = [],
  71. returnType = childType,
  72. InvokeCode = newChildExpr childName)
  73. newChildMethod.AddXmlDoc (sprintf @"Creates a new %s element" childName)
  74. ownerType.AddMember newChildMethod
  75. let addChildMethod =
  76. ProvidedMethod(
  77. methodName = "Add" + niceChildName,
  78. parameters = [ProvidedParameter("element", childType)],
  79. returnType = typeof<unit>,
  80. InvokeCode = addChildExpr childName)
  81. addChildMethod.AddXmlDoc (sprintf @"Adds a %s element" childName)
  82. ownerType.AddMember addChildMethod
  83. else
  84. let childGetter =
  85. ProvidedProperty(
  86. propertyName = niceName childName,
  87. propertyType = childType,
  88. GetterCode = singleAccessExpr childName)
  89. childGetter.AddXmlDoc (sprintf @"Gets the %s attribute" childName)
  90. ownerType.AddMember childGetter
  91. ownerType
  92. type ExprDef = Expr list -> Expr
  93. type GeneratedParserSettings = {
  94. Schema: CompoundProperty
  95. EmptyConstructor: ExprDef
  96. FileNameConstructor: ExprDef
  97. DocumentContentConstructor : ExprDef
  98. RootPropertyGetter: ExprDef
  99. ToStringExpr: ExprDef }
  100. /// Generates constructors for loading data and adds type representing Root node
  101. let createParserType<'a> typeName (generateTypeF: ProvidedTypeDefinition -> CompoundProperty -> ProvidedTypeDefinition) settings =
  102. let parserType = erasedType<'a> thisAssembly rootNamespace typeName
  103. let defaultConstructor =
  104. ProvidedConstructor(
  105. parameters = [],
  106. InvokeCode = settings.EmptyConstructor)
  107. defaultConstructor.AddXmlDoc "Initializes the document from the schema sample."
  108. parserType.AddMember defaultConstructor
  109. let fileNameConstructor =
  110. ProvidedConstructor(
  111. parameters = [ProvidedParameter("filename", typeof<string>)],
  112. InvokeCode = settings.FileNameConstructor)
  113. fileNameConstructor.AddXmlDoc "Initializes a document from the given path."
  114. parserType.AddMember fileNameConstructor
  115. let inlinedDocumentConstructor =
  116. ProvidedConstructor(
  117. parameters = [ProvidedParameter("documentContent", typeof<string>)],
  118. InvokeCode = settings.DocumentContentConstructor)
  119. inlinedDocumentConstructor.AddXmlDoc "Initializes a document from the given string."
  120. parserType.AddMember inlinedDocumentConstructor
  121. let rootProperty =
  122. ProvidedProperty(
  123. propertyName = "Root",
  124. propertyType = generateTypeF parserType settings.Schema,
  125. GetterCode = settings.RootPropertyGetter)
  126. rootProperty.AddXmlDoc "Gets the document root"
  127. parserType.AddMember rootProperty
  128. let toStringMethod =
  129. ProvidedMethod(
  130. methodName = "ToString",
  131. parameters = [],
  132. returnType = typeof<string>,
  133. InvokeCode = settings.ToStringExpr)
  134. toStringMethod.AddXmlDoc "Gets the string representation"
  135. parserType.AddMember toStringMethod
  136. parserType