@@ -37,35 +37,64 @@ pub async fn generate_enum(
3737 let name: String = pascal_case ( & en. name ) ;
3838
3939 // Some enums are shared between all schemas
40+ // Hasura suffixes 'Enum' to the end of custom enums created
41+ // via a table with 'value' and 'comment' columns.
4042 if global_enum_suffixes
4143 . iter ( )
4244 . any ( |suffix| name. ends_with ( suffix) )
4345 {
44- let e = Enum :: new ( & name) . with_values ( & values) . to_string ( ) ;
45-
46- let instances = enum_instances ( & name, & values, & original_values) ;
47- let package_name = pascal_case ( & workspace_config. shared_graphql_enums_lib ) ;
48- let module_name = format ! ( "{package_name}.{name}" ) ;
49- imports. push ( PurescriptImport :: new ( & module_name, "oa-gql-enums" ) . add_specified ( & name) ) ;
50-
51- let lib_path = format ! (
52- "{}{}" ,
53- & workspace_config. shared_graphql_enums_dir, & workspace_config. shared_graphql_enums_lib
54- ) ;
55- write (
56- & format ! ( "{lib_path}/src/{package_name}/{name}.purs" ) ,
57- & format ! (
58- "module {module_name} ({name}(..)) where\n \n {MODULE_IMPORTS}\n \n {e}{instances}"
59- ) ,
60- ) ;
61- write ( & format ! ( "{lib_path}/spago.yaml" ) , & enums_spago_yaml ( ) ) ;
62- None
46+ if use_variant ( & name, & workspace_config) {
47+ let lib_path = format ! (
48+ "{}{}" ,
49+ & workspace_config. shared_graphql_enums_dir,
50+ & workspace_config. shared_graphql_enums_lib
51+ ) ;
52+ let package_name = pascal_case ( & workspace_config. shared_graphql_enums_lib ) ;
53+
54+ if let Some ( variant) = variant_mod ( & name, & original_values) {
55+ write (
56+ & format ! ( "{lib_path}/src/{package_name}/{name}.purs" ) ,
57+ & variant,
58+ ) ;
59+ write ( & format ! ( "{lib_path}/spago.yaml" ) , & enums_spago_yaml ( ) ) ;
60+ }
61+
62+ None
63+ } else {
64+ let e = Enum :: new ( & name) . with_values ( & values) . to_string ( ) ;
65+
66+ let instances = enum_instances ( & name, & values, & original_values) ;
67+ let package_name = pascal_case ( & workspace_config. shared_graphql_enums_lib ) ;
68+ let module_name = format ! ( "{package_name}.{name}" ) ;
69+ imports. push ( PurescriptImport :: new ( & module_name, "oa-gql-enums" ) . add_specified ( & name) ) ;
70+
71+ let lib_path = format ! (
72+ "{}{}" ,
73+ & workspace_config. shared_graphql_enums_dir,
74+ & workspace_config. shared_graphql_enums_lib
75+ ) ;
76+ write (
77+ & format ! ( "{lib_path}/src/{package_name}/{name}.purs" ) ,
78+ & format ! (
79+ "module {module_name} ({name}(..)) where\n \n {MODULE_IMPORTS}\n \n {e}{instances}"
80+ ) ,
81+ ) ;
82+ write ( & format ! ( "{lib_path}/spago.yaml" ) , & enums_spago_yaml ( ) ) ;
83+ None
84+ }
6385 // Otherwise write schema-specific variant enums
6486 } else {
6587 Some ( Variant :: new ( & name) . with_values ( & original_values) )
6688 }
6789}
6890
91+ fn use_variant ( name : & str , workspace_config : & WorkspaceConfig ) -> bool {
92+ workspace_config
93+ . variant_enums
94+ . iter ( )
95+ . any ( |suffix| name. ends_with ( suffix) )
96+ }
97+
6998fn first_upper ( s : & str ) -> String {
7099 let mut c = s. chars ( ) ;
71100 match c. next ( ) {
@@ -185,6 +214,173 @@ fn enum_instances(name: &str, values: &Vec<String>, original_values: &Vec<String
185214 instances
186215}
187216
217+ fn variant_mod ( name : & str , original_values : & Vec < String > ) -> Option < String > {
218+ if original_values. len ( ) == 0 {
219+ return None ;
220+ }
221+
222+ let values: Vec < String > = original_values. iter ( ) . map ( |v| v. to_lowercase ( ) ) . collect ( ) ;
223+
224+ let mut instances = String :: new ( ) ;
225+ let first_value = & values[ 0 ] ;
226+ let last_value = values
227+ . last ( )
228+ . expect ( "Enums should have at least one value in order to get last." ) ;
229+
230+ let mut variant = String :: new ( ) ;
231+ let mut variant_fns = String :: new ( ) ;
232+
233+ // Define the type
234+ variant. push_str ( & format ! (
235+ r#"
236+ type {name} = Variant"#
237+ ) ) ;
238+
239+ for value in & values {
240+ let variant_name = value. to_lowercase ( ) ;
241+ let variant_member = if value == first_value {
242+ format ! ( " ( {variant_name}\n " )
243+ } else {
244+ format ! ( " , {variant_name}\n " )
245+ } ;
246+
247+ // Add the variant member to the row
248+ variant. push_str ( & variant_member) ;
249+
250+ // Define the variant fn for easy calling
251+ variant_fns. push_str ( & to_variant ( name, & variant_name) ) ;
252+ }
253+
254+ // Add the variant type closing bracket
255+ variant. push_str ( "\n )" ) ;
256+
257+ // instances:
258+
259+ // Next values in the enum
260+ let succ_values = values
261+ . iter ( )
262+ . enumerate ( )
263+ . map ( |( i, v) | {
264+ if i == values. len ( ) - 1 {
265+ format ! ( "{} -> Nothing" , v)
266+ } else {
267+ format ! ( "{} -> Just {}" , v, values[ i + 1 ] )
268+ }
269+ } )
270+ . collect :: < Vec < String > > ( )
271+ . join ( "\n " ) ;
272+
273+ // Previous values in the enum
274+ let pred_values = values
275+ . iter ( )
276+ . enumerate ( )
277+ . map ( |( i, v) | {
278+ if i == 0 {
279+ format ! ( "{} -> Nothing" , v)
280+ } else {
281+ format ! ( "{} -> Just {}" , v, values[ i - 1 ] )
282+ }
283+ } )
284+ . collect :: < Vec < String > > ( )
285+ . join ( "\n " ) ;
286+
287+ // Cardinality of the enum
288+ let cardinality = values. len ( ) ;
289+
290+ // Convert an enum index to the corresponding value
291+ let to_enum = values
292+ . iter ( )
293+ . enumerate ( )
294+ . map ( |( i, v) | format ! ( "{} -> Just {}" , i, v) )
295+ . collect :: < Vec < String > > ( )
296+ . join ( "\n " ) ;
297+
298+ // Convert an enum value to the corresponding index
299+ let from_enum = values
300+ . iter ( )
301+ . enumerate ( )
302+ . map ( |( i, v) | format ! ( "{} -> {}" , v, i) )
303+ . collect :: < Vec < String > > ( )
304+ . join ( "\n " ) ;
305+
306+ let decode_json = values
307+ . iter ( )
308+ . zip ( original_values. iter ( ) )
309+ . map ( |( v, original) | format ! ( r#""{original}" -> pure {v}"# ) )
310+ . collect :: < Vec < String > > ( )
311+ . join ( "\n " ) ;
312+
313+ let encode_json = values
314+ . iter ( )
315+ . zip ( original_values. iter ( ) )
316+ . map ( |( v, original) | format ! ( r#"on (Proxy @"{v}") (\_ -> show {original})"# ) )
317+ . collect :: < Vec < String > > ( )
318+ . join ( "\n " ) ;
319+
320+ instances. push_str ( & format ! (
321+ r#"
322+
323+ instance DecodeJson {name} where
324+ decodeJson = decodeJson >=> case _ of
325+ {decode_json}
326+ s -> Left $ TypeMismatch $ \"Not a {name}: \" <> s
327+
328+ instance EncodeJson {name} where
329+ encodeJson = case_
330+ {encode_json}
331+
332+ instance MakeFixture {name} where mkFixture = {first_value}
333+
334+ instance Show {name} where
335+ show a = unvariant q.data_type # \(Unvariant f) -> f \p _ -> reflectSymbol p
336+
337+ instance Eq {name} where
338+ eq = eq `on` show
339+
340+ instance Ord {name} where
341+ compare = compare `on` show
342+
343+ instance GqlArgString {name} where
344+ toGqlArgStringImpl = show
345+
346+ instance DecodeHasura {name} where
347+ decodeHasura = decodeJson
348+
349+ instance EncodeHasura {name} where
350+ encodeHasura = encodeJson
351+
352+ instance Enum {name} where
353+ succ a = case a of
354+ {succ_values}
355+ pred a = case a of
356+ {pred_values}
357+
358+ instance Bounded {name} where
359+ top = {last_value}
360+ bottom = {first_value}
361+
362+ instance BoundedEnum {name} where
363+ cardinality = Cardinality {cardinality}
364+ toEnum a = case a of
365+ {to_enum}
366+ _ -> Nothing
367+ fromEnum a = case a of
368+ {from_enum}
369+ "#
370+ ) ) ;
371+
372+ Some ( format ! ( "module {name} where\n \n {VARIANT_MODULE_IMPORTS}\n \n {variant}\n \n {variant_fns}\n \n {instances}" ) )
373+ }
374+
375+ fn to_variant ( type_name : & str , name : & str ) -> String {
376+ format ! (
377+ r#"
378+ var :: {type_name}
379+ var = inj (Proxy @"{name}") unit
380+ "#
381+ )
382+ }
383+
188384fn enums_spago_yaml ( ) -> String {
189385 r#"package:
190386 name: oa-gql-enums
@@ -201,6 +397,7 @@ fn enums_spago_yaml() -> String {
201397 - prelude
202398 - simple-json
203399 - transformers
400+ - variant
204401 - oa-make-fixture
205402 - oa-encode-decode
206403"#
@@ -227,3 +424,9 @@ import Data.Bifunctor (lmap)
227424import Foreign.Class as FC
228425import Class.EncodeOa (class EncodeOa)
229426import Class.DecodeOa (class DecodeOa)"# ;
427+
428+ const VARIANT_MODULE_IMPORTS : & str = r#"import Prelude
429+
430+ import Data.Variant (Unvariant(..), Variant, inj, unvariant)
431+ import Data.Enum (class Enum, class BoundedEnum, Cardinality(..))
432+ import Proxy (Proxy(..))"# ;
0 commit comments