enm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,543 @@
1
+ use magnus::{
2
+ block::Yield, function, method, prelude::*, Error, ExceptionClass, RModule, Ruby, TryConvert,
3
+ Value,
4
+ };
5
+
6
+ use pgenum_parser::{Database, EnumType, PGEnumError, RON_VERSION};
7
+
8
+ /// Convert an `anyhow::Error` into a `magnus::Error` with the appropriate
9
+ /// Ruby exception class based on the `PGEnumError` variant.
10
+ fn to_ruby_err(err: anyhow::Error) -> Error {
11
+ let ruby = Ruby::get().expect("called outside Ruby");
12
+ let module: RModule = ruby.class_object().const_get("ENM").expect("ENM module not defined");
13
+
14
+ let (class_name, message) = match err.downcast_ref::<PGEnumError>() {
15
+ Some(PGEnumError::DuplicatedType { .. }) => ("DuplicatedTypeError", err.to_string()),
16
+ Some(PGEnumError::DuplicatedValue { .. }) => ("DuplicatedValueError", err.to_string()),
17
+ Some(PGEnumError::EnumConflict { .. }) => ("EnumConflictError", err.to_string()),
18
+ Some(PGEnumError::EnumNotFound { .. }) => ("EnumNotFoundError", err.to_string()),
19
+ Some(PGEnumError::InvalidRepresentation(_)) => ("SerializationError", err.to_string()),
20
+ Some(PGEnumError::IOError { .. }) => ("IOError", err.to_string()),
21
+ Some(PGEnumError::SQLParseError(_)) => ("ParseError", err.to_string()),
22
+ Some(PGEnumError::UnknownVersion(_)) => ("UnknownVersionError", err.to_string()),
23
+ None => ("Error", err.to_string()),
24
+ };
25
+
26
+ let exception_class: ExceptionClass = module
27
+ .const_get(class_name)
28
+ .expect("exception class not defined");
29
+
30
+ Error::new(exception_class, message)
31
+ }
32
+
33
+ /// A wrapper around `pgenum_parser::Database`. It provides
34
+ /// ruby bindings to the underlying Rust implementation.
35
+ #[magnus::wrap(class = "ENM::Database", free_immediately, size)]
36
+ struct DatabaseWrapper {
37
+ inner: Database,
38
+ }
39
+
40
+ impl DatabaseWrapper {
41
+ fn new(inner: Database) -> Self {
42
+ Self { inner }
43
+ }
44
+
45
+ fn empty(&self) -> bool {
46
+ self.inner.enums().is_empty()
47
+ }
48
+
49
+ fn from_ron(version: u32, ron: String) -> Result<Self, Error> {
50
+ let inner = Database::from_ron(version, &ron).map_err(to_ruby_err)?;
51
+
52
+ Ok(Self::new(inner))
53
+ }
54
+
55
+ fn value_to_string(value: Value) -> Result<String, Error> {
56
+ String::try_convert(value).or_else(|_| value.funcall("to_s", ()))
57
+ }
58
+
59
+ fn from_ron_file(version: u32, path: Value) -> Result<Self, Error> {
60
+ let path = Self::value_to_string(path)?;
61
+ let inner = Database::from_ron_file(version, &path).map_err(to_ruby_err)?;
62
+ Ok(Self::new(inner))
63
+ }
64
+
65
+ fn from_sql(name: String, sql: String) -> Result<Self, Error> {
66
+ let inner = Database::from_sql(&name, &sql).map_err(to_ruby_err)?;
67
+
68
+ Ok(Self::new(inner))
69
+ }
70
+
71
+ fn from_sql_file(name: String, path: Value) -> Result<Self, Error> {
72
+ let path = Self::value_to_string(path)?;
73
+ let inner = Database::from_sql_file(&name, &path).map_err(to_ruby_err)?;
74
+
75
+ Ok(Self::new(inner))
76
+ }
77
+
78
+ fn get(&self, name: String, schema: Option<String>) -> Result<Enum, Error> {
79
+ let enum_type = self
80
+ .inner
81
+ .get(schema.as_deref(), &name)
82
+ .map_err(to_ruby_err)?;
83
+
84
+ Ok(Enum {
85
+ inner: enum_type.clone(),
86
+ })
87
+ }
88
+
89
+ fn lazy_get(&self, key: Value) -> Result<Enum, Error> {
90
+ let s: String = String::try_convert(key)
91
+ .or_else(|_| key.funcall("to_s", ()))?;
92
+
93
+ if let Some((schema, name)) = s.split_once('.') {
94
+ self.get(name.to_string(), Some(schema.to_string()))
95
+ } else {
96
+ self.get(s, None)
97
+ }
98
+ }
99
+
100
+ fn name(&self) -> String {
101
+ self.inner.name().to_string()
102
+ }
103
+
104
+ fn to_ron(&self) -> Result<String, Error> {
105
+ self.inner.to_ron().map_err(to_ruby_err)
106
+ }
107
+
108
+ fn to_ron_file(&self, path: Value) -> Result<(), Error> {
109
+ let path = Self::value_to_string(path)?;
110
+ self.inner.to_ron_file(&path).map_err(to_ruby_err)
111
+ }
112
+ }
113
+
114
+ #[magnus::wrap(class = "ENM::Enum", free_immediately, size)]
115
+ struct Enum {
116
+ inner: EnumType,
117
+ }
118
+
119
+ impl Enum {
120
+ fn comment(&self) -> Option<String> {
121
+ self.inner.comment().map(|s| s.to_string())
122
+ }
123
+
124
+ fn digest(&self) -> String {
125
+ self.inner.digest().to_string()
126
+ }
127
+
128
+ fn each(ruby: &Ruby, rb_self: Value) -> Yield<std::vec::IntoIter<String>> {
129
+ if ruby.block_given() {
130
+ let wrapper = <&Enum>::try_convert(rb_self).expect("self is Enum");
131
+
132
+ Yield::Iter(wrapper.into_iter().cloned().collect::<Vec<_>>().into_iter())
133
+ } else {
134
+ Yield::Enumerator(rb_self.enumeratorize("each", ()))
135
+ }
136
+ }
137
+
138
+ fn name(&self) -> String {
139
+ self.inner.name().to_string()
140
+ }
141
+
142
+ fn schema(&self) -> String {
143
+ self.inner.schema().to_string()
144
+ }
145
+
146
+ fn values(&self) -> Vec<String> {
147
+ self.inner.values().to_vec()
148
+ }
149
+ }
150
+
151
+ impl<'a> IntoIterator for &'a Enum {
152
+ type Item = &'a String;
153
+ type IntoIter = std::slice::Iter<'a, String>;
154
+
155
+ fn into_iter(self) -> Self::IntoIter {
156
+ (&self.inner).into_iter()
157
+ }
158
+ }
159
+
160
+ #[magnus::init]
161
+ fn init(ruby: &Ruby) -> Result<(), Error> {
162
+ let module: RModule = ruby.define_module("ENM")?;
163
+
164
+ let error_class = module.define_error("Error", ruby.exception_standard_error())?;
165
+
166
+ module.define_error("DuplicatedTypeError", error_class)?;
167
+ module.define_error("DuplicatedValueError", error_class)?;
168
+ module.define_error("EnumConflictError", error_class)?;
169
+ module.define_error("EnumNotFoundError", error_class)?;
170
+ module.define_error("IOError", error_class)?;
171
+ module.define_error("ParseError", error_class)?;
172
+ module.define_error("SerializationError", error_class)?;
173
+ module.define_error("UnknownVersionError", error_class)?;
174
+
175
+ // Database class
176
+ let database_klass = module.define_class("Database", ruby.class_object())?;
177
+
178
+ database_klass.define_singleton_method("from_ron", function!(DatabaseWrapper::from_ron, 2))?;
179
+ database_klass.define_singleton_method("from_ron_file", function!(DatabaseWrapper::from_ron_file, 2))?;
180
+ database_klass.define_singleton_method("from_sql", function!(DatabaseWrapper::from_sql, 2))?;
181
+ database_klass.define_singleton_method("from_sql_file", function!(DatabaseWrapper::from_sql_file, 2))?;
182
+ database_klass.define_method("[]", method!(DatabaseWrapper::lazy_get, 1))?;
183
+ database_klass.define_method("name", method!(DatabaseWrapper::name, 0))?;
184
+ database_klass.define_method("empty?", method!(DatabaseWrapper::empty, 0))?;
185
+ database_klass.define_method("to_ron", method!(DatabaseWrapper::to_ron, 0))?;
186
+ database_klass.define_method("to_ron_file", method!(DatabaseWrapper::to_ron_file, 1))?;
187
+ database_klass.define_private_method("_get", method!(DatabaseWrapper::get, 2))?;
188
+
189
+ // Enum class with Enumerable
190
+ let enum_klass = module.define_class("Enum", ruby.class_object())?;
191
+ let enumerable: RModule = ruby.class_object().const_get("Enumerable")?;
192
+
193
+ enum_klass.include_module(enumerable)?;
194
+
195
+ enum_klass.define_method("comment", method!(Enum::comment, 0))?;
196
+ enum_klass.define_method("digest", method!(Enum::digest, 0))?;
197
+ enum_klass.define_method("each", method!(Enum::each, 0))?;
198
+ enum_klass.define_method("name", method!(Enum::name, 0))?;
199
+ enum_klass.define_method("schema", method!(Enum::schema, 0))?;
200
+ enum_klass.define_method("values", method!(Enum::values, 0))?;
201
+
202
+ if module.const_get::<_, Value>("RON_VERSION").is_err() {
203
+ module.const_set("RON_VERSION", RON_VERSION)?;
204
+ }
205
+
206
+ Ok(())
207
+ }
208
+
209
+ #[cfg(test)]
210
+ mod tests {
211
+ use super::*;
212
+ use magnus::eval;
213
+ use rb_sys_test_helpers::ruby_test;
214
+
215
+ const VALID_SQL: &str = r#"
216
+ CREATE TYPE public.access_management AS ENUM ('global', 'contextual', 'forbidden');
217
+ COMMENT ON TYPE public.access_management IS 'Represents access management levels.';
218
+ CREATE TYPE public.analytics_context AS ENUM ('admin', 'frontend');
219
+ CREATE TYPE asset_kind AS ENUM ('unknown', 'image', 'video');
220
+ "#;
221
+
222
+ fn setup(ruby: &Ruby) {
223
+ init(ruby).expect("init failed");
224
+ }
225
+
226
+ fn ruby_string_value(ruby: &Ruby, s: &str) -> Value {
227
+ ruby.str_new(s).as_value()
228
+ }
229
+
230
+ #[ruby_test]
231
+ fn test_init_defines_enm_module() {
232
+ let ruby = Ruby::get().unwrap();
233
+ setup(&ruby);
234
+
235
+ let result: bool = eval("defined?(ENM) == 'constant'").unwrap();
236
+
237
+ assert!(result);
238
+ }
239
+
240
+ #[ruby_test]
241
+ fn test_init_defines_error_classes() {
242
+ let ruby = Ruby::get().unwrap();
243
+ setup(&ruby);
244
+
245
+ for class_name in &[
246
+ "ENM::Error",
247
+ "ENM::DuplicatedTypeError",
248
+ "ENM::DuplicatedValueError",
249
+ "ENM::EnumConflictError",
250
+ "ENM::EnumNotFoundError",
251
+ "ENM::IOError",
252
+ "ENM::ParseError",
253
+ "ENM::SerializationError",
254
+ "ENM::UnknownVersionError",
255
+ ] {
256
+ let result: bool = eval(&format!("defined?({class_name}) == 'constant'")).unwrap();
257
+
258
+ assert!(result, "{class_name} should be defined");
259
+ }
260
+ }
261
+
262
+ #[ruby_test]
263
+ fn test_init_sets_ron_version_constant() {
264
+ let ruby = Ruby::get().unwrap();
265
+ setup(&ruby);
266
+
267
+ let version: u32 = eval("ENM::RON_VERSION").unwrap();
268
+ assert_eq!(version, RON_VERSION);
269
+ }
270
+
271
+ #[ruby_test]
272
+ fn test_database_from_sql() {
273
+ let ruby = Ruby::get().unwrap();
274
+ setup(&ruby);
275
+
276
+ let db = DatabaseWrapper::from_sql("test_db".to_string(), VALID_SQL.to_string()).unwrap();
277
+ assert_eq!(db.name(), "test_db");
278
+ assert!(!db.empty());
279
+ }
280
+
281
+ #[ruby_test]
282
+ fn test_database_from_sql_empty() {
283
+ let ruby = Ruby::get().unwrap();
284
+ setup(&ruby);
285
+
286
+ let db = DatabaseWrapper::from_sql(
287
+ "empty_db".to_string(),
288
+ "CREATE TABLE t (id int);".to_string(),
289
+ )
290
+ .unwrap();
291
+ assert!(db.empty());
292
+ }
293
+
294
+ #[ruby_test]
295
+ fn test_database_from_sql_invalid() {
296
+ let ruby = Ruby::get().unwrap();
297
+
298
+ setup(&ruby);
299
+
300
+ let result = DatabaseWrapper::from_sql("bad".to_string(), "not valid sql".to_string());
301
+ assert!(result.is_err());
302
+ }
303
+
304
+ #[ruby_test]
305
+ fn test_database_ron_round_trip() {
306
+ let ruby = Ruby::get().unwrap();
307
+
308
+ setup(&ruby);
309
+
310
+ let db = DatabaseWrapper::from_sql("rt".to_string(), VALID_SQL.to_string()).unwrap();
311
+ let ron = db.to_ron().unwrap();
312
+
313
+ let db2 = DatabaseWrapper::from_ron(RON_VERSION, ron).unwrap();
314
+
315
+ assert_eq!(db2.name(), "rt");
316
+ assert!(!db2.empty());
317
+ }
318
+
319
+ #[ruby_test]
320
+ fn test_database_get_enum_by_name_and_schema() {
321
+ let ruby = Ruby::get().unwrap();
322
+ setup(&ruby);
323
+
324
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
325
+ let e = db
326
+ .get("access_management".to_string(), Some("public".to_string()))
327
+ .unwrap();
328
+
329
+ assert_eq!(e.name(), "access_management");
330
+ assert_eq!(e.schema(), "public");
331
+ }
332
+
333
+ #[ruby_test]
334
+ fn test_database_get_enum_not_found() {
335
+ let ruby = Ruby::get().unwrap();
336
+
337
+ setup(&ruby);
338
+
339
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
340
+ let result = db.get("nonexistent".to_string(), None);
341
+
342
+ assert!(result.is_err());
343
+ }
344
+
345
+ #[ruby_test]
346
+ fn test_lazy_get_with_symbol() {
347
+ let ruby = Ruby::get().unwrap();
348
+
349
+ setup(&ruby);
350
+
351
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
352
+ let key: Value = eval(":access_management").unwrap();
353
+ let e = db.lazy_get(key).unwrap();
354
+
355
+ assert_eq!(e.name(), "access_management");
356
+ }
357
+
358
+ #[ruby_test]
359
+ fn test_lazy_get_with_schema_dot_notation() {
360
+ let ruby = Ruby::get().unwrap();
361
+
362
+ setup(&ruby);
363
+
364
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
365
+ let key: Value = eval("'public.access_management'").unwrap();
366
+ let e = db.lazy_get(key).unwrap();
367
+
368
+ assert_eq!(e.name(), "access_management");
369
+ assert_eq!(e.schema(), "public");
370
+ }
371
+
372
+ #[ruby_test]
373
+ fn test_lazy_get_with_string_no_schema() {
374
+ let ruby = Ruby::get().unwrap();
375
+
376
+ setup(&ruby);
377
+
378
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
379
+ let key: Value = eval("'access_management'").unwrap();
380
+ let e = db.lazy_get(key).unwrap();
381
+
382
+ assert_eq!(e.name(), "access_management");
383
+ }
384
+
385
+ #[ruby_test]
386
+ fn test_enum_comment() {
387
+ let ruby = Ruby::get().unwrap();
388
+
389
+ setup(&ruby);
390
+
391
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
392
+
393
+ let with_comment = db
394
+ .get("access_management".to_string(), Some("public".to_string()))
395
+ .unwrap();
396
+ assert_eq!(
397
+ with_comment.comment(),
398
+ Some("Represents access management levels.".to_string())
399
+ );
400
+
401
+ let without_comment = db
402
+ .get("analytics_context".to_string(), Some("public".to_string()))
403
+ .unwrap();
404
+
405
+ assert_eq!(without_comment.comment(), None);
406
+ }
407
+
408
+ #[ruby_test]
409
+ fn test_enum_each_with_block() {
410
+ let ruby = Ruby::get().unwrap();
411
+
412
+ setup(&ruby);
413
+
414
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
415
+ let e = db
416
+ .get("analytics_context".to_string(), Some("public".to_string()))
417
+ .unwrap();
418
+
419
+ let values: Vec<String> = e.values();
420
+
421
+ assert_eq!(values, vec!["admin", "frontend"]);
422
+ }
423
+
424
+ #[ruby_test]
425
+ fn test_database_from_ron_rejects_bad_version() {
426
+ let ruby = Ruby::get().unwrap();
427
+
428
+ setup(&ruby);
429
+
430
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
431
+ let ron = db.to_ron().unwrap();
432
+
433
+ let result = DatabaseWrapper::from_ron(999, ron);
434
+
435
+ assert!(result.is_err());
436
+ }
437
+
438
+ #[ruby_test]
439
+ fn test_database_from_sql_file() {
440
+ let ruby = Ruby::get().unwrap();
441
+
442
+ setup(&ruby);
443
+
444
+ let path = std::env::temp_dir().join("enm_test_from_sql_file.sql");
445
+ let path_str = path.to_str().unwrap();
446
+
447
+ std::fs::write(&path, VALID_SQL).unwrap();
448
+
449
+ let db = DatabaseWrapper::from_sql_file(
450
+ "test_db".to_string(),
451
+ ruby_string_value(&ruby, path_str),
452
+ )
453
+ .unwrap();
454
+
455
+ assert_eq!(db.name(), "test_db");
456
+ assert!(!db.empty());
457
+ }
458
+
459
+ #[ruby_test]
460
+ fn test_database_from_sql_file_not_found() {
461
+ let ruby = Ruby::get().unwrap();
462
+
463
+ setup(&ruby);
464
+
465
+ let result = DatabaseWrapper::from_sql_file(
466
+ "test".to_string(),
467
+ ruby_string_value(&ruby, "/nonexistent/path/enm_test.sql"),
468
+ );
469
+
470
+ assert!(result.is_err());
471
+ }
472
+
473
+ #[ruby_test]
474
+ fn test_database_from_ron_file() {
475
+ let ruby = Ruby::get().unwrap();
476
+
477
+ setup(&ruby);
478
+
479
+ let db = DatabaseWrapper::from_sql("rt".to_string(), VALID_SQL.to_string()).unwrap();
480
+ let ron = db.to_ron().unwrap();
481
+ let path = std::env::temp_dir().join("enm_test_from_ron_file.ron");
482
+ let path_str = path.to_str().unwrap();
483
+
484
+ std::fs::write(&path, &ron).unwrap();
485
+
486
+ let db2 = DatabaseWrapper::from_ron_file(RON_VERSION, ruby_string_value(&ruby, path_str))
487
+ .unwrap();
488
+
489
+ assert_eq!(db2.name(), "rt");
490
+ assert!(!db2.empty());
491
+ }
492
+
493
+ #[ruby_test]
494
+ fn test_database_from_ron_file_not_found() {
495
+ let ruby = Ruby::get().unwrap();
496
+
497
+ setup(&ruby);
498
+
499
+ let result = DatabaseWrapper::from_ron_file(
500
+ RON_VERSION,
501
+ ruby_string_value(&ruby, "/nonexistent/path/enm_test.ron"),
502
+ );
503
+
504
+ assert!(result.is_err());
505
+ }
506
+
507
+ #[ruby_test]
508
+ fn test_database_to_ron_file() {
509
+ let ruby = Ruby::get().unwrap();
510
+
511
+ setup(&ruby);
512
+
513
+ let db = DatabaseWrapper::from_sql("test".to_string(), VALID_SQL.to_string()).unwrap();
514
+ let path = std::env::temp_dir().join("enm_test_to_ron_file.ron");
515
+ let path_str = path.to_str().unwrap();
516
+
517
+ db.to_ron_file(ruby_string_value(&ruby, path_str)).unwrap();
518
+
519
+ let db2 = DatabaseWrapper::from_ron_file(RON_VERSION, ruby_string_value(&ruby, path_str))
520
+ .unwrap();
521
+
522
+ assert_eq!(db2.name(), "test");
523
+ assert!(!db2.empty());
524
+ }
525
+
526
+ #[ruby_test]
527
+ fn test_database_to_ron_file_round_trip() {
528
+ let ruby = Ruby::get().unwrap();
529
+
530
+ setup(&ruby);
531
+
532
+ let db = DatabaseWrapper::from_sql("rt".to_string(), VALID_SQL.to_string()).unwrap();
533
+ let expected_ron = db.to_ron().unwrap();
534
+ let path = std::env::temp_dir().join("enm_test_to_ron_file_rt.ron");
535
+ let path_str = path.to_str().unwrap();
536
+
537
+ db.to_ron_file(ruby_string_value(&ruby, path_str)).unwrap();
538
+ let db2 = DatabaseWrapper::from_ron_file(RON_VERSION, ruby_string_value(&ruby, path_str))
539
+ .unwrap();
540
+
541
+ assert_eq!(db2.to_ron().unwrap(), expected_ron);
542
+ }
543
+ }
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ENM
4
+ class Database
5
+ # @param [String, Symbol] name
6
+ # @param [String, Symbol, nil] schema
7
+ # @return [ENM::Enum, nil]
8
+ def get(name, schema: nil)
9
+ _get(name, schema)
10
+ end
11
+ end
12
+ end
data/lib/enm/enum.rb ADDED
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ENM
4
+ # An enum type from the database.
5
+ class Enum
6
+ extend Dry::Core::Cache
7
+
8
+ # @!parse [ruby]
9
+ # include Enumerable
10
+
11
+ # @!attribute [r] comment
12
+ # @return [String, nil] comment associated with the enum type
13
+
14
+ # @!attribute [r] digest
15
+ # @api private
16
+ # @return [String] a digest representing the enum values, for caching purposes
17
+
18
+ # @!attribute [r] name
19
+ # @return [String] the name of the enum type
20
+
21
+ # @!attribute [r] schema
22
+ # @return [String] the schema of the enum type
23
+
24
+ # @see ENM::Enum::DryTypeBuilder
25
+ # @param [String, Symbol, #to_s, #to_sym, nil] default whether to use a
26
+ # certain value as dry-type's default, since it must be defined _before_
27
+ # the enum call.
28
+ # @param [:string, :symbol] mode the underlying type of the enum values.
29
+ # @return [Dry::Types::Type]
30
+ def dry(default: nil, mode: :string)
31
+ fetch_or_store([digest, default, mode]) do
32
+ DryTypeBuilder.new(self, mode:, default:).()
33
+ end
34
+ end
35
+
36
+ # Builds a `Dry::Types::Type` for an {ENM::Enum}.
37
+ #
38
+ # @api private
39
+ # @see ENM::Enum#dry
40
+ class DryTypeBuilder
41
+ include Dry::Initializer[undefined: false].define -> do
42
+ param :enum, ENM::Types::Enum
43
+
44
+ option :mode, ENM::Types::DryMode
45
+
46
+ option :default, ENM::Types::DefaultValue.optional, as: :default_value, optional: true
47
+ end
48
+
49
+ # @return [String, Symbol, nil]
50
+ attr_reader :default
51
+
52
+ def initialize(...)
53
+ super
54
+
55
+ @default = derive_real_default
56
+ end
57
+
58
+ # @return [Dry::Types::Type]
59
+ def call
60
+ base = build_base
61
+
62
+ type = default? ? base.default(default.freeze) : base
63
+
64
+ enum_values = symbol? ? enum.map(&:to_sym) : enum.to_a
65
+
66
+ type.enum(*enum_values)
67
+ end
68
+
69
+ def default? = !default_value.nil?
70
+
71
+ def symbol? = mode == :symbol
72
+
73
+ private
74
+
75
+ # @return [Dry::Types::Type]
76
+ def build_base = symbol? ? ENM::Types::Coercible::Symbol : ENM::Types::Coercible::String
77
+
78
+ # @return [String, Symbol, nil]
79
+ def derive_real_default
80
+ return unless default?
81
+
82
+ unless enum.include?(default_value)
83
+ raise ENM::InvalidDefaultError, "Default #{default_value.inspect} not present in #{enum.inspect}"
84
+ end
85
+
86
+ symbol? ? default_value.to_sym : default_value
87
+ end
88
+ end
89
+ end
90
+ end