use std::ops::RangeInclusive;
use std::path::PathBuf;
use indexmap::IndexMap;
use proc_macro2::Span;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::Ident;
use syn::LitBool;
use syn::LitFloat;
use syn::LitInt;
use syn::LitStr;
use syn::Token;
use syn_prelude::ToIdent;
use syn_prelude::ToLitStr;
use crate::dep::Deps;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct ProtobufPath {
pub segments: Punctuated<Ident, Token![.]>,
}
impl ProtobufPath {
pub fn from_ident(name: Ident) -> Self {
let mut slf = Self {
segments: Punctuated::new(),
};
slf.segments.push(name);
slf
}
pub fn new_empty(span: Span) -> Self {
let mut segments = Punctuated::new();
segments.push_value(("google", span).to_ident());
segments.push_punct(Token);
segments.push_value(("protobuf", span).to_ident());
segments.push_punct(Token);
segments.push(("Empty", span).to_ident());
Self { segments }
}
}
impl ProtobufPath {
pub fn local_name(&self) -> &Ident {
if let Some(last) = self.segments.last() {
return last;
} else {
unreachable!()
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Syntax {
Proto2(Span),
Proto3(Option<Span>),
}
impl Syntax {
pub fn version(&self) -> usize {
match self {
Self::Proto2(_) => 2,
Self::Proto3(_) => 3,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Modifier {
Optional,
Repeated,
Required,
}
#[derive(Debug, Clone)]
pub struct Group {
pub name: Ident,
pub fields: Vec<Field>,
}
#[derive(Debug, Clone)]
pub enum FieldType {
Int32(Span),
Int64(Span),
Uint32(Span),
Uint64(Span),
Sint32(Span),
Sint64(Span),
Bool(Span),
Fixed64(Span),
Sfixed64(Span),
Double(Span),
String(Span),
Bytes(Span),
Fixed32(Span),
Sfixed32(Span),
Float(Span),
MessageOrEnum(Type),
Map(MapType),
Group(Group),
}
#[derive(Debug, Clone)]
pub struct MapType {
pub span: Span,
pub key: Box<FieldType>,
pub value: Box<FieldType>,
}
impl FieldType {
#[allow(unused)]
pub fn span(&self) -> Span {
match self {
Self::Int32(span) => *span,
Self::Int64(span) => *span,
Self::Uint32(span) => *span,
Self::Uint64(span) => *span,
Self::Sint32(span) => *span,
Self::Sint64(span) => *span,
Self::Bool(span) => *span,
Self::Fixed64(span) => *span,
Self::Sfixed64(span) => *span,
Self::Double(span) => *span,
Self::String(span) => *span,
Self::Bytes(span) => *span,
Self::Fixed32(span) => *span,
Self::Sfixed32(span) => *span,
Self::Float(span) => *span,
Self::MessageOrEnum(t) => t.type_path.span(),
Self::Map(map) => map.span,
Self::Group(group) => group.name.span(),
}
}
pub fn is_message(&self) -> bool {
match self {
Self::MessageOrEnum(t) => t.target_is_message,
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct Type {
pub type_path: ProtobufPath,
pub target_is_message: bool,
pub ty: syn::Type,
}
#[derive(Debug, Clone)]
pub enum TagValue {
Value(Span, i32),
AutoIncr,
}
impl ToLitStr for TagValue {
fn to_lit_str(&self) -> LitStr {
if let Self::Value(span, value) = self {
LitStr::new(&format!("{}", value), *span)
} else {
unreachable!()
}
}
}
#[derive(Debug, Clone)]
pub struct Field {
pub name: Ident,
pub field_name: Ident,
pub modifier: Option<Modifier>,
pub typ: FieldType,
pub tag: TagValue,
pub options: Vec<ProtobufOption>,
pub enum_field: bool,
}
#[derive(Debug, Clone)]
pub enum MessageElement {
Field(Field),
OneOf(OneOf),
}
#[derive(Debug, Clone, Copy)]
pub enum NestedTypeIndex {
Message(usize),
Enum(usize),
Oneof(usize),
}
#[derive(Debug, Clone)]
pub struct Message {
pub name: Ident,
pub struct_name: Ident,
pub nested_mod_name: Option<Ident>,
pub fields: Vec<MessageElement>,
pub reserved_nums: Vec<RangeInclusive<i32>>,
pub reserved_names: Vec<Ident>,
pub messages: Vec<Message>,
pub enums: Vec<Enumeration>,
pub options: Vec<ProtobufOption>,
pub extension_ranges: Vec<RangeInclusive<i32>>,
pub extensions: Vec<Extension>,
pub nested_types: Vec<NestedTypeIndex>,
}
#[derive(Debug, Clone)]
pub struct EnumValue {
pub name: Ident,
pub variant_name: Ident,
pub proto_name: LitStr,
pub tag: Option<LitInt>,
pub tag_value: i32,
pub options: Option<Vec<ProtobufOption>>,
}
#[derive(Debug, Clone)]
pub struct Enumeration {
pub nested_mod_name: Option<Ident>,
pub name: Ident,
pub values: Vec<EnumValue>,
pub options: Vec<ProtobufOption>,
pub reserved_nums: Vec<RangeInclusive<i32>>,
pub reserved_names: Vec<Ident>,
}
#[derive(Debug, Clone)]
pub struct OneOf {
pub name: Ident,
pub field_name: Ident,
pub nested_mod_name: Ident,
pub field_lit: LitStr,
pub enum_name: Ident,
pub tags: LitStr,
pub fields: Vec<Field>,
pub options: Vec<ProtobufOption>,
}
#[derive(Debug, Clone)]
pub struct Extension {
pub extendee: ProtobufPath,
pub field: Field,
}
#[derive(Debug, Clone)]
pub struct Method {
pub name: Ident,
pub method_name: Ident,
pub input_message: Option<Message>,
pub input_type: Type,
pub output_message: Option<Message>,
pub output_type: Type,
#[allow(dead_code)] pub client_streaming: Option<Span>,
#[allow(dead_code)] pub server_streaming: Option<Span>,
pub options: Punctuated<ProtobufOption, Token![;]>,
}
#[derive(Debug, Clone)]
pub struct Service {
pub name: Ident,
pub code_name: Ident,
pub methods: Vec<Method>,
pub options: Vec<ProtobufOption>,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct AnyTypeUrl {
pub prefix: ProtobufPath,
pub full_type_name: ProtobufPath,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum ProtobufConstantMessageFieldName {
Regular(Ident),
Extension(ProtobufPath),
AnyTypeUrl(AnyTypeUrl),
}
#[derive(Debug, Clone)]
pub struct ProtobufConstantMessage {
pub fields: IndexMap<ProtobufConstantMessageFieldName, ProtobufConstant>,
}
#[derive(Debug, Clone)]
#[allow(unused)]
pub enum ProtobufConstant {
U64(LitInt, bool),
F64(LitFloat, bool),
Bool(LitBool),
Ident(ProtobufPath),
String(LitStr),
Message(ProtobufConstantMessage),
}
#[derive(Debug, Clone)]
pub enum ProtobufOptionNamePart {
Direct(Ident),
Ext(ProtobufPath),
}
#[derive(Debug, Clone)]
pub struct ProtobufOptionNameExt(pub Vec<ProtobufOptionNamePart>);
#[derive(Debug, Clone)]
pub enum ProtobufOptionName {
Builtin(Ident),
Ext(ProtobufOptionNameExt),
}
impl ProtobufOptionName {
pub fn is_option(&self, opt_name: &str) -> bool {
match self {
ProtobufOptionName::Builtin(name) => name.eq(opt_name),
ProtobufOptionName::Ext(ext) => {
let mut compared_index = 0;
for (index, n) in opt_name.split('.').enumerate() {
if let Some(seg) = ext.0.get(index) {
match seg {
ProtobufOptionNamePart::Direct(d) => {
if !d.eq(n) {
return false;
}
}
ProtobufOptionNamePart::Ext(_ext) => return false,
}
} else {
return false;
}
compared_index = index;
}
ext.0.len() == compared_index + 1
}
}
}
}
#[derive(Debug, Clone)]
pub struct ProtobufOption {
pub name: ProtobufOptionName,
pub value: ProtobufConstant,
}
pub trait GetOption {
fn get_option<'a>(&'a self, name: &str) -> Option<&'a ProtobufOption>;
}
impl GetOption for Vec<ProtobufOption> {
fn get_option<'a>(&'a self, name: &str) -> Option<&'a ProtobufOption> {
self.iter().find(|opt| opt.name.is_option(name))
}
}
impl<P> GetOption for Punctuated<ProtobufOption, P> {
fn get_option<'a>(&'a self, name: &str) -> Option<&'a ProtobufOption> {
self.iter().find(|opt| opt.name.is_option(name))
}
}
#[derive(Debug, Clone)]
pub enum ImportVis {
Default,
Public(Span),
Weak(Span),
}
impl Default for ImportVis {
fn default() -> Self {
ImportVis::Default
}
}
#[derive(Debug, Clone)]
pub struct Import {
pub import_token: Span,
pub path: LitStr,
pub builtin: bool,
pub vis: ImportVis,
pub file_path: Option<FilePath>,
}
#[derive(Debug, Clone)]
pub struct FilePath {
pub root: bool,
pub bin: bool,
pub example: bool,
pub is_mod: bool,
pub path: PathBuf,
pub mod_path: syn::Path,
}
#[derive(Debug, Clone)]
pub struct Package {
pub package: Ident,
}
#[derive(Debug, Clone)]
pub enum DeclIndex {
Message(usize),
Enum(usize),
Service(usize),
}
#[derive(Debug, Clone)]
pub struct Protocol {
pub imports: Vec<Import>,
pub package: Option<Package>,
pub syntax: Syntax,
pub messages: Vec<Message>,
pub enums: Vec<Enumeration>,
pub extensions: Vec<Extension>,
pub services: Vec<Service>,
pub options: Vec<ProtobufOption>,
pub decls: Vec<DeclIndex>,
pub deps: Deps,
}