mod chain;
mod descriptor_set;
mod google;
mod include;
#[cfg(test)]
mod tests;
pub use chain::ChainFileResolver;
pub use descriptor_set::DescriptorSetFileResolver;
pub use google::GoogleFileResolver;
pub use include::IncludeFileResolver;
use prost_types::FileDescriptorProto;
use std::{
fs,
io::{self, Read},
path::{Path, PathBuf},
};
use bytes::{Buf, Bytes};
pub(crate) use include::{check_shadow, path_to_file_name};
use prost::{DecodeError, Message};
use crate::error::{Error, ErrorKind};
const MAX_FILE_LEN: u64 = i32::MAX as u64;
pub trait FileResolver {
fn resolve_path(&self, _path: &Path) -> Option<String> {
None
}
fn open_file(&self, name: &str) -> Result<File, Error>;
}
impl<T> FileResolver for Box<T>
where
T: FileResolver + ?Sized,
{
fn resolve_path(&self, path: &Path) -> Option<String> {
(**self).resolve_path(path)
}
fn open_file(&self, name: &str) -> Result<File, Error> {
(**self).open_file(name)
}
}
#[derive(Debug, Clone)]
pub struct File {
pub(crate) path: Option<PathBuf>,
pub(crate) source: Option<String>,
pub(crate) descriptor: FileDescriptorProto,
pub(crate) encoded: Option<Bytes>,
}
#[derive(Debug, Clone)]
pub struct FileMetadata {
pub(crate) name: String,
pub(crate) path: Option<PathBuf>,
pub(crate) is_import: bool,
}
impl File {
pub fn open(name: &str, path: &Path) -> Result<Self, Error> {
let map_io_err = |err: io::Error| -> Error {
match err.kind() {
io::ErrorKind::NotFound => Error::file_not_found(name),
io::ErrorKind::InvalidData => Error::from_kind(ErrorKind::FileInvalidUtf8 {
name: name.to_owned(),
}),
_ => Error::from_kind(ErrorKind::OpenFile {
name: name.to_owned(),
path: path.to_owned(),
err,
}),
}
};
let file = fs::File::open(path).map_err(map_io_err)?;
let metadata = file.metadata().map_err(map_io_err)?;
if metadata.len() > MAX_FILE_LEN {
return Err(Error::from_kind(ErrorKind::FileTooLarge {
name: name.to_owned(),
}));
}
let mut buf = String::with_capacity(metadata.len() as usize);
file.take(MAX_FILE_LEN)
.read_to_string(&mut buf)
.map_err(map_io_err)?;
let descriptor = protox_parse::parse(name, &buf)?;
Ok(File {
path: Some(path.to_owned()),
source: Some(buf),
descriptor,
encoded: None,
})
}
pub fn from_source(name: &str, source: &str) -> Result<Self, Error> {
let descriptor = protox_parse::parse(name, source)?;
Ok(File {
path: None,
source: Some(source.to_owned()),
descriptor,
encoded: None,
})
}
pub fn from_file_descriptor_proto(file: prost_types::FileDescriptorProto) -> Self {
File {
path: None,
source: None,
descriptor: file,
encoded: None,
}
}
pub fn decode_file_descriptor_proto<B>(mut buf: B) -> Result<Self, DecodeError>
where
B: Buf,
{
let encoded = buf.copy_to_bytes(buf.remaining());
Ok(File {
path: None,
source: None,
descriptor: FileDescriptorProto::decode(encoded.as_ref())?,
encoded: Some(encoded),
})
}
pub fn name(&self) -> &str {
self.descriptor.name()
}
pub fn path(&self) -> Option<&Path> {
self.path.as_deref()
}
pub fn source(&self) -> Option<&str> {
self.source.as_deref()
}
pub fn file_descriptor_proto(&self) -> &FileDescriptorProto {
&self.descriptor
}
}
impl FileMetadata {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn path(&self) -> Option<&Path> {
self.path.as_deref()
}
pub fn is_import(&self) -> bool {
self.is_import
}
}
impl From<FileDescriptorProto> for File {
fn from(file: FileDescriptorProto) -> Self {
File::from_file_descriptor_proto(file)
}
}
impl From<File> for FileDescriptorProto {
fn from(file: File) -> Self {
file.descriptor
}
}