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
//! To generate Rust types from an XDR spec at build time, add `fastxdr` to your
//! `Cargo.toml`:
//!
//! ```toml
//! [build-dependencies]
//! # For the code generation
//! fastxdr = "1.0"
//!
//! [dependencies]
//! # Required dependencies of the generated code
//! thiserror = "1.0"
//! bytes = "0.5"
//! ```
//!
//! And add a `build.rs` at the crate root (not in `src`!):
//!
//! ```no_run
//! # std::env::set_var("OUT_DIR", "./");
//! fn main() {
//!     // Tell Cargo to regenerate the types if the XDR spec changes
//!     println!("cargo:rerun-if-changed=xdr_spec.x");
//!
//!     // Read from xdr_spec.x, writing the generated code to out.rs
//!     std::fs::write(
//!         std::path::Path::new(std::env::var("OUT_DIR").unwrap().as_str()).join("out.rs"),
//!         fastxdr::Generator::default()
//!             .generate(include_str!("xdr_spec.x"))
//!             .unwrap(),
//!     )
//!     .unwrap();
//! }
//! ```
//!
//! And then include the generated file in your application:
//!
//! ```compile_fail
//! // Where out.rs is the filename from above
//! include!(concat!(env!("OUT_DIR"), "/out.rs"));
//! use xdr::*;
//! ```
//!
//! To view the generated types, either export the generated types in your
//! application and use `cargo doc`, or use the CLI to produce the generated
//! code directly for reading.

#![allow(clippy::needless_doctest_main)]

pub mod ast;
pub mod impls;

use crate::impls::{print_impl_from, print_impl_wire_size, print_types, template};
use std::fmt::Write;

/// `DEFAULT_DERIVE` defines the default "derive" line prepended to type
/// declarations.
///
/// Custom "derive" lines can be used when generating Rust types with
/// [`Generator::with_derive()`](Generator::with_derive).
pub const DEFAULT_DERIVE: &str = "#[derive(Debug, PartialEq)]";

pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + 'static>>;

#[derive(Debug)]
pub struct Generator {
    derive: String,
}

impl std::default::Default for Generator {
    fn default() -> Self {
        Generator {
            derive: DEFAULT_DERIVE.to_string(),
        }
    }
}

impl Generator {
    pub fn with_derive<D: AsRef<str>>(self, derive: D) -> Self {
        Self {
            derive: derive.as_ref().to_string(),
        }
    }

    pub fn generate<T: AsRef<str>>(&self, xdr: T) -> Result<String> {
        // Create the AST
        let ast = crate::ast::Ast::new(xdr.as_ref())?;

        let mut out = String::new();

        // Print the file header
        writeln!(out, "{}", include_str!("header.rs"))?;

        // Generate the types
        print_types(&mut out, &ast, &self.derive.as_str())?;

        // Write the two from traits, one for Bytes and one for &mut Bytes
        print_impl_from(&mut out, template::bytes::Bytes, &ast)?;
        print_impl_from(&mut out, template::bytes::RefMutBytes, &ast)?;

        // Write the wire_size() implementations
        print_impl_wire_size(&mut out, template::bytes::Bytes, &ast)?;

        // End the header.rs with a closing }
        writeln!(out, "}}")?;

        Ok(out)
    }
}