ovsdb_build/
lib.rs

1//! `ovsdb-build` compiles OVSDB schema objects into rust entity definitions and proxies for use with
2//! [`ovsdb`].
3//!
4//! # Dependencies
5//!
6//! ```toml
7//! [dependencies]
8//! ovsdb = { version = <ovsdb-version>, features = ["client"] }
9//! serde = { version = <serde-version>, features = ["derive"] }
10//!
11//! [build-dependencies]
12//! ovsdb-build = <ovsdb-version>
13//! ```
14//!
15//! # Examples
16//!
17//! ```rust,no_run
18//! // build.rs
19//! fn main() -> Result<(), Box<dyn std::error::Error>> {
20//!     ovsdb_build::configure().compile("/path/to/vswitch.ovsschema", "vswitch");
21//!     Ok(())
22//! }
23//! ```
24//!
25//! [`ovsdb`]: https://docs.rs/ovsdb
26
27// Built-in Lints
28#![warn(
29    unreachable_pub,
30    missing_debug_implementations,
31    missing_copy_implementations,
32    elided_lifetimes_in_paths,
33    missing_docs
34)]
35// Clippy lints
36#![allow(
37    clippy::match_same_arms,
38    clippy::needless_doctest_main,
39    clippy::map_unwrap_or,
40    clippy::redundant_field_names,
41    clippy::type_complexity
42)]
43#![warn(
44    clippy::unwrap_used,
45    // clippy::print_stdout,
46    clippy::mut_mut,
47    clippy::non_ascii_literal,
48    clippy::similar_names,
49    clippy::unicode_not_nfc,
50    clippy::enum_glob_use,
51    clippy::if_not_else,
52    clippy::items_after_statements,
53    clippy::used_underscore_binding
54)]
55#![deny(unsafe_code)]
56
57use std::fs::File;
58use std::io::Write;
59use std::path::{Path, PathBuf};
60
61use convert_case::{Case, Casing};
62use ovsdb::schema::Schema;
63use quote::format_ident;
64
65mod attributes;
66mod entity;
67mod enumeration;
68mod field;
69use attributes::Attributes;
70use entity::Entity;
71use enumeration::Enumeration;
72use field::{Field, Kind};
73
74/// Error type for Schema and generation errors.
75#[derive(thiserror::Error, Debug)]
76pub enum Error {
77    /// General IO error
78    #[error("Unexpected IO Error")]
79    Io(#[from] std::io::Error),
80    /// Invalid code generation
81    #[error("Token parse error")]
82    Tokens(#[from] syn::Error),
83    /// OVSDB parsing error
84    #[error("Parsing error")]
85    OVSDB(#[from] ovsdb::Error),
86}
87
88/// Standard result for all build related methods.
89pub type Result<T> = std::result::Result<T, Error>;
90
91pub(crate) fn str_to_name<T>(str: T) -> String
92where
93    T: AsRef<str>,
94{
95    str.as_ref().to_case(Case::UpperCamel)
96}
97
98pub(crate) fn name_to_ident<T>(name: T) -> syn::Ident
99where
100    T: AsRef<str>,
101{
102    format_ident!("{}", name.as_ref())
103}
104
105/// Schema entity builder
106#[derive(Clone, Debug, Default)]
107pub struct Builder {
108    out_dir: Option<PathBuf>,
109}
110
111impl Builder {
112    fn new() -> Self {
113        Self::default()
114    }
115
116    fn generate_modules(&self, schema: &Schema, directory: &Path) -> Result<()> {
117        std::fs::create_dir_all(directory)?;
118
119        let mod_filename = directory.join("mod.rs");
120        let mut mod_file = File::create(mod_filename)?;
121        for table in schema.tables() {
122            let filename = directory.join(format!("{}.rs", table.name().to_case(Case::Snake)));
123            let entity = Entity::from_table(table);
124            entity.to_file(&filename)?;
125
126            mod_file.write_all(
127                format!(
128                    "mod {table_name};\npub use {table_name}::*;\n",
129                    table_name = &table.name().to_case(Case::Snake)
130                )
131                .as_bytes(),
132            )?;
133        }
134        Ok(())
135    }
136
137    /// Compile the `.ovsschema` file into rust objects.
138    pub fn compile<P>(self, schema_file: P, module: P) -> Result<()>
139    where
140        P: AsRef<Path>,
141    {
142        let mut output_dir = match &self.out_dir {
143            Some(dir) => dir.to_path_buf(),
144            None => match std::env::var("OUT_DIR") {
145                Ok(val) => PathBuf::from(val),
146                Err(_) => todo!(),
147            },
148        };
149
150        let schema = ovsdb::schema::Schema::from_file(schema_file)?;
151
152        output_dir.push(module);
153
154        self.generate_modules(&schema, &output_dir)
155    }
156}
157
158/// Configure `ovsdb-build` code generation options.
159pub fn configure() -> Builder {
160    Builder::new()
161}