#![warn(missing_docs)]
#[macro_use]
extern crate log;
extern crate shellexpand;
extern crate symbolic_expressions;
use std::fmt;
use std::path::{Path, PathBuf};
use std::result;
pub use symbolic_expressions::{Sexp,SexpError};
pub use error::*;
pub use util::read_file;
pub use util::write_file;
fn parse_split_quote_aware_int(n_opt: Option<usize>, s: &str) -> Result<Vec<String>, KicadError> {
let mut i = 0;
let mut v: Vec<String> = vec![];
let mut inside_quotation = false;
let mut quotation_seen = false;
let mut s2: String = "".into();
for c in s.chars() {
if let Some(n) = n_opt {
if i == n {
s2.push(c);
continue;
}
}
if !inside_quotation && c == '\"' {
inside_quotation = true;
continue;
}
if inside_quotation && c == '\"' {
inside_quotation = false;
quotation_seen = true;
continue;
}
if !inside_quotation && c == ' ' {
if !s2.is_empty() || quotation_seen {
i += 1;
v.push(s2.clone());
s2.clear();
}
quotation_seen = false;
continue;
}
s2.push(c);
}
if !s2.is_empty() || quotation_seen {
v.push(s2.clone())
}
if let Some(n) = n_opt {
if v.len() < n {
return str_error(format!("expecting {} elements in {}", n, s));
}
}
Ok(v)
}
fn parse_split_quote_aware_n(n: usize, s: &str) -> Result<Vec<String>, KicadError> {
parse_split_quote_aware_int(Some(n), s)
}
fn parse_split_quote_aware(s: &str) -> Result<Vec<String>, KicadError> {
parse_split_quote_aware_int(None, s)
}
#[derive(Debug)]
pub enum KicadFile {
Unknown(PathBuf),
Module(footprint::Module),
Schematic(schematic::Schematic),
Layout(layout::Layout),
SymbolLib(symbol_lib::SymbolLib),
Project(project::Project),
FpLibTable(fp_lib_table::FpLibTable),
}
#[derive(PartialEq)]
pub enum Expected {
Module,
Schematic,
Layout,
SymbolLib,
Project,
FpLibTable,
Any,
}
impl fmt::Display for KicadFile {
fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
match *self {
KicadFile::Unknown(ref x) => write!(f, "unknown: {}", x.to_str().unwrap()),
KicadFile::Module(_) => write!(f, "module"),
KicadFile::Schematic(_) => write!(f, "schematic"),
KicadFile::Layout(_) => write!(f, "layout"),
KicadFile::SymbolLib(_) => write!(f, "symbollib"),
KicadFile::Project(_) => write!(f, "project"),
KicadFile::FpLibTable(_) => write!(f, "fp-lib-table"),
}
}
}
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn read_kicad_file(name: &Path, expected: Expected) -> Result<KicadFile, KicadError> {
let data = read_file(name)?;
match footprint::parse(&data) {
Ok(module) => return Ok(KicadFile::Module(module)),
Err(x) => if expected == Expected::Module {
return Err(x);
},
}
match schematic::parse(Some(PathBuf::from(name)), &data) {
Ok(sch) => return Ok(KicadFile::Schematic(sch)),
Err(x) => if expected == Expected::Schematic {
return Err(x);
},
}
match layout::parse(&data) {
Ok(layout) => return Ok(KicadFile::Layout(layout)),
Err(x) => if expected == Expected::Layout {
return Err(x);
},
}
match symbol_lib::parse_str(&data) {
Ok(sl) => return Ok(KicadFile::SymbolLib(sl)),
Err(x) => if expected == Expected::SymbolLib {
return Err(x);
},
}
match project::parse_str(&data) {
Ok(p) => return Ok(KicadFile::Project(p)),
Err(x) => if expected == Expected::Project {
return Err(x);
},
}
match fp_lib_table::parse(&data) {
Ok(p) => return Ok(KicadFile::FpLibTable(p)),
Err(x) => if expected == Expected::FpLibTable {
return Err(x.into());
},
}
Ok(KicadFile::Unknown(PathBuf::from(name)))
}
pub fn read_module(name: &Path) -> Result<footprint::Module, KicadError> {
match read_kicad_file(name, Expected::Module)? {
KicadFile::Module(mo) => Ok(mo),
x => str_error(format!("unexpected {} in {}", x, name.display())),
}
}
pub fn read_schematic(name: &Path) -> Result<schematic::Schematic, KicadError> {
match read_kicad_file(name, Expected::Schematic)? {
KicadFile::Schematic(mo) => Ok(mo),
x => str_error(format!("unexpected {} in {}", x, name.display())),
}
}
pub fn read_layout(name: &Path) -> Result<layout::Layout, KicadError> {
match read_kicad_file(name, Expected::Layout)? {
KicadFile::Layout(mo) => Ok(mo),
x => str_error(format!("unexpected {} in {}", x, name.display())),
}
}
pub fn write_layout(layout: &layout::Layout, name: &Path) -> Result<(), KicadError> {
let s = layout::layout_to_string(layout, 0)?;
write_file(name, &s)
}
pub fn write_module(module: &footprint::Module, name: &Path) -> Result<(), KicadError> {
let s = footprint::module_to_string(module, 0)?;
write_file(&name, &s)
}
pub fn read_symbol_lib(name: &Path) -> Result<symbol_lib::SymbolLib, KicadError> {
match read_kicad_file(name, Expected::SymbolLib)? {
KicadFile::SymbolLib(mo) => Ok(mo),
x => str_error(format!("unexpected {} in {}", x, name.display())),
}
}
pub fn read_project(name: &Path) -> Result<project::Project, KicadError> {
match read_kicad_file(name, Expected::Project)? {
KicadFile::Project(mo) => Ok(mo),
x => str_error(format!("unexpected {} in {}", x, name.display())),
}
}
pub fn read_fp_lib_table(name: &Path) -> Result<fp_lib_table::FpLibTable, KicadError> {
match read_kicad_file(name, Expected::FpLibTable)? {
KicadFile::FpLibTable(mo) => Ok(mo),
x => str_error(format!("unexpected {} in {}", x, name.display())),
}
}
fn wrap<X, Y, F, G>(s: &Sexp, make: F, wrapper: G) -> result::Result<Y,SexpError>
where
F: Fn(&Sexp) -> result::Result<X,SexpError>,
G: Fn(X) -> Y,
{
Ok(wrapper(make(s)?))
}
#[derive(Debug)]
pub struct Bound {
pub x1: f64,
pub y1: f64,
pub x2: f64,
pub y2: f64,
pub is_bounded: bool,
}
impl Default for Bound {
fn default() -> Bound {
Bound {
x1: 0.0,
y1: 0.0,
x2: 0.0,
y2: 0.0,
is_bounded: false,
}
}
}
impl Bound {
pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Bound {
Bound {
x1: x1,
y1: y1,
x2: x2,
y2: y2,
is_bounded: true,
}
}
pub fn new_from_i64(x1: i64, y1: i64, x2: i64, y2: i64) -> Bound {
Bound {
x1: x1 as f64,
y1: y1 as f64,
x2: x2 as f64,
y2: y2 as f64,
is_bounded: true,
}
}
pub fn update(&mut self, other: &Bound) {
if other.is_bounded {
if !self.is_bounded {
self.is_bounded = true;
self.x1 = other.x1;
self.y1 = other.y1;
self.x2 = other.x2;
self.y2 = other.y2;
} else {
self.x1 = self.x1.min(other.x1);
self.y1 = self.y1.min(other.y1);
self.x2 = self.x2.max(other.x2);
self.y2 = self.y2.max(other.y2);
}
}
}
pub fn swap_if_needed(&mut self) {
if self.x1 > self.x2 {
std::mem::swap(&mut self.x1, &mut self.x2);
}
if self.y1 > self.y2 {
std::mem::swap(&mut self.y1, &mut self.y2);
}
}
pub fn width(&self) -> f64 {
(self.x1 - self.x2).abs()
}
pub fn height(&self) -> f64 {
(self.y1 - self.y2).abs()
}
}
pub trait BoundingBox {
fn bounding_box(&self) -> Bound;
}
pub trait Adjust {
fn adjust(&mut self, x: f64, y: f64);
}
pub fn reference_ord(r: &str) -> (char, i64) {
let c = r.chars().nth(0).unwrap();
let mut s = String::new();
for c in r.chars() {
if c >= '0' && c <= '9' {
s.push(c)
}
if c == ',' {
break;
}
}
let num = match s.parse::<i64>() {
Ok(n) => n,
Err(_) => 0,
};
(c, num)
}
pub mod error;
pub mod footprint;
pub mod schematic;
pub mod layout;
pub mod symbol_lib;
pub mod project;
pub mod fp_lib_table;
pub mod checkfix;
mod util;
mod formatter;