Skip to main content

cqlib_core/visualization/circuit/
style.rs

1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2026
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13//! Gate style maps for figure visualization.
14//!
15//! This module loads built-in JSON style dictionaries (`default`, `gray`) and merges
16//! optional runtime overrides for figure rendering.
17
18use serde::Deserialize;
19use std::collections::HashMap;
20
21/// Per-gate visual style settings (compatible with Python style JSON schema).
22#[derive(Debug, Clone, Deserialize, Default)]
23pub struct GateStyle {
24    /// Optional gate border color in CSS color syntax.
25    #[serde(default)]
26    pub border_color: Option<String>,
27    /// Optional gate fill color in CSS color syntax.
28    #[serde(default)]
29    pub background_color: Option<String>,
30    /// Optional gate label font size.
31    #[serde(default)]
32    pub font_size: Option<f64>,
33    /// Optional gate label text color in CSS color syntax.
34    #[serde(default)]
35    pub text_color: Option<String>,
36    /// Optional connector or wire color in CSS color syntax.
37    #[serde(default)]
38    pub line_color: Option<String>,
39    /// Optional connector or wire stroke width.
40    #[serde(default)]
41    pub line_width: Option<f64>,
42}
43
44/// Style dictionary keyed by gate name (with mandatory `default` fallback).
45#[derive(Debug, Clone)]
46pub struct StyleBook {
47    styles: HashMap<String, GateStyle>,
48    default_style: GateStyle,
49}
50
51impl StyleBook {
52    /// Load a built-in style map by name and apply optional runtime overrides.
53    ///
54    /// Supported built-in names are `default` and `gray`. Unknown names fall back to
55    /// `default`.
56    pub fn new(style_name: &str, overrides: &HashMap<String, GateStyle>) -> Self {
57        let mut styles = load_style(style_name);
58        for (k, v) in overrides {
59            styles.insert(k.clone(), v.clone());
60        }
61        if !styles.contains_key("default") {
62            styles.insert("default".to_string(), GateStyle::default());
63        }
64        let default_style = styles.get("default").cloned().unwrap_or_default();
65        Self {
66            styles,
67            default_style,
68        }
69    }
70
71    /// Return style for gate name, falling back to `default`.
72    pub fn get(&self, gate_name: &str) -> &GateStyle {
73        self.styles.get(gate_name).unwrap_or(&self.default_style)
74    }
75}
76
77/// Load built-in style JSON by name.
78///
79/// Supported names:
80/// - `default`
81/// - `gray`
82///
83/// Unknown names fall back to `default`.
84fn load_style(style_name: &str) -> HashMap<String, GateStyle> {
85    let style_key = style_name.trim().to_ascii_lowercase();
86    let json = match style_key.as_str() {
87        "gray" => include_str!("styles/gray.json"),
88        _ => include_str!("styles/default.json"),
89    };
90    serde_json::from_str(json).unwrap_or_else(|_| {
91        let mut fallback = HashMap::new();
92        fallback.insert("default".to_string(), GateStyle::default());
93        fallback
94    })
95}