use serde::{Deserialize, Serialize}; // --- Boyut sistemi --- #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum SizeValue { #[serde(rename = "fixed")] Fixed { value: f64 }, #[serde(rename = "auto")] Auto, #[serde(rename = "fr")] Fr { value: f64 }, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct SizeConstraint { pub width: SizeValue, pub height: SizeValue, pub min_width: Option, pub min_height: Option, pub max_width: Option, pub max_height: Option, } impl Default for SizeConstraint { fn default() -> Self { Self { width: SizeValue::Auto, height: SizeValue::Auto, min_width: None, min_height: None, max_width: None, max_height: None, } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PageSettings { pub width: f64, pub height: f64, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Padding { #[serde(default)] pub top: f64, #[serde(default)] pub right: f64, #[serde(default)] pub bottom: f64, #[serde(default)] pub left: f64, } // --- Positioning --- #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(tag = "type")] pub enum PositionMode { #[default] #[serde(rename = "flow")] Flow, #[serde(rename = "absolute")] Absolute { x: f64, y: f64 }, } // --- Stil --- #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct TextStyle { pub font_size: Option, pub font_weight: Option, pub font_style: Option, pub font_family: Option, pub color: Option, pub align: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct LineStyle { pub stroke_color: Option, pub stroke_width: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct ContainerStyle { pub background_color: Option, pub border_color: Option, pub border_width: Option, pub border_radius: Option, pub border_style: Option, } // --- Binding --- #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ScalarBinding { pub path: String, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ArrayBinding { pub path: String, } // --- Tablo --- #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TableColumn { pub id: String, pub field: String, pub title: String, pub width: SizeValue, pub align: String, pub format: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct TableStyle { pub header_bg: Option, pub header_color: Option, pub zebra_odd: Option, pub zebra_even: Option, pub border_color: Option, pub border_width: Option, pub font_size: Option, pub header_font_size: Option, /// Hücre iç boşluğu — yatay (sol+sağ), mm cinsinden. Default: 2.0 pub cell_padding_h: Option, /// Hücre iç boşluğu — dikey (üst+alt), mm cinsinden. Default: 1.0 pub cell_padding_v: Option, /// Header hücre iç boşluğu — yatay (sol+sağ), mm cinsinden. Default: cell_padding_h pub header_padding_h: Option, /// Header hücre iç boşluğu — dikey (üst+alt), mm cinsinden. Default: cell_padding_v pub header_padding_v: Option, } // --- Barcode --- #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct BarcodeStyle { pub color: Option, pub include_text: Option, } // --- Rich Text --- #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RichTextSpan { #[serde(default)] pub text: Option, #[serde(default)] pub binding: Option, #[serde(default)] pub style: TextStyle, } // --- Chart --- #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ChartType { Bar, Line, Pie, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum GroupMode { Grouped, Stacked, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct ChartTitle { pub text: String, pub font_size: Option, pub color: Option, pub align: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct ChartLegend { pub show: bool, pub position: Option, pub font_size: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct ChartLabels { pub show: bool, pub font_size: Option, pub color: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct ChartAxis { pub x_label: Option, pub y_label: Option, pub show_grid: Option, pub grid_color: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct ChartStyle { pub colors: Option>, pub background_color: Option, pub bar_gap: Option, pub line_width: Option, pub show_points: Option, pub curve_type: Option, pub inner_radius: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ChartElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub chart_type: ChartType, pub data_source: ArrayBinding, pub category_field: String, pub value_field: String, #[serde(default)] pub group_field: Option, #[serde(default)] pub group_mode: Option, #[serde(default)] pub title: Option, #[serde(default)] pub legend: Option, #[serde(default)] pub labels: Option, #[serde(default)] pub axis: Option, #[serde(default)] pub style: ChartStyle, } // --- Element tipleri --- #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct ImageStyle { pub object_fit: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum TemplateElement { #[serde(rename = "container")] Container(ContainerElement), #[serde(rename = "static_text")] StaticText(StaticTextElement), #[serde(rename = "text")] Text(TextElement), #[serde(rename = "line")] Line(LineElement), #[serde(rename = "repeating_table")] RepeatingTable(RepeatingTableElement), #[serde(rename = "image")] Image(ImageElement), #[serde(rename = "page_number")] PageNumber(PageNumberElement), #[serde(rename = "barcode")] Barcode(BarcodeElement), #[serde(rename = "page_break")] PageBreak(PageBreakElement), #[serde(rename = "current_date")] CurrentDate(CurrentDateElement), #[serde(rename = "shape")] Shape(ShapeElement), #[serde(rename = "checkbox")] Checkbox(CheckboxElement), #[serde(rename = "calculated_text")] CalculatedText(CalculatedTextElement), #[serde(rename = "rich_text")] RichText(RichTextElement), #[serde(rename = "chart")] Chart(Box), } impl TemplateElement { pub fn id(&self) -> &str { match self { Self::Container(e) => &e.id, Self::StaticText(e) => &e.id, Self::Text(e) => &e.id, Self::Line(e) => &e.id, Self::RepeatingTable(e) => &e.id, Self::Image(e) => &e.id, Self::PageNumber(e) => &e.id, Self::Barcode(e) => &e.id, Self::PageBreak(e) => &e.id, Self::CurrentDate(e) => &e.id, Self::Shape(e) => &e.id, Self::Checkbox(e) => &e.id, Self::CalculatedText(e) => &e.id, Self::RichText(e) => &e.id, Self::Chart(e) => &e.id, } } pub fn position(&self) -> &PositionMode { match self { Self::Container(e) => &e.position, Self::StaticText(e) => &e.position, Self::Text(e) => &e.position, Self::Line(e) => &e.position, Self::RepeatingTable(e) => &e.position, Self::Image(e) => &e.position, Self::PageNumber(e) => &e.position, Self::Barcode(e) => &e.position, Self::PageBreak(_) => &PositionMode::Flow, Self::CurrentDate(e) => &e.position, Self::Shape(e) => &e.position, Self::Checkbox(e) => &e.position, Self::CalculatedText(e) => &e.position, Self::RichText(e) => &e.position, Self::Chart(e) => &e.position, } } pub fn size(&self) -> &SizeConstraint { static DEFAULT_SIZE: SizeConstraint = SizeConstraint { width: SizeValue::Auto, height: SizeValue::Auto, min_width: None, min_height: None, max_width: None, max_height: None, }; match self { Self::Container(e) => &e.size, Self::StaticText(e) => &e.size, Self::Text(e) => &e.size, Self::Line(e) => &e.size, Self::RepeatingTable(e) => &e.size, Self::Image(e) => &e.size, Self::PageNumber(e) => &e.size, Self::Barcode(e) => &e.size, Self::PageBreak(_) => &DEFAULT_SIZE, Self::CurrentDate(e) => &e.size, Self::Shape(e) => &e.size, Self::Checkbox(e) => &e.size, Self::CalculatedText(e) => &e.size, Self::RichText(e) => &e.size, Self::Chart(e) => &e.size, } } } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RichTextElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, #[serde(default)] pub style: TextStyle, // varsayilan stil (span'lar override edebilir) pub content: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ContainerElement { pub id: String, #[serde(default)] pub position: PositionMode, #[serde(default)] pub size: SizeConstraint, #[serde(default = "default_column")] pub direction: String, #[serde(default)] pub gap: f64, #[serde(default)] pub padding: Padding, #[serde(default = "default_stretch")] pub align: String, #[serde(default = "default_start")] pub justify: String, #[serde(default)] pub style: ContainerStyle, #[serde(default)] pub children: Vec, #[serde(default = "default_auto")] pub break_inside: String, } fn default_auto() -> String { "auto".to_string() } fn default_column() -> String { "column".to_string() } fn default_stretch() -> String { "stretch".to_string() } fn default_start() -> String { "start".to_string() } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct StaticTextElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub style: TextStyle, pub content: String, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TextElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub style: TextStyle, pub content: Option, pub binding: ScalarBinding, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct LineElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub style: LineStyle, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ImageElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub src: Option, pub binding: Option, pub style: ImageStyle, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PageNumberElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub style: TextStyle, pub format: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct BarcodeElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub format: String, // qr, ean13, ean8, code128, code39 pub value: Option, pub binding: Option, pub style: BarcodeStyle, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RepeatingTableElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub data_source: ArrayBinding, pub columns: Vec, pub style: TableStyle, #[serde(default = "default_true")] pub repeat_header: Option, } fn default_true() -> Option { Some(true) } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PageBreakElement { pub id: String, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CurrentDateElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub style: TextStyle, pub format: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ShapeElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub shape_type: String, // rectangle, ellipse, rounded_rectangle pub style: ContainerStyle, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase", default)] pub struct CheckboxStyle { pub size: Option, // mm — kare boyutu pub check_color: Option, // checkmark rengi pub border_color: Option, // kare kenar rengi pub border_width: Option, // kenar kalınlığı } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CheckboxElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub checked: Option, // statik değer pub binding: Option, // dinamik boolean binding pub style: CheckboxStyle, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CalculatedTextElement { pub id: String, pub position: PositionMode, pub size: SizeConstraint, pub style: TextStyle, pub expression: String, pub format: Option, } // --- Template --- #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Template { pub id: String, pub name: String, pub page: PageSettings, pub fonts: Vec, #[serde(default)] pub header: Option, #[serde(default)] pub footer: Option, pub root: ContainerElement, #[serde(default)] pub format_config: Option, } /// Sayı/para birimi formatlama ayarları. /// Belirtilmezse Türk Lirası varsayılan (. binlik, , ondalık, ₺ sembol). #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct FormatConfig { /// Binlik ayırıcı (varsayılan ".") #[serde(default = "FormatConfig::default_thousands_sep")] pub thousands_separator: String, /// Ondalık ayırıcı (varsayılan ",") #[serde(default = "FormatConfig::default_decimal_sep")] pub decimal_separator: String, /// Para birimi sembolü (varsayılan "₺") #[serde(default = "FormatConfig::default_currency_symbol")] pub currency_symbol: String, /// Para birimi sembolü pozisyonu: "suffix" (varsayılan) veya "prefix" #[serde(default = "FormatConfig::default_currency_position")] pub currency_position: String, } impl FormatConfig { fn default_thousands_sep() -> String { ".".to_string() } fn default_decimal_sep() -> String { ",".to_string() } fn default_currency_symbol() -> String { "₺".to_string() } fn default_currency_position() -> String { "suffix".to_string() } } impl Default for FormatConfig { fn default() -> Self { Self { thousands_separator: Self::default_thousands_sep(), decimal_separator: Self::default_decimal_sep(), currency_symbol: Self::default_currency_symbol(), currency_position: Self::default_currency_position(), } } }