Current section
Files
Jump to
Current section
Files
lib/charts_ex.ex
defmodule ChartsEx do
@moduledoc """
SVG chart rendering for Elixir powered by charts-rs.
## Usage
Three input modes are supported:
### Builder structs (recommended)
alias ChartsEx.BarChart
BarChart.new()
|> BarChart.title("Downloads")
|> BarChart.x_axis(["Mon", "Tue", "Wed"])
|> BarChart.add_series("Hex", [120.0, 200.0, 150.0])
|> ChartsEx.render()
### Atom-key maps
ChartsEx.render(%{
type: :bar,
title_text: "Downloads",
series_list: [%{name: "Hex", data: [120.0, 200.0]}],
x_axis_data: ["Mon", "Tue"]
})
### Raw JSON
ChartsEx.render(~s({"type":"bar","title_text":"Downloads","x_axis_data":["Mon","Tue"],"series_list":[{"name":"Hex","data":[120.0,200.0]}]}))
"""
@type_map %{
bar: "bar",
horizontal_bar: "horizontal_bar",
line: "line",
pie: "pie",
radar: "radar",
scatter: "scatter",
candlestick: "candlestick",
heatmap: "heatmap",
table: "table",
multi_chart: "multi_chart"
}
@doc """
Renders a chart to SVG.
Accepts a chart struct, atom-key map with `:type`, or raw JSON string.
Returns `{:ok, svg_string}` or `{:error, message}`.
"""
@spec render(struct() | map() | String.t()) :: {:ok, String.t()} | {:error, String.t()}
def render(%mod{} = chart) do
if function_exported?(mod, :to_json, 1) do
chart |> mod.to_json() |> ChartsEx.Native.render()
else
{:error, "#{inspect(mod)} does not implement ChartsEx.Chart"}
end
end
def render(map) when is_map(map) do
map
|> stringify_map()
|> Jason.encode!()
|> ChartsEx.Native.render()
end
def render(json) when is_binary(json) do
ChartsEx.Native.render(json)
end
@doc """
Like `render/1` but raises on error.
"""
@spec render!(struct() | map() | String.t()) :: String.t()
def render!(input) do
case render(input) do
{:ok, svg} -> svg
{:error, msg} -> raise RuntimeError, "ChartsEx render error: #{msg}"
end
end
defp stringify_map(map) when is_map(map) do
Map.new(map, fn
{:type, v} when is_atom(v) -> {"type", Map.get(@type_map, v, Atom.to_string(v))}
{k, v} when is_atom(k) -> {Atom.to_string(k), stringify_value(v)}
{k, v} -> {k, stringify_value(v)}
end)
end
defp stringify_value(v) when is_map(v), do: stringify_map(v)
defp stringify_value(v) when is_list(v), do: Enum.map(v, &stringify_value/1)
defp stringify_value(v) when is_atom(v) and not is_nil(v) and not is_boolean(v),
do: Atom.to_string(v)
defp stringify_value(v), do: v
end