1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Provides function to deal with Rust syntax.
#![deny(warnings)]
#![warn(missing_docs)]

pub mod ts;

use std::{
    fs::File,
    io::{Read, Write},
    path::Path,
};
use syn::{
    Attribute, FnArg, ImplItemMethod, Lit, Meta, MetaList, MetaNameValue, NestedMeta, Visibility,
};

/// Defines the `Args` to be used in binaries.
#[macro_export]
macro_rules! Args {
    ($bin_name:expr) => {
        #[derive(clap::Clap)]
        #[clap(name = $bin_name, version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"))]
        #[clap(setting = clap::AppSettings::ColoredHelp)]
        struct Args {
            /// Sets the time (any format) for generated output.
            #[clap(long = "now")]
            now: Option<String>,

            #[clap()]
            files: Vec<String>,
        }
        impl Args {
            fn now(&mut self) -> String {
                if self.now.is_none() {
                    self.now = Some(Utc::now().to_string());
                }
                self.now.clone().unwrap()
            }
        }
    }
}

/// Returns the Rust syntax tree for the given `file_name` path.
/// Panics if the file cannot be open or the file has syntax errors.
pub fn parse_rust<S: AsRef<Path>>(file_name: S) -> syn::File {
    let mut file = File::open(file_name).expect("Unable to open file");
    let mut src = String::new();
    file.read_to_string(&mut src).expect("Unable to read file");

    syn::parse_file(&src).expect("Unable to parse file")
}

/// Joins segments of path by `::`.
pub fn join_path(path: &syn::Path) -> String {
    path.segments
        .iter()
        .map(|seg| seg.ident.to_string())
        .collect::<Vec<String>>()
        .join("::")
}

/// Returns `true` if the `method` is explicitly marked as `pub`.
/// Returns `false` otherwise.
pub fn is_public(method: &ImplItemMethod) -> bool {
    match method.vis {
        Visibility::Public(_) => true,
        _ => false,
    }
}

/// Returns `true` if `attrs` contain `attr_name`.
/// Returns `false` otherwise.
pub fn has_attr(attrs: &Vec<Attribute>, attr_name: &str) -> bool {
    for attr in attrs {
        if attr.path.is_ident(attr_name) {
            return true;
        }
    }
    false
}

/// Returns whether the given `method` is marked as `payable`. 
pub fn is_payable(method: &ImplItemMethod) -> bool {
    has_attr(&method.attrs, "payable")
}

/// Returns `true` if any of the attributes under item derive from `macro_name`.
/// Returns `false` otherwise.
pub fn derives(attrs: &Vec<Attribute>, macro_name: &str) -> bool {
    for attr in attrs {
        if attr.path.is_ident("derive") {
            if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
                for elem in nested {
                    if let NestedMeta::Meta(meta) = elem {
                        if meta.path().is_ident(macro_name) {
                            return true;
                        }
                    }
                }
            } else {
                panic!("not expected");
            }
        }
    }
    false
}

/// Returns `true` if `method` is declared as `mut`.
pub fn is_mut(method: &ImplItemMethod) -> bool {
    if let Some(FnArg::Receiver(r)) = method.sig.inputs.iter().next() {
        r.mutability.is_some()
    } else {
        false
    }
}

/// Writes Rust `doc` comments to `file`.
/// Each line of `doc` is prefixed with `prefix`.
pub fn write_docs<W: Write, F: Fn(String) -> String>(
    file: &mut W,
    attrs: &Vec<Attribute>,
    mapf: F,
) {
    for attr in attrs {
        if attr.path.is_ident("doc") {
            if let Ok(Meta::NameValue(MetaNameValue {
                lit: Lit::Str(lit), ..
            })) = attr.parse_meta()
            {
                writeln!(file, "{}", mapf(lit.value())).unwrap();
            } else {
                panic!("not expected");
            }
        }
    }
}