use std::fmt::{self, Display, Write};
use crate::{Block, Doc, Formatter, FunctionParam, Type};
#[derive(Debug, Clone)]
pub struct Function {
name: String,
doc: Option<Doc>,
params: Vec<FunctionParam>,
ret: Type,
attributes: Vec<String>,
is_static: bool,
is_inline: bool,
is_extern: bool,
body: Block,
}
impl Function {
pub fn new(name: &str, ret: Type) -> Self {
Self::with_string(String::from(name), ret)
}
pub fn with_string(name: String, ret: Type) -> Self {
Self {
name,
doc: None,
params: Vec::new(),
ret,
attributes: Vec::new(),
is_static: false,
is_inline: false,
is_extern: false,
body: Block::new(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn to_type(&self) -> Type {
panic!("needs to implement a corresponding type.")
}
pub fn ret_type(&self) -> &Type {
&self.ret
}
pub fn doc(&mut self, doc: Doc) -> &mut Self {
self.doc = Some(doc);
self
}
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 new_param(&mut self, name: &str, ty: Type) -> &mut FunctionParam {
self.params.push(FunctionParam::new(name, ty));
self.params.last_mut().unwrap()
}
pub fn push_param(&mut self, item: FunctionParam) -> &mut Self {
self.params.push(item);
self
}
pub fn param_by_name(&self, name: &str) -> Option<&FunctionParam> {
self.params.iter().find(|f| f.name() == name)
}
pub fn param_by_name_mut(&mut self, name: &str) -> Option<&mut FunctionParam> {
self.params.iter_mut().find(|f| f.name() == name)
}
pub fn param_by_idx(&self, idx: usize) -> Option<&FunctionParam> {
self.params.get(idx)
}
pub fn param_by_idx_mut(&mut self, idx: usize) -> Option<&mut FunctionParam> {
self.params.get_mut(idx)
}
pub fn push_attribute(&mut self, attr: &str) -> &mut Self {
self.attributes.push(String::from(attr));
self
}
pub fn toggle_static(&mut self, val: bool) -> &mut Self {
if val {
self.is_extern = false;
}
self.is_static = val;
self
}
pub fn set_static(&mut self) -> &mut Self {
self.toggle_static(true)
}
pub fn toggle_inline(&mut self, val: bool) -> &mut Self {
if val {
self.is_extern = false;
}
self.is_inline = true;
self
}
pub fn set_inline(&mut self) -> &mut Self {
self.toggle_inline(true)
}
pub fn toggle_extern(&mut self, val: bool) -> &mut Self {
if val {
self.is_inline = false;
self.is_extern = false;
}
self.is_extern = val;
self
}
pub fn set_extern(&mut self) -> &mut Self {
self.toggle_extern(true)
}
pub fn set_body(&mut self, body: Block) -> &mut Self {
if !body.is_empty() {
self.is_extern = false;
}
self.body = body;
self
}
pub fn body(&mut self) -> &mut Block {
&mut self.body
}
pub fn do_fmt(&self, fmt: &mut Formatter<'_>, decl_only: bool) -> fmt::Result {
if let Some(ref docs) = self.doc {
docs.fmt(fmt)?;
}
if self.body.is_empty() && self.is_extern {
write!(fmt, "extern ")?;
}
if self.is_static {
write!(fmt, "static ")?;
}
if self.is_inline {
write!(fmt, "inline ")?;
}
self.ret.fmt(fmt)?;
write!(fmt, " {}(", self.name)?;
if self.params.is_empty() {
write!(fmt, "void")?;
} else {
for (i, f) in self.params.iter().enumerate() {
if i != 0 {
write!(fmt, ", ")?;
}
f.fmt(fmt)?;
}
}
write!(fmt, ")")?;
if !self.attributes.is_empty() {
write!(fmt, "__attribute__() // TODO")?;
}
if !self.body.is_empty() && (!decl_only || self.is_inline) {
fmt.block(|fmt| self.body.fmt(fmt))?;
writeln!(fmt)
} else {
writeln!(fmt, ";")
}
}
pub fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
self.do_fmt(fmt, false)
}
pub fn fmt_decl(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
self.do_fmt(fmt, true)
}
pub fn fmt_def(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
if self.is_inline {
return Ok(());
}
self.do_fmt(fmt, false)
}
}
impl Display for Function {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ret = String::new();
self.fmt(&mut Formatter::new(&mut ret)).unwrap();
write!(f, "{ret}")
}
}