1 /**
2 * Copyright © DiamondMVC 2019
3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 */
6 module diamond.seo.schema.schemaobject;
7 
8 import diamond.core.apptype;
9 
10 static if (isWeb)
11 {
12   import std.variant;
13 
14   /// Wrapper around a schema object.
15   abstract class SchemaObject
16   {
17     private:
18     /// The type of the schema object.
19     string _type;
20     /// Boolean determining whether the object is a child or not.
21     package(diamond) bool _isChild;
22     /// ALl fields attached to the schema object.
23     Variant[string] _fields;
24 
25     public:
26     final:
27     /**
28     * Creates a new schema object.
29     * Params:
30     *   type = The type of the schema object. Equivalent to "@type"
31     */
32     this(string type)
33     {
34       _type = type;
35     }
36 
37     @property
38     {
39       /// Gets a boolean determining whether the schema object is a root object or not.
40       bool isRoot() { return !_isChild; }
41     }
42 
43     /**
44     * Adds a schema object field to the schema object.
45     * Params:
46     *   key =          The key of the field.
47     *   schemaObject = The schema object to add.
48     */
49     void addField(T : SchemaObject)(string key, T schemaObject)
50     {
51       auto schema = cast(SchemaObject)schemaObject;
52 
53       if (schema)
54       {
55         schema._isChild = true;
56       }
57 
58       _fields[key] = schema;
59     }
60 
61     /**
62     * Adds an array of schema objects to the schema object.
63     * Params:
64     *   key =    The key of the field.
65     *   values = An array of schema objects to add.
66     */
67     void addField(T : SchemaObject)(string key, T[] values)
68     {
69       SchemaObject[] arrayResult;
70 
71       if (values)
72       {
73         foreach (child; values)
74         {
75           child._isChild = true;
76 
77           arrayResult ~= child;
78         }
79 
80         _fields[key] = arrayResult ? arrayResult : [];
81       }
82       else
83       {
84         _fields[key] = arrayResult;
85       }
86     }
87 
88     /**
89     * Adds a field to the schema object.
90     * Params:
91     *   key =   The key of the field.
92     *   value = The value of the field.
93     */
94     void addField(T)(string key, T value)
95     {
96       _fields[key] = value;
97     }
98 
99     /**
100     * Removes a field from the schema object.
101     * Params:
102     *   key = The key of the field to remove.
103     */
104     void removeField(string key)
105     {
106       _fields.remove(key);
107     }
108 
109     /// Converts the schema object to a string that represent a JSON-LD object.
110     override string toString()
111     {
112       import std..string : format;
113       import std.conv : to;
114       import std.array : join;
115 
116       string[] fieldsValue;
117 
118       if (_fields && _fields.length)
119       {
120         foreach (k,v; _fields)
121         {
122           if (!v.hasValue)
123           {
124             continue;
125           }
126 
127           if (v.convertsTo!SchemaObject)
128           {
129             auto schemaObject = v.get!SchemaObject;
130 
131             fieldsValue ~= "\"%s\": %s".format(k,schemaObject.toString());
132           }
133           else if (v.convertsTo!(SchemaObject[]))
134           {
135             auto schemaObjects = v.get!(SchemaObject[]);
136 
137             if (schemaObjects)
138             {
139               fieldsValue ~= "\"%s\": %s".format(k,to!string(schemaObjects));
140             }
141             else
142             {
143               fieldsValue ~= "\"%s\": null".format(k);
144             }
145           }
146           else if (v.convertsTo!string)
147           {
148             auto stringValue = v.get!string;
149 
150             fieldsValue ~= "\"%s\": \"%s\"".format(k,stringValue);
151           }
152           else
153           {
154             fieldsValue ~= "\"%s\": %s".format(k,v.toString());
155           }
156         }
157       }
158 
159       if (!_isChild)
160       {
161         return `{
162     "@context" : "http://schema.org",
163     "@type": "%s"
164     %s
165   }`.format
166         (
167           _type,
168           fieldsValue && fieldsValue.length ? (", " ~ fieldsValue.join(",\r\n")) : ""
169         );
170       }
171       else
172       {
173         return `{
174     "@type": "%s"
175     %s
176     }`.format
177         (
178           _type,
179           fieldsValue && fieldsValue.length ? (", " ~ fieldsValue.join(",\r\n")) : ""
180         );
181       }
182     }
183   }
184 }