use std::path::Path;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use crate::{RectStyling, Style};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct Options {
beta: bool,
layout: Layout,
lines: Lines,
orientation: Orientation,
spacing: Spacing,
style: Style,
url: String,
}
impl Options {
pub fn from_json(path: &Path) -> Result<Self> {
let json = std::fs::read_to_string(path).context("Error reading JSON with plot Config")?;
serde_json::from_str(&json).context("Error parsing JSON to plot Config")
}
pub fn get_layout(&self) -> Layout {
self.layout
}
pub fn get_lines(&self) -> Lines {
self.lines
}
pub fn get_orientation(&self) -> Orientation {
self.orientation
}
pub fn get_spacing(&self) -> Spacing {
self.spacing
}
pub fn get_style(&self) -> &Style {
&self.style
}
pub fn get_url(&self) -> String {
let channel = match self.beta {
true => "beta",
false => "stable",
};
match self.url.ends_with("/") {
true => format!("{}{channel}", self.url),
false => format!("{}/{channel}", self.url),
}
}
pub fn beta(mut self) -> Self {
self.beta = true;
self
}
pub fn layout(mut self, new_layout: Layout) -> Self {
self.layout = new_layout;
self.lines = if let Lines::Technical(radius) = self.lines {
match new_layout {
Layout::Diagram | Layout::DiagramBalanced => Lines::Technical(radius),
_ => Lines::Spline,
}
} else {
Lines::Spline
};
self
}
pub fn orientation(mut self, new_orientation: Orientation) -> Self {
self.orientation = new_orientation;
self
}
pub fn radius<R: Into<f64>>(mut self, radius: R) -> Self {
if ![Layout::Diagram, Layout::DiagramBalanced].contains(&self.layout) {
return self;
}
self.lines = Lines::Technical(radius.into() as f32);
self
}
pub fn scale<S: Into<f64>>(self, scale: S) -> Self {
let scale_f64 = scale.into();
self.style(|s| s.scale(scale_f64)).spacing(|s| s.scale(scale_f64))
}
pub fn dev(mut self) -> Self {
self.url = "http://0.0.0.0:9000".to_string();
self
}
pub fn spacing(mut self, configure_spacing: impl FnOnce(Spacing) -> Spacing) -> Self {
self.spacing = configure_spacing(self.spacing);
self
}
pub fn style(mut self, configure_style: impl FnOnce(Style) -> Style) -> Self {
self.style = configure_style(self.style);
self
}
fn _lines(mut self, new_lines: Lines) -> Self {
self.lines = new_lines;
self
}
}
impl Default for Options {
fn default() -> Self {
Self::mode(true)
}
}
impl Options {
fn mode(dark: bool) -> Self {
Self {
beta: false,
layout: Layout::default(),
lines: Lines::default(),
orientation: Orientation::default(),
spacing: Spacing::default(),
style: if dark { Style::dark() } else { Style::light() },
url: "https://api.graphplot.io/".into(),
}
}
pub fn print() -> Self {
let config = Self::mode(false);
config.style(|s| s.background_color("#ffffffff").graph(|g| g.background_color("#ffffffff")))
}
pub fn light() -> Self {
Self::mode(false)
}
pub fn dark() -> Self {
Self::mode(true)
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Layout {
Circular,
Forcedirected,
ForcedirectedScaled,
Layered,
Radial,
Spring,
#[default]
Diagram,
DiagramBalanced,
}
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Lines {
Spline,
None,
Line,
Curved,
Polyline,
Technical(f32),
}
impl Default for Lines {
fn default() -> Self {
Self::Technical(5.0)
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Orientation {
#[default]
Undefined,
Down,
Up,
Right,
Left,
}
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct Spacing {
nodes: Space,
layers: Space,
}
impl Spacing {
pub fn get_nodes(&self) -> Space {
self.nodes
}
pub fn get_layers(&self) -> Space {
self.layers
}
pub fn layers<S: Into<f64>>(mut self, space: S) -> Self {
self.layers = Space::Static(space.into());
self
}
pub fn nodes<S: Into<f64>>(mut self, space: S) -> Self {
self.nodes = Space::Static(space.into());
self
}
pub fn scale<S: Into<f64>>(mut self, scale: S) -> Self {
let scale_f64 = scale.into();
if let Space::Static(layers) = self.layers {
self.layers = Space::Static(layers * scale_f64);
}
if let Space::Static(nodes) = self.nodes {
self.nodes = Space::Static(nodes * scale_f64);
}
self
}
}
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Space {
#[default]
Auto,
Static(f64),
}