Skip to content

Commit 2c997ef

Browse files
committed
Allow optional Variant enums
1 parent bf45724 commit 2c997ef

File tree

4 files changed

+237
-23
lines changed

4 files changed

+237
-23
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rust-purs-gql"
3-
version = "0.1.11"
3+
version = "0.1.12"
44
edition = "2021"
55
default-run = "pursgql"
66
repository = "https://github.com/OxfordAbstracts/purescript-graphql-schema-gen"
@@ -43,7 +43,7 @@ ci = "github"
4343
# The installers to generate for each app
4444
installers = ["npm"]
4545
# Target platforms to build apps for (Rust target-triple syntax)
46-
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"]
46+
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]
4747
# The archive format to use for windows builds (defaults .zip)
4848
windows-archive = ".tar.gz"
4949
# The archive format to use for non-windows builds (defaults .tar.xz)

src/config/workspace.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub struct WorkspaceConfig {
3838
pub shared_graphql_enums_dir: String,
3939
pub schema_libs_prefix: String,
4040
pub schema_libs_dir: String,
41+
pub variant_enums: Vec<String>,
4142
}
4243

4344
impl WorkspaceConfig {
@@ -50,7 +51,7 @@ impl WorkspaceConfig {
5051
yaml_hash.get(&Yaml::String("shared_graphql_enums_dir".to_string()))?;
5152
let schema_libs_prefix = yaml_hash.get(&Yaml::String("schema_libs_prefix".to_string()))?;
5253
let schema_libs_dir = yaml_hash.get(&Yaml::String("schema_libs_dir".to_string()))?;
53-
54+
let variant_enums = yaml_hash.get(&Yaml::String("variant_enums".to_string()))?;
5455
Some(Self {
5556
postgres_enums_lib: postgres_enums_lib
5657
.as_str()
@@ -76,6 +77,16 @@ impl WorkspaceConfig {
7677
.as_str()
7778
.expect("Workspace yaml should contain schema_libs_dir key.")
7879
.to_string(),
80+
variant_enums: variant_enums
81+
.as_vec()
82+
.unwrap_or(&vec![])
83+
.iter()
84+
.map(|v| {
85+
v.as_str()
86+
.expect("Workspace yaml variant enums should all be strings")
87+
.to_string()
88+
})
89+
.collect(),
7990
})
8091
}
8192
}

src/enums/generate_enum.rs

Lines changed: 222 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
6998
fn 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+
188384
fn 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)
227424
import Foreign.Class as FC
228425
import Class.EncodeOa (class EncodeOa)
229426
import 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

Comments
 (0)