// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Public facade API for parsing and compiling diago sources.
///|
pub(all) enum OutputMode {
Svg
Ascii
Unicode
} derive(Debug, Eq)
///|
pub(all) enum LayoutEngine {
Auto
Dagre
Elk
Railway
} derive(Debug, Eq)
///|
pub struct ParseOptions {
import_resolver : @ir.ImportResolver?
}
///|
pub fn ParseOptions::new() -> ParseOptions {
{ import_resolver: None }
}
///|
pub fn ParseOptions::with_import_resolver(
_self : ParseOptions,
import_resolver : @ir.ImportResolver?,
) -> ParseOptions {
{ import_resolver, }
}
///|
pub struct LayoutPlugin(
(@graph.GraphInput, @engine_api.LayoutConfig, @engine_api.Direction) -> Result[
@graph.GraphInput,
String,
]
)
///|
pub fn LayoutPlugin::new(
f : (@graph.GraphInput, @engine_api.LayoutConfig, @engine_api.Direction) -> Result[
@graph.GraphInput,
String,
],
) -> LayoutPlugin {
LayoutPlugin(f)
}
///|
pub fn LayoutPlugin::run(
self : LayoutPlugin,
graph : @graph.GraphInput,
config : @engine_api.LayoutConfig,
direction : @engine_api.Direction,
) -> Result[@graph.GraphInput, String] {
let LayoutPlugin(f) = self
f(graph, config, direction)
}
///|
pub struct RoutePlugin((@graph.GraphInput) -> Result[@graph.GraphInput, String])
///|
pub fn RoutePlugin::new(
f : (@graph.GraphInput) -> Result[@graph.GraphInput, String],
) -> RoutePlugin {
RoutePlugin(f)
}
///|
pub fn RoutePlugin::run(
self : RoutePlugin,
graph : @graph.GraphInput,
) -> Result[@graph.GraphInput, String] {
let RoutePlugin(f) = self
f(graph)
}
///|
pub struct CompileOptions {
output_mode : OutputMode
layout_engine : LayoutEngine
direction : @engine_api.Direction
target_spec : String?
layout_plugin : LayoutPlugin?
route_plugin : RoutePlugin?
parse_options : ParseOptions
theme_name : String?
dark_theme_id : Int?
sketch : Bool?
pad : Double?
center : Bool?
theme_overrides : @diagram.ThemeOverrides?
dark_theme_overrides : @diagram.ThemeOverrides?
scale : Double?
no_xml_tag : Bool?
salt : String?
omit_version : Bool?
}
///|
pub fn CompileOptions::new() -> CompileOptions {
{
output_mode: Svg,
layout_engine: Auto,
direction: Down,
target_spec: None,
layout_plugin: None,
route_plugin: None,
parse_options: ParseOptions::new(),
theme_name: None,
dark_theme_id: None,
sketch: None,
pad: None,
center: None,
theme_overrides: None,
dark_theme_overrides: None,
scale: None,
no_xml_tag: None,
salt: None,
omit_version: None,
}
}
///|
pub fn CompileOptions::with_output_mode(
self : CompileOptions,
output_mode : OutputMode,
) -> CompileOptions {
{ ..self, output_mode, }
}
///|
pub fn CompileOptions::with_layout_engine(
self : CompileOptions,
layout_engine : LayoutEngine,
) -> CompileOptions {
{ ..self, layout_engine, }
}
///|
pub fn CompileOptions::with_direction(
self : CompileOptions,
direction : @engine_api.Direction,
) -> CompileOptions {
{ ..self, direction, }
}
///|
pub fn CompileOptions::with_target_spec(
self : CompileOptions,
target_spec : String?,
) -> CompileOptions {
{ ..self, target_spec, }
}
///|
pub fn CompileOptions::with_layout_plugin(
self : CompileOptions,
layout_plugin : LayoutPlugin?,
) -> CompileOptions {
{ ..self, layout_plugin, }
}
///|
pub fn CompileOptions::with_route_plugin(
self : CompileOptions,
route_plugin : RoutePlugin?,
) -> CompileOptions {
{ ..self, route_plugin, }
}
///|
pub fn CompileOptions::with_parse_options(
self : CompileOptions,
parse_options : ParseOptions,
) -> CompileOptions {
{ ..self, parse_options, }
}
///|
pub fn CompileOptions::with_theme_name(
self : CompileOptions,
theme_name : String?,
) -> CompileOptions {
{ ..self, theme_name, }
}
///|
pub fn CompileOptions::with_dark_theme_id(
self : CompileOptions,
dark_theme_id : Int?,
) -> CompileOptions {
{ ..self, dark_theme_id, }
}
///|
pub fn CompileOptions::with_sketch(
self : CompileOptions,
sketch : Bool?,
) -> CompileOptions {
{ ..self, sketch, }
}
///|
pub fn CompileOptions::with_pad(
self : CompileOptions,
pad : Double?,
) -> CompileOptions {
{ ..self, pad, }
}
///|
pub fn CompileOptions::with_center(
self : CompileOptions,
center : Bool?,
) -> CompileOptions {
{ ..self, center, }
}
///|
pub fn CompileOptions::with_theme_overrides(
self : CompileOptions,
theme_overrides : @diagram.ThemeOverrides?,
) -> CompileOptions {
{ ..self, theme_overrides, }
}
///|
pub fn CompileOptions::with_dark_theme_overrides(
self : CompileOptions,
dark_theme_overrides : @diagram.ThemeOverrides?,
) -> CompileOptions {
{ ..self, dark_theme_overrides, }
}
///|
pub fn CompileOptions::with_scale(
self : CompileOptions,
scale : Double?,
) -> CompileOptions {
{ ..self, scale, }
}
///|
pub fn CompileOptions::with_no_xml_tag(
self : CompileOptions,
no_xml_tag : Bool?,
) -> CompileOptions {
{ ..self, no_xml_tag, }
}
///|
pub fn CompileOptions::with_salt(
self : CompileOptions,
salt : String?,
) -> CompileOptions {
{ ..self, salt, }
}
///|
pub fn CompileOptions::with_omit_version(
self : CompileOptions,
omit_version : Bool?,
) -> CompileOptions {
{ ..self, omit_version, }
}
///|
pub(all) suberror DiagoError {
ParseError(String)
IOError(String)
IrError(String)
GraphError(String)
LayoutError(String)
RenderError(String)
ConfigError(String)
TargetError(String)
UnsupportedFeature(String)
} derive(Eq, Debug)
///|
pub fn parse(
source : String,
options? : ParseOptions = ParseOptions::new(),
) -> @ir.Map raise DiagoError {
match
@compiler.compile_ir(source, options=to_compiler_parse_options(options)) {
Ok(ir_map) => ir_map
Err(err) => raise_diago_compiler_error(err)
}
}
///|
pub fn compile_diagram(
source : String,
options? : CompileOptions = CompileOptions::new(),
) -> @diagram.Diagram raise DiagoError {
let compiled = match
@compiler.compile_graph(
"diagram",
source,
options=to_compiler_parse_options(options.parse_options),
) {
Ok(compiled) => compiled
Err(err) => raise_diago_compiler_error(err)
}
match
@lib.compile_diagram(
compiled.graph_input(),
compiled.source_config(),
options=to_lib_diagram_options(options),
) {
Ok(diagram) => diagram
Err(err) => raise_diago_lib_error(err)
}
}
///|
pub fn compile(
source : String,
options? : CompileOptions = CompileOptions::new(),
) -> String raise DiagoError {
let diagram = compile_diagram(source, options~)
match @lib.render(diagram, options=to_lib_render_options(options)) {
Ok(rendered) => rendered
Err(err) => raise_diago_lib_error(err)
}
}
///|
fn to_compiler_parse_options(options : ParseOptions) -> @compiler.ParseOptions {
@compiler.ParseOptions::new().with_import_resolver(options.import_resolver)
}
///|
fn to_lib_diagram_options(options : CompileOptions) -> @lib.DiagramOptions {
let mut resolved = @lib.DiagramOptions::new()
.with_layout_engine(to_lib_layout_engine(options.layout_engine))
.with_direction(options.direction)
.with_target_spec(options.target_spec)
.with_theme_name(options.theme_name)
match options.layout_plugin {
Some(plugin) =>
resolved = resolved.with_layout_plugin(
Some(fn(
graph : @graph.GraphInput,
config : @engine_api.LayoutConfig,
direction : @engine_api.Direction,
) -> Result[@graph.GraphInput, String] {
plugin.run(graph, config, direction)
}),
)
None => ()
}
match options.route_plugin {
Some(plugin) =>
resolved = resolved.with_route_plugin(
Some(fn(
graph : @graph.GraphInput,
) -> Result[@graph.GraphInput, String] {
plugin.run(graph)
}),
)
None => ()
}
resolved
}
///|
fn to_lib_render_options(options : CompileOptions) -> @lib.RenderOptions {
@lib.RenderOptions::new()
.with_output_mode(to_lib_output_mode(options.output_mode))
.with_theme_name(options.theme_name)
.with_dark_theme_id(options.dark_theme_id)
.with_sketch(options.sketch)
.with_pad(options.pad)
.with_center(options.center)
.with_theme_overrides(options.theme_overrides)
.with_dark_theme_overrides(options.dark_theme_overrides)
.with_scale(options.scale)
.with_no_xml_tag(options.no_xml_tag)
.with_salt(options.salt)
.with_omit_version(options.omit_version)
}
///|
fn to_lib_layout_engine(layout_engine : LayoutEngine) -> @lib.LayoutEngine {
match layout_engine {
Auto => Auto
Dagre => Dagre
Elk => Elk
Railway => Railway
}
}
///|
fn to_lib_output_mode(output_mode : OutputMode) -> @lib.OutputMode {
match output_mode {
Svg => Svg
Ascii => Ascii
Unicode => Unicode
}
}
///|
fn[T] raise_diago_compiler_error(
err : @compiler.CompileError,
) -> T raise DiagoError {
match err {
ParseError(message) => raise ParseError(message)
IOError(message) => raise IOError(message)
IrError(message) => raise IrError(message)
GraphError(message) => raise GraphError(message)
ConfigError(message) => raise ConfigError(message)
}
}
///|
fn[T] raise_diago_lib_error(err : @lib.LibError) -> T raise DiagoError {
match err {
LayoutError(message) => raise LayoutError(message)
RenderError(message) => raise RenderError(message)
ConfigError(message) => raise ConfigError(message)
TargetError(message) => raise TargetError(message)
UnsupportedFeature(message) => raise UnsupportedFeature(message)
}
}