use std::fmt::{self, Write};
use std::fs;
use std::path::Path;
use crate::{
Class, Comment, Doc, Enum, Formatter, Function, IfDef, Include, Macro, Struct, Type, Union,
Variable,
};
#[derive(Debug, Clone)]
pub enum Item {
Comment(Comment),
Enum(Enum),
IfDef(IfDef),
Include(Include),
Macro(Macro),
Struct(Struct),
Union(Union),
Function(Function),
Class(Class),
Variable(Variable),
TypeDef(Type, String),
NewLine,
}
#[derive(Debug, Clone)]
pub struct Scope {
doc: Option<Doc>,
items: Vec<Item>,
file: Option<String>,
}
impl Scope {
pub fn new() -> Self {
Scope {
doc: None,
items: Vec::new(),
file: None,
}
}
pub fn push_doc_str(&mut self, doc: &str) -> &mut Self {
if let Some(d) = &mut self.doc {
d.add_text(doc);
} else {
self.doc = Some(Doc::with_str(doc));
}
self
}
pub fn push_empty_line(&mut self) -> &mut Self {
self.items.push(Item::NewLine);
self
}
pub fn doc(&mut self, doc: Doc) -> &mut Self {
self.doc = Some(doc);
self
}
pub fn set_filename(&mut self, file: &str) -> &mut Self {
self.file = Some(String::from(file));
self
}
pub fn new_comment(&mut self, comment: &str) -> &mut Comment {
self.push_comment(Comment::with_str(comment));
match *self.items.last_mut().unwrap() {
Item::Comment(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_comment(&mut self, comment: Comment) -> &mut Self {
self.items.push(Item::Comment(comment));
self
}
pub fn new_include(&mut self, inc: &str, system: bool) -> &mut Include {
if system {
self.push_include(Include::new_system(inc));
} else {
self.push_include(Include::new(inc));
}
match *self.items.last_mut().unwrap() {
Item::Include(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_include(&mut self, inc: Include) -> &mut Self {
self.items.push(Item::Include(inc));
self
}
pub fn new_enum(&mut self, name: &str) -> &mut Enum {
self.push_enum(Enum::new(name));
match *self.items.last_mut().unwrap() {
Item::Enum(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_enum(&mut self, def: Enum) -> &mut Self {
self.items.push(Item::Enum(def));
self
}
pub fn new_struct(&mut self, name: &str) -> &mut Struct {
self.push_struct(Struct::new(name));
match *self.items.last_mut().unwrap() {
Item::Struct(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_struct(&mut self, inc: Struct) -> &mut Self {
self.items.push(Item::Struct(inc));
self
}
pub fn new_class(&mut self, name: &str) -> &mut Class {
self.push_class(Class::new(name));
match *self.items.last_mut().unwrap() {
Item::Class(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_class(&mut self, c: Class) -> &mut Self {
self.items.push(Item::Class(c));
self
}
pub fn new_union(&mut self, name: &str) -> &mut Union {
self.push_union(Union::new(name));
match *self.items.last_mut().unwrap() {
Item::Union(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_union(&mut self, c: Union) -> &mut Self {
self.items.push(Item::Union(c));
self
}
pub fn new_function(&mut self, name: &str, ty: Type) -> &mut Function {
self.push_function(Function::new(name, ty));
match *self.items.last_mut().unwrap() {
Item::Function(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_function(&mut self, c: Function) -> &mut Self {
self.items.push(Item::Function(c));
self
}
pub fn new_macro(&mut self, name: &str) -> &mut Macro {
self.push_macro(Macro::new(name));
match *self.items.last_mut().unwrap() {
Item::Macro(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_macro(&mut self, mac: Macro) -> &mut Self {
self.items.push(Item::Macro(mac));
self
}
pub fn new_variable(&mut self, name: &str, ty: Type) -> &mut Variable {
self.push_variable(Variable::new(name, ty));
match *self.items.last_mut().unwrap() {
Item::Variable(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_variable(&mut self, mac: Variable) -> &mut Self {
self.items.push(Item::Variable(mac));
self
}
pub fn new_ifdef(&mut self, sym: &str) -> &mut IfDef {
self.push_ifdef(IfDef::new(sym));
match *self.items.last_mut().unwrap() {
Item::IfDef(ref mut v) => v,
_ => unreachable!(),
}
}
pub fn push_ifdef(&mut self, ifdef: IfDef) -> &mut Self {
self.items.push(Item::IfDef(ifdef));
self
}
pub fn new_typedef(&mut self, name: &str, ty: Type) -> &mut Self {
self.items.push(Item::TypeDef(ty, String::from(name)));
self
}
pub fn do_fmt(&self, fmt: &mut Formatter<'_>, only_decls: bool) -> fmt::Result {
self.doc.as_ref().map(|d| d.fmt(fmt));
writeln!(fmt)?;
for item in self.items.iter() {
writeln!(fmt)?;
match &item {
Item::Comment(v) => v.fmt(fmt)?,
Item::Include(v) => v.fmt(fmt)?,
Item::Struct(v) => v.fmt(fmt)?,
Item::Macro(v) => v.fmt(fmt)?,
Item::Enum(v) => v.fmt(fmt)?,
Item::Variable(v) => v.fmt(fmt)?,
Item::IfDef(v) => v.do_fmt(fmt, only_decls)?,
Item::Union(v) => v.fmt(fmt)?,
Item::Function(v) => v.do_fmt(fmt, only_decls)?,
Item::Class(v) => v.do_fmt(fmt, only_decls)?,
Item::TypeDef(ty, name) => {
writeln!(fmt, "typedef {ty} {name};")?;
}
Item::NewLine => writeln!(fmt)?,
}
}
Ok(())
}
pub fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
self.do_fmt(fmt, true)
}
pub fn to_file(&self, path: &Path, only_decls: bool) -> std::io::Result<()> {
let file = if let Some(f) = &self.file {
path.join(f.as_str())
} else {
path.join("file.c")
};
let mut ret = String::new();
self.do_fmt(&mut Formatter::new(&mut ret), only_decls).unwrap();
fs::write(file, ret.as_bytes())
}
}
impl Default for Scope {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for Scope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ret = String::new();
self.fmt(&mut Formatter::new(&mut ret)).unwrap();
if ret.as_bytes().last() == Some(&b'\n') {
ret.pop();
}
write!(f, "{ret}")
}
}