use std::{
cell::RefCell,
rc::{Rc, Weak},
};
use quick_xml::{Reader, Writer, events::BytesStart};
use super::{
AreaSymbol, CombinedAreaSymbol, CombinedLineSymbol, LineSymbol, PointSymbol, SymbolSet,
TextSymbol,
};
use crate::utils::{parse_attr, parse_attr_raw};
use crate::{Code, Error, Result, colors::ColorSet};
#[derive(Default, Debug, Clone)]
pub struct SymbolCommon {
pub name: String,
pub code: Code,
pub description: String,
pub is_helper_symbol: bool,
pub is_hidden: bool,
pub is_protected: bool,
pub custom_icon: Option<String>,
}
#[derive(Debug, Clone)]
pub enum WeakSymbol {
Line(Weak<RefCell<LineSymbol>>),
Area(Weak<RefCell<AreaSymbol>>),
Point(Weak<RefCell<PointSymbol>>),
Text(Weak<RefCell<TextSymbol>>),
CombinedArea(Weak<RefCell<CombinedAreaSymbol>>),
CombinedLine(Weak<RefCell<CombinedLineSymbol>>),
}
impl WeakSymbol {
pub fn upgrade(&self) -> Option<Symbol> {
match self {
WeakSymbol::Line(weak) => weak.upgrade().map(Symbol::Line),
WeakSymbol::Area(weak) => weak.upgrade().map(Symbol::Area),
WeakSymbol::Point(weak) => weak.upgrade().map(Symbol::Point),
WeakSymbol::Text(weak) => weak.upgrade().map(Symbol::Text),
WeakSymbol::CombinedArea(weak) => weak.upgrade().map(Symbol::CombinedArea),
WeakSymbol::CombinedLine(weak) => weak.upgrade().map(Symbol::CombinedLine),
}
}
}
impl PartialEq for WeakSymbol {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Line(l0), Self::Line(r0)) => l0.ptr_eq(r0),
(Self::Area(l0), Self::Area(r0)) => l0.ptr_eq(r0),
(Self::Point(l0), Self::Point(r0)) => l0.ptr_eq(r0),
(Self::Text(l0), Self::Text(r0)) => l0.ptr_eq(r0),
(Self::CombinedArea(l0), Self::CombinedArea(r0)) => l0.ptr_eq(r0),
(Self::CombinedLine(l0), Self::CombinedLine(r0)) => l0.ptr_eq(r0),
_ => false,
}
}
}
impl From<Weak<RefCell<AreaSymbol>>> for WeakSymbol {
fn from(value: Weak<RefCell<AreaSymbol>>) -> Self {
WeakSymbol::Area(value)
}
}
impl From<Weak<RefCell<LineSymbol>>> for WeakSymbol {
fn from(value: Weak<RefCell<LineSymbol>>) -> Self {
WeakSymbol::Line(value)
}
}
impl From<Weak<RefCell<PointSymbol>>> for WeakSymbol {
fn from(value: Weak<RefCell<PointSymbol>>) -> Self {
WeakSymbol::Point(value)
}
}
impl From<Weak<RefCell<TextSymbol>>> for WeakSymbol {
fn from(value: Weak<RefCell<TextSymbol>>) -> Self {
WeakSymbol::Text(value)
}
}
impl From<Weak<RefCell<CombinedAreaSymbol>>> for WeakSymbol {
fn from(value: Weak<RefCell<CombinedAreaSymbol>>) -> Self {
WeakSymbol::CombinedArea(value)
}
}
impl From<Weak<RefCell<CombinedLineSymbol>>> for WeakSymbol {
fn from(value: Weak<RefCell<CombinedLineSymbol>>) -> Self {
WeakSymbol::CombinedLine(value)
}
}
#[derive(Debug, Clone)]
pub enum Symbol {
Line(Rc<RefCell<LineSymbol>>),
Area(Rc<RefCell<AreaSymbol>>),
Point(Rc<RefCell<PointSymbol>>),
Text(Rc<RefCell<TextSymbol>>),
CombinedArea(Rc<RefCell<CombinedAreaSymbol>>),
CombinedLine(Rc<RefCell<CombinedLineSymbol>>),
}
impl Symbol {
pub fn downgrade(&self) -> WeakSymbol {
match self {
Symbol::Line(rc) => WeakSymbol::Line(Rc::downgrade(rc)),
Symbol::Area(rc) => WeakSymbol::Area(Rc::downgrade(rc)),
Symbol::Point(rc) => WeakSymbol::Point(Rc::downgrade(rc)),
Symbol::Text(rc) => WeakSymbol::Text(Rc::downgrade(rc)),
Symbol::CombinedArea(rc) => WeakSymbol::CombinedArea(Rc::downgrade(rc)),
Symbol::CombinedLine(rc) => WeakSymbol::CombinedLine(Rc::downgrade(rc)),
}
}
}
impl PartialEq for Symbol {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Line(l0), Self::Line(r0)) => l0.as_ptr() == r0.as_ptr(),
(Self::Area(l0), Self::Area(r0)) => l0.as_ptr() == r0.as_ptr(),
(Self::Point(l0), Self::Point(r0)) => l0.as_ptr() == r0.as_ptr(),
(Self::Text(l0), Self::Text(r0)) => l0.as_ptr() == r0.as_ptr(),
(Self::CombinedArea(l0), Self::CombinedArea(r0)) => l0.as_ptr() == r0.as_ptr(),
(Self::CombinedLine(l0), Self::CombinedLine(r0)) => l0.as_ptr() == r0.as_ptr(),
_ => false,
}
}
}
impl From<LineSymbol> for Symbol {
fn from(value: LineSymbol) -> Self {
Symbol::Line(Rc::new(RefCell::new(value)))
}
}
impl From<AreaSymbol> for Symbol {
fn from(value: AreaSymbol) -> Self {
Symbol::Area(Rc::new(RefCell::new(value)))
}
}
impl From<TextSymbol> for Symbol {
fn from(value: TextSymbol) -> Self {
Symbol::Text(Rc::new(RefCell::new(value)))
}
}
impl From<PointSymbol> for Symbol {
fn from(value: PointSymbol) -> Self {
Symbol::Point(Rc::new(RefCell::new(value)))
}
}
impl From<CombinedLineSymbol> for Symbol {
fn from(value: CombinedLineSymbol) -> Self {
Symbol::CombinedLine(Rc::new(RefCell::new(value)))
}
}
impl From<CombinedAreaSymbol> for Symbol {
fn from(value: CombinedAreaSymbol) -> Self {
Symbol::CombinedArea(Rc::new(RefCell::new(value)))
}
}
macro_rules! impl_symbol_getter {
($method:ident -> $ret_type:ty, |$s:ident| $expr:expr) => {
pub fn $method(&self) -> Result<$ret_type> {
match self {
Symbol::Line(rc) => {
let $s = rc.try_borrow()?;
Ok($expr)
}
Symbol::Area(rc) => {
let $s = rc.try_borrow()?;
Ok($expr)
}
Symbol::Point(rc) => {
let $s = rc.try_borrow()?;
Ok($expr)
}
Symbol::Text(rc) => {
let $s = rc.try_borrow()?;
Ok($expr)
}
Symbol::CombinedLine(rc) => {
let $s = rc.try_borrow()?;
Ok($expr)
}
Symbol::CombinedArea(rc) => {
let $s = rc.try_borrow()?;
Ok($expr)
}
}
}
};
}
macro_rules! impl_symbol_setter {
($method:ident($param:ident: $param_type:ty), |$s:ident| $expr:expr) => {
pub fn $method(&self, $param: $param_type) -> Result<()> {
match self {
Symbol::Line(rc) => {
let mut $s = rc.try_borrow_mut()?;
$expr
}
Symbol::Area(rc) => {
let mut $s = rc.try_borrow_mut()?;
$expr
}
Symbol::Point(rc) => {
let mut $s = rc.try_borrow_mut()?;
$expr
}
Symbol::Text(rc) => {
let mut $s = rc.try_borrow_mut()?;
$expr
}
Symbol::CombinedLine(rc) => {
let mut $s = rc.try_borrow_mut()?;
$expr
}
Symbol::CombinedArea(rc) => {
let mut $s = rc.try_borrow_mut()?;
$expr
}
}
Ok(())
}
};
}
impl Symbol {
impl_symbol_getter!(has_custom_icon -> bool, |s| s.common.custom_icon.is_some());
impl_symbol_setter!(set_custom_icon(icon: Option<String>), |s| s.common.custom_icon = icon);
impl_symbol_getter!(get_code -> Code, |s| s.common.code);
impl_symbol_setter!(set_code(code: Code), |s| s.common.code = code);
impl_symbol_getter!(is_helper_symbol -> bool, |s| s.common.is_helper_symbol);
impl_symbol_setter!(set_helper_symbol(is_helper: bool), |s| s.common.is_helper_symbol = is_helper);
impl_symbol_getter!(is_hidden -> bool, |s| s.common.is_helper_symbol);
impl_symbol_setter!(set_hidden(is_hidden: bool), |s| s.common.is_hidden = is_hidden);
impl_symbol_getter!(is_protected -> bool, |s| s.common.is_helper_symbol);
impl_symbol_setter!(set_protected(is_protected: bool), |s| s.common.is_protected = is_protected);
pub(super) fn parse<R: std::io::BufRead>(
reader: &mut Reader<R>,
element: &BytesStart<'_>,
color_set: &ColorSet,
) -> Result<(usize, Symbol, Vec<usize>)> {
let mut id = usize::MAX;
let mut symbol_type = u8::MAX;
let mut common = SymbolCommon::default();
for attr in element.attributes().filter_map(std::result::Result::ok) {
match attr.key.local_name().as_ref() {
b"type" => symbol_type = parse_attr_raw(attr.value).unwrap_or(symbol_type),
b"name" => common.name = parse_attr(attr, element.decoder()).unwrap_or(common.name),
b"code" => common.code = parse_attr_raw(attr.value).unwrap_or(common.code),
b"id" => id = parse_attr_raw(attr.value).unwrap_or(id),
b"is_helper_symbol" => {
common.is_helper_symbol = attr.as_bool().unwrap_or(false);
}
b"is_hidden" => {
common.is_hidden = attr.as_bool().unwrap_or(false);
}
b"is_protected" => {
common.is_protected = attr.as_bool().unwrap_or(false);
}
_ => {}
}
}
if id == usize::MAX {
return Err(Error::ParseOmapFileError(
"Could not parse symbol".to_string(),
));
}
let mut public_component_ids = Vec::new();
let symbol = match symbol_type {
1 => Symbol::Point(Rc::new(RefCell::new(PointSymbol::parse(
reader, color_set, common,
)?))),
2 => Symbol::Line(Rc::new(RefCell::new(LineSymbol::parse(
reader, color_set, common,
)?))),
4 => Symbol::Area(Rc::new(RefCell::new(AreaSymbol::parse(
reader, color_set, common,
)?))),
8 => Symbol::Text(Rc::new(RefCell::new(TextSymbol::parse(
reader, color_set, common,
)?))),
16 => {
let (symbol, component_ids) = CombinedAreaSymbol::parse(reader, color_set, common)?;
public_component_ids.extend(component_ids);
Symbol::CombinedArea(Rc::new(RefCell::new(symbol)))
}
_ => {
return Err(Error::ParseOmapFileError(format!(
"Could not parse symbol of type {symbol_type}"
)));
}
};
Ok((id, symbol, public_component_ids))
}
pub(super) fn write<W: std::io::Write>(
&self,
writer: &mut Writer<W>,
symbol_set: &SymbolSet,
color_set: &ColorSet,
index: usize,
) -> Result<()> {
match self {
Symbol::Line(rc) => rc.try_borrow()?.write(writer, color_set, Some(index)),
Symbol::Area(rc) => rc.try_borrow()?.write(writer, color_set, Some(index)),
Symbol::Point(rc) => rc.try_borrow()?.write(writer, color_set, Some(index)),
Symbol::Text(rc) => rc.try_borrow()?.write(writer, color_set, index),
Symbol::CombinedArea(rc) => {
rc.try_borrow()?.write(writer, symbol_set, color_set, index)
}
Symbol::CombinedLine(rc) => {
rc.try_borrow()?.write(writer, symbol_set, color_set, index)
}
}?;
Ok(())
}
}