@@ -121,7 +121,7 @@ defmodule Construct.Type do
121121 :error
122122
123123 """
124- @ spec cast ( t , term , options ) :: cast_ret
124+ @ spec cast ( t , term , options ) :: cast_ret | any
125125 when options: [ make_map: boolean ]
126126
127127 def cast ( { :array , type } , term , opts ) when is_list ( term ) do
@@ -152,7 +152,7 @@ defmodule Construct.Type do
152152 @ doc """
153153 Behaves like `cast/3`, but without options provided to nested types.
154154 """
155- @ spec cast ( t , term ) :: cast_ret
155+ @ spec cast ( t , term ) :: cast_ret | any
156156
157157 def cast ( type , term )
158158
@@ -336,10 +336,7 @@ defmodule Construct.Type do
336336 defp cast_naive_datetime ( % { } = map ) do
337337 with { :ok , date } <- cast_date ( map ) ,
338338 { :ok , time } <- cast_time ( map ) do
339- case NaiveDateTime . new ( date , time ) do
340- { :ok , _ } = ok -> ok
341- { :error , _ } -> :error
342- end
339+ NaiveDateTime . new ( date , time )
343340 end
344341 end
345342 defp cast_naive_datetime ( _ ) do
@@ -377,6 +374,172 @@ defmodule Construct.Type do
377374 end
378375 end
379376
377+ ## Typespecs
378+
379+ @ doc """
380+ Returns typespec AST for given type
381+
382+ iex> spec([CommaList, {:array, :integer}]) |> Macro.to_string()
383+ "list(:integer)"
384+
385+ iex> spec({:array, :string}) |> Macro.to_string()
386+ "list(String.t())"
387+
388+ iex> spec({:map, CustomType}) |> Macro.to_string()
389+ "%{optional(term) => CustomType.t()}"
390+
391+ iex> spec(:string) |> Macro.to_string()
392+ "String.t()"
393+
394+ iex> spec(CustomType) |> Macro.to_string()
395+ "CustomType.t()"
396+ """
397+ @ spec spec ( t ) :: Macro . t ( )
398+
399+ def spec ( type ) when is_list ( type ) do
400+ type |> List . last ( ) |> spec ( )
401+ end
402+
403+ def spec ( { :array , type } ) do
404+ quote do
405+ list ( unquote ( spec ( type ) ) )
406+ end
407+ end
408+
409+ def spec ( { :map , type } ) do
410+ quote do
411+ % { optional ( term ) => unquote ( spec ( type ) ) }
412+ end
413+ end
414+
415+ def spec ( :string ) do
416+ quote do
417+ String . t ( )
418+ end
419+ end
420+
421+ def spec ( :decimal ) do
422+ quote do
423+ Decimal . t ( )
424+ end
425+ end
426+
427+ def spec ( :utc_datetime ) do
428+ quote do
429+ DateTime . t ( )
430+ end
431+ end
432+
433+ def spec ( :naive_datetime ) do
434+ quote do
435+ NaiveDateTime . t ( )
436+ end
437+ end
438+
439+ def spec ( :date ) do
440+ quote do
441+ Date . t ( )
442+ end
443+ end
444+
445+ def spec ( :time ) do
446+ quote do
447+ Time . t ( )
448+ end
449+ end
450+
451+ def spec ( type ) when type in @ builtin do
452+ type
453+ end
454+
455+ def spec ( type ) when is_atom ( type ) do
456+ quote do
457+ unquote ( type ) . t ( )
458+ end
459+ end
460+
461+ def spec ( type ) do
462+ type
463+ end
464+
465+ @ doc """
466+ Returns typespec AST for given term
467+
468+ iex> typeof(nil) |> Macro.to_string()
469+ "nil"
470+
471+ iex> typeof(1.42) |> Macro.to_string()
472+ "float()"
473+
474+ iex> typeof("string") |> Macro.to_string()
475+ "String.t()"
476+
477+ iex> typeof(CustomType) |> Macro.to_string()
478+ "CustomType.t()"
479+
480+ iex> typeof(&NaiveDateTime.utc_now/0) |> Macro.to_string()
481+ "NaiveDateTime.t()"
482+ """
483+ @ spec spec ( t ) :: Macro . t ( )
484+
485+ def typeof ( term ) when is_nil ( term ) do
486+ nil
487+ end
488+
489+ def typeof ( term ) when is_integer ( term ) do
490+ { :integer , [ ] , [ ] }
491+ end
492+
493+ def typeof ( term ) when is_float ( term ) do
494+ { :float , [ ] , [ ] }
495+ end
496+
497+ def typeof ( term ) when is_boolean ( term ) do
498+ { :boolean , [ ] , [ ] }
499+ end
500+
501+ def typeof ( term ) when is_binary ( term ) do
502+ quote do
503+ String . t ( )
504+ end
505+ end
506+
507+ def typeof ( term ) when is_pid ( term ) do
508+ { :pid , [ ] , [ ] }
509+ end
510+
511+ def typeof ( term ) when is_reference ( term ) do
512+ { :reference , [ ] , [ ] }
513+ end
514+
515+ def typeof ( % { __struct__: struct } ) when is_atom ( struct ) do
516+ quote do
517+ unquote ( struct ) . t ( )
518+ end
519+ end
520+
521+ def typeof ( term ) when is_map ( term ) do
522+ { :map , [ ] , [ ] }
523+ end
524+
525+ def typeof ( term ) when is_atom ( term ) do
526+ quote do
527+ unquote ( term ) . t ( )
528+ end
529+ end
530+
531+ def typeof ( term ) when is_list ( term ) do
532+ { :list , [ ] , [ ] }
533+ end
534+
535+ def typeof ( term ) when is_function ( term , 0 ) do
536+ term . ( ) |> typeof ( )
537+ end
538+
539+ def typeof ( _ ) do
540+ { :term , [ ] , [ ] }
541+ end
542+
380543 ## Helpers
381544
382545 defp validate_decimal ( { :ok , % { __struct__: Decimal , coef: coef } } ) when coef in [ :inf , :qNaN , :sNaN ] ,
@@ -422,8 +585,6 @@ defmodule Construct.Type do
422585 { :ok , acc }
423586 end
424587
425- defp map ( _ , _ , _ , _ , _ ) , do: :error
426-
427588 defp to_i ( nil ) , do: nil
428589 defp to_i ( int ) when is_integer ( int ) , do: int
429590 defp to_i ( bin ) when is_binary ( bin ) do
0 commit comments