use anyhow::Result;
use include_dir::Dir;
use std::path::{Path, PathBuf};
pub struct ProtoDir<'a> {
pub dir: Dir<'a>,
}
impl<'a> ProtoDir<'a> {
pub fn extract(&self, out_dir: &Path) -> Result<ExtractedProtoDir> {
let proto_path = out_dir.join("proto");
self.dir.extract(&proto_path)?;
Ok(ExtractedProtoDir {
path: proto_path,
files: self
.dir
.find("**/*.proto")?
.map(|f| f.path().to_path_buf())
.collect::<Vec<_>>(),
})
}
}
impl<'a> AsRef<Dir<'a>> for ProtoDir<'a> {
fn as_ref(&self) -> &Dir<'a> {
&self.dir
}
}
pub struct ExtractedProtoDir {
path: PathBuf,
files: Vec<PathBuf>,
}
impl ExtractedProtoDir {
pub fn to_glob(&self) -> String {
format!("{}/**", self.path.display())
}
pub fn as_path(&self) -> &Path {
self.path.as_path()
}
pub fn protos(&self) -> &[PathBuf] {
&self.files
}
}
#[macro_export]
macro_rules! include_proto_dir {
($path:tt) => {
$crate::ProtoDir {
dir: include_dir::include_dir!($path),
}
};
}
#[macro_export]
macro_rules! include_proto_dir_default {
() => {
pub const PROTO_DIR: $crate::ProtoDir =
$crate::include_proto_dir!("$CARGO_MANIFEST_DIR/proto");
};
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
fn create_mock_proto_dir() -> ProtoDir<'static> {
ProtoDir {
dir: include_dir::include_dir!("proto"),
}
}
#[test]
fn test_extract() -> Result<()> {
let proto_dir = create_mock_proto_dir();
let tmp_dir = tempdir()?;
let extracted_proto_dir = proto_dir.extract(tmp_dir.path())?;
assert!(extracted_proto_dir.as_path().exists());
let expected_proto_path = extracted_proto_dir.as_path().join("foo/v1/foo.proto");
assert!(expected_proto_path.exists());
Ok(())
}
#[test]
fn test_to_glob() -> Result<()> {
let proto_dir = create_mock_proto_dir();
let tmp_dir = tempdir()?;
let extracted_proto_dir = proto_dir.extract(tmp_dir.path())?;
let glob_pattern = extracted_proto_dir.to_glob();
let expected_pattern = format!("{}/**", extracted_proto_dir.as_path().display());
assert_eq!(glob_pattern, expected_pattern);
Ok(())
}
#[test]
fn test_as_path() -> Result<()> {
let proto_dir = create_mock_proto_dir();
let tmp_dir = tempdir()?;
let extracted_proto_dir = proto_dir.extract(tmp_dir.path())?;
assert_eq!(extracted_proto_dir.as_path(), &tmp_dir.path().join("proto"));
Ok(())
}
#[test]
fn test_protos() -> Result<()> {
let proto_dir = create_mock_proto_dir();
let tmp_dir = tempdir()?;
let extracted_proto_dir = proto_dir.extract(tmp_dir.path())?;
let protos = extracted_proto_dir.protos();
assert!(!protos.is_empty());
for proto in protos {
assert_eq!(proto.extension().and_then(|s| s.to_str()), Some("proto"));
}
Ok(())
}
#[test]
fn test_macro_include_proto_dir() -> Result<()> {
const PROTO_DIR: ProtoDir = include_proto_dir!("proto");
let tmp_dir = tempdir()?;
let extracted_proto_dir = PROTO_DIR.extract(tmp_dir.path())?;
let expected_proto_path = extracted_proto_dir.as_path().join("foo/v1/foo.proto");
assert!(expected_proto_path.exists());
Ok(())
}
#[test]
fn test_macro_include_proto_dir_default() -> Result<()> {
include_proto_dir_default!();
let tmp_dir = tempdir()?;
let extracted_proto_dir = PROTO_DIR.extract(tmp_dir.path())?;
let expected_proto_path = extracted_proto_dir.as_path().join("foo/v1/foo.proto");
assert!(expected_proto_path.exists());
Ok(())
}
}