use crate::{kw, utils::DebugPunctuated, Item, Modifier, SolIdent, Spanned, Type};
use proc_macro2::Span;
use std::{cmp::Ordering, fmt};
use syn::{
braced,
parse::{Lookahead1, Parse, ParseStream},
punctuated::Punctuated,
token::Brace,
Attribute, Error, Result, Token,
};
#[derive(Clone)]
pub struct ItemContract {
pub attrs: Vec<Attribute>,
pub kind: ContractKind,
pub name: SolIdent,
pub inheritance: Option<Inheritance>,
pub brace_token: Brace,
pub body: Vec<Item>,
}
impl fmt::Display for ItemContract {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.kind, self.name)?;
if let Some(inheritance) = &self.inheritance {
write!(f, " {inheritance}")?;
}
let s = self
.body
.iter()
.map(|item| item.to_string())
.collect::<Vec<_>>()
.join("\n")
.replace('\n', "\n ");
write!(f, " {{\n {s}\n}}")
}
}
impl fmt::Debug for ItemContract {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ItemContract")
.field("attrs", &self.attrs)
.field("kind", &self.kind)
.field("name", &self.name)
.field("inheritance", &self.inheritance)
.field("body", &self.body)
.finish()
}
}
impl Parse for ItemContract {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let kind;
let content;
Ok(Self {
attrs: input.call(Attribute::parse_outer)?,
kind: {
kind = input.parse()?;
kind
},
name: input.parse()?,
inheritance: {
if input.peek(kw::is) {
if kind.is_library() {
return Err(input.error("libraries are not allowed to inherit"));
}
Some(input.parse()?)
} else {
None
}
},
brace_token: braced!(content in input),
body: {
let mut body = Vec::new();
while !content.is_empty() {
let item: Item = content.parse()?;
if matches!(item, Item::Contract(_)) {
return Err(Error::new(item.span(), "cannot declare nested contracts"));
}
body.push(item);
}
body
},
})
}
}
impl Spanned for ItemContract {
fn span(&self) -> Span {
self.name.span()
}
fn set_span(&mut self, span: Span) {
self.name.set_span(span);
}
}
impl ItemContract {
pub fn as_type(&self) -> Type {
Type::Address(self.span(), None)
}
pub fn is_abstract_contract(&self) -> bool {
self.kind.is_abstract_contract()
}
pub fn is_contract(&self) -> bool {
self.kind.is_contract()
}
pub fn is_interface(&self) -> bool {
self.kind.is_interface()
}
pub fn is_library(&self) -> bool {
self.kind.is_library()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum ContractKind {
AbstractContract(Token![abstract], kw::contract),
Contract(kw::contract),
Interface(kw::interface),
Library(kw::library),
}
impl fmt::Display for ContractKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl fmt::Debug for ContractKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_debug_str())
}
}
impl PartialOrd for ContractKind {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ContractKind {
fn cmp(&self, other: &Self) -> Ordering {
self.idx().cmp(&other.idx())
}
}
impl Parse for ContractKind {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![abstract]) {
Ok(Self::AbstractContract(input.parse()?, input.parse()?))
} else if lookahead.peek(kw::contract) {
input.parse().map(Self::Contract)
} else if lookahead.peek(kw::interface) {
input.parse().map(Self::Interface)
} else if lookahead.peek(kw::library) {
input.parse().map(Self::Library)
} else {
Err(lookahead.error())
}
}
}
impl Spanned for ContractKind {
fn span(&self) -> Span {
match self {
Self::AbstractContract(kw_abstract, kw_contract) => {
let span = kw_abstract.span;
span.join(kw_contract.span).unwrap_or(span)
}
Self::Contract(kw) => kw.span,
Self::Interface(kw) => kw.span,
Self::Library(kw) => kw.span,
}
}
fn set_span(&mut self, span: Span) {
match self {
Self::AbstractContract(kw_abstract, kw_contract) => {
kw_abstract.span = span;
kw_contract.span = span;
}
Self::Contract(kw) => kw.span = span,
Self::Interface(kw) => kw.span = span,
Self::Library(kw) => kw.span = span,
}
}
}
impl ContractKind {
pub fn peek(lookahead: &Lookahead1<'_>) -> bool {
lookahead.peek(Token![abstract])
|| lookahead.peek(kw::contract)
|| lookahead.peek(kw::interface)
|| lookahead.peek(kw::library)
}
pub fn is_abstract_contract(self) -> bool {
matches!(self, Self::AbstractContract(..))
}
pub fn is_contract(self) -> bool {
matches!(self, Self::Contract(_))
}
pub fn is_interface(self) -> bool {
matches!(self, Self::Interface(_))
}
pub fn is_library(self) -> bool {
matches!(self, Self::Library(_))
}
pub const fn as_debug_str(self) -> &'static str {
match self {
Self::AbstractContract(..) => "AbstractContract",
Self::Contract(_) => "Contract",
Self::Interface(_) => "Interface",
Self::Library(_) => "Library",
}
}
pub const fn as_str(self) -> &'static str {
match self {
Self::AbstractContract(..) => "abstract contract",
Self::Contract(_) => "contract",
Self::Interface(_) => "interface",
Self::Library(_) => "library",
}
}
fn idx(&self) -> usize {
match self {
Self::AbstractContract(..) => 0,
Self::Contract(_) => 1,
Self::Interface(_) => 2,
Self::Library(_) => 3,
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Inheritance {
pub is_token: kw::is,
pub inheritance: Punctuated<Modifier, Token![,]>,
}
impl fmt::Display for Inheritance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("is ")?;
for (i, modifier) in self.inheritance.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
modifier.fmt(f)?;
}
Ok(())
}
}
impl fmt::Debug for Inheritance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Inheritance").field(DebugPunctuated::new(&self.inheritance)).finish()
}
}
impl Parse for Inheritance {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let is_token = input.parse()?;
let mut inheritance = Punctuated::new();
loop {
if input.is_empty() || input.peek(Brace) {
break;
}
inheritance.push_value(input.parse()?);
if input.is_empty() || input.peek(Brace) {
break;
}
inheritance.push_punct(input.parse()?);
}
if inheritance.is_empty() {
Err(input.parse::<SolIdent>().unwrap_err())
} else {
Ok(Self { is_token, inheritance })
}
}
}
impl Spanned for Inheritance {
fn span(&self) -> Span {
let span = self.is_token.span;
self.inheritance.last().and_then(|last| span.join(last.span())).unwrap_or(span)
}
fn set_span(&mut self, span: Span) {
self.is_token.span = span;
}
}