2 Commits
0.0.3 ... 0.1.1

Author SHA1 Message Date
369ccbe84e Make sure that imports are always in the same order between runs. This
makes the output more stable for putting it into a versioning system
like git
2025-07-26 13:12:34 +02:00
6cb51c4120 Cleanup 2025-05-29 20:28:15 +02:00
7 changed files with 69 additions and 33 deletions

2
Cargo.lock generated
View File

@ -561,7 +561,7 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "jrpc-cli" name = "jrpc-cli"
version = "0.1.0" version = "0.1.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",

View File

@ -1,7 +1,7 @@
[package] [package]
edition = "2021" edition = "2021"
name = "jrpc-cli" name = "jrpc-cli"
version = "0.1.0" version = "0.1.1"
[workspace] [workspace]
resolver = "2" resolver = "2"

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, path::PathBuf}; use std::{collections::BTreeMap, path::PathBuf};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@ -8,7 +8,7 @@ use crate::{
}; };
pub trait Compile { pub trait Compile {
fn new(options: &HashMap<String, String>) -> Result<Self> fn new(options: &BTreeMap<String, String>) -> Result<Self>
where where
Self: Sized; Self: Sized;

View File

@ -1,5 +1,5 @@
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{BTreeMap, BTreeSet},
error::Error, error::Error,
fmt::{Debug, Display}, fmt::{Debug, Display},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
@ -20,7 +20,7 @@ pub trait Definition: Debug {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct IR { pub struct IR {
pub options: HashMap<String, String>, pub options: BTreeMap<String, String>,
pub steps: Vec<Step>, pub steps: Vec<Step>,
} }
@ -58,7 +58,12 @@ impl ToString for BaseType {
impl Hash for BaseType { impl Hash for BaseType {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.to_string().hash(state); // Hash the enum variant itself
std::mem::discriminant(self).hash(state);
// If the variant has data, hash that too
if let BaseType::Custom(name) = self {
name.hash(state);
}
} }
} }
@ -178,7 +183,7 @@ impl Type {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TypeDefinition { pub struct TypeDefinition {
pub name: String, pub name: String,
pub depends: HashSet<BaseType>, pub depends: BTreeSet<BaseType>,
pub fields: Vec<Field>, pub fields: Vec<Field>,
pub position: ParserPosition, pub position: ParserPosition,
} }
@ -223,7 +228,7 @@ pub struct EnumField {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ServiceDefinition { pub struct ServiceDefinition {
pub name: String, pub name: String,
pub depends: HashSet<BaseType>, pub depends: BTreeSet<BaseType>,
pub methods: Vec<Method>, pub methods: Vec<Method>,
pub position: ParserPosition, pub position: ParserPosition,
} }
@ -259,7 +264,7 @@ pub struct MethodOutput {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MethodDecorators { pub struct MethodDecorators {
pub description: Option<String>, pub description: Option<String>,
pub parameter_descriptions: HashMap<String, String>, pub parameter_descriptions: BTreeMap<String, String>,
pub return_description: Option<String>, pub return_description: Option<String>,
} }
@ -267,7 +272,7 @@ fn build_type(stmt: &TypeStatement) -> Result<TypeDefinition> {
let mut typedef = TypeDefinition { let mut typedef = TypeDefinition {
position: stmt.position.clone(), position: stmt.position.clone(),
name: stmt.name.clone(), name: stmt.name.clone(),
depends: HashSet::new(), depends: BTreeSet::new(),
fields: Vec::new(), fields: Vec::new(),
}; };
@ -332,7 +337,7 @@ fn build_service(stmt: &ServiceStatement) -> Result<ServiceDefinition> {
let mut servdef = ServiceDefinition { let mut servdef = ServiceDefinition {
position: stmt.position.clone(), position: stmt.position.clone(),
name: stmt.name.clone(), name: stmt.name.clone(),
depends: HashSet::new(), depends: BTreeSet::new(),
methods: Vec::new(), methods: Vec::new(),
}; };
@ -349,7 +354,7 @@ fn build_service(stmt: &ServiceStatement) -> Result<ServiceDefinition> {
}), }),
decorators: MethodDecorators { decorators: MethodDecorators {
description: None, description: None,
parameter_descriptions: HashMap::new(), parameter_descriptions: BTreeMap::new(),
return_description: None, return_description: None,
}, },
}; };
@ -450,7 +455,7 @@ fn build_service(stmt: &ServiceStatement) -> Result<ServiceDefinition> {
} }
pub fn build_ir(root: &Vec<RootNode>) -> Result<IR> { pub fn build_ir(root: &Vec<RootNode>) -> Result<IR> {
let mut options = HashMap::<String, String>::new(); let mut options = BTreeMap::<String, String>::new();
let mut steps = Vec::new(); let mut steps = Vec::new();
for node in root { for node in root {
@ -476,8 +481,8 @@ pub fn build_ir(root: &Vec<RootNode>) -> Result<IR> {
} }
} }
let mut all_types = HashSet::<String>::new(); let mut all_types = BTreeSet::<String>::new();
let mut serv_types = HashSet::<String>::new(); let mut serv_types = BTreeSet::<String>::new();
for bi in &BUILT_INS { for bi in &BUILT_INS {
all_types.insert(bi.to_string()); all_types.insert(bi.to_string());
@ -577,3 +582,36 @@ impl Display for IRError {
write!(f, "ParserError: {} at {:?}", self.message, self.position) write!(f, "ParserError: {} at {:?}", self.message, self.position)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn btreeset_order_is_consistent() {
let mut set1 = BTreeSet::new();
let mut set2 = BTreeSet::new();
let elements = vec![
BaseType::Custom("CustomType".to_string()),
BaseType::Void,
BaseType::Bytes,
BaseType::Float,
];
// Insert in normal order
for elem in &elements {
set1.insert(elem.clone());
}
// Insert in reverse order
for elem in elements.iter().rev() {
set2.insert(elem.clone());
}
let iter1: Vec<_> = set1.iter().cloned().collect();
let iter2: Vec<_> = set2.iter().cloned().collect();
assert_eq!(iter1, iter2); // Order must be the same
}
}

View File

@ -1,6 +1,6 @@
use anyhow::Result; use anyhow::Result;
use log::warn; use log::warn;
use std::collections::{HashMap, HashSet}; use std::collections::{BTreeMap, BTreeSet};
use crate::compile::{Compile, CompileContext, FileGenerator}; use crate::compile::{Compile, CompileContext, FileGenerator};
use crate::ir::{BaseType, EnumDefinition, ServiceDefinition, Step, Type, TypeDefinition}; use crate::ir::{BaseType, EnumDefinition, ServiceDefinition, Step, Type, TypeDefinition};
@ -46,7 +46,7 @@ impl RustCompiler {
fn add_dependencies( fn add_dependencies(
&mut self, &mut self,
file: &mut FileGenerator, file: &mut FileGenerator,
depends: &HashSet<BaseType>, depends: &BTreeSet<BaseType>,
) -> Result<()> { ) -> Result<()> {
for dep in depends { for dep in depends {
match dep { match dep {
@ -409,7 +409,7 @@ impl RustCompiler {
} }
impl Compile for RustCompiler { impl Compile for RustCompiler {
fn new(options: &HashMap<String, String>) -> anyhow::Result<Self> { fn new(options: &BTreeMap<String, String>) -> anyhow::Result<Self> {
let crate_name = if let Some(crate_name) = options.get("rust_crate") { let crate_name = if let Some(crate_name) = options.get("rust_crate") {
crate_name.to_string() crate_name.to_string()
} else { } else {

View File

@ -1,18 +1,13 @@
use anyhow::{anyhow, Result}; use std::collections::{BTreeMap, BTreeSet};
use anyhow::Result;
use log::info; use log::info;
use std::collections::{HashMap, HashSet};
use crate::compile::{Compile, CompileContext, FileGenerator}; use crate::compile::{Compile, CompileContext, FileGenerator};
use crate::ir::{BaseType, EnumDefinition, ServiceDefinition, Step, Type, TypeDefinition}; use crate::ir::{BaseType, EnumDefinition, ServiceDefinition, Step, Type, TypeDefinition};
use crate::IR; use crate::IR;
// #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
// pub enum Flavour {
// ESM,
// Node,
// }
pub trait Flavour { pub trait Flavour {
fn ext() -> &'static str; fn ext() -> &'static str;
fn name() -> &'static str; fn name() -> &'static str;
@ -115,9 +110,6 @@ impl<F: Flavour> TypeScriptCompiler<F> {
fn type_to_typescript_ext(typ: &Type) -> String { fn type_to_typescript_ext(typ: &Type) -> String {
let mut result = Self::type_to_typescript(&typ.0); let mut result = Self::type_to_typescript(&typ.0);
let (optional, array, map) = typ.1.get_flags(); let (optional, array, map) = typ.1.get_flags();
if optional {
result = format!("({} | undefined)", result);
}
if array { if array {
result = format!("({})[]", result); result = format!("({})[]", result);
} }
@ -128,13 +120,17 @@ impl<F: Flavour> TypeScriptCompiler<F> {
result result
); );
} }
if optional {
// Optional should be the last modifier
result = format!("({} | undefined)", result);
}
result result
} }
fn add_dependencies( fn add_dependencies(
&mut self, &mut self,
file: &mut FileGenerator, file: &mut FileGenerator,
depends: &HashSet<BaseType>, depends: &BTreeSet<BaseType>,
) -> Result<()> { ) -> Result<()> {
let esm = F::ext(); let esm = F::ext();
file.a0(format!( file.a0(format!(
@ -439,7 +435,7 @@ import {{ VerificationError }} from \"./ts_base{esm}\";
} }
impl<F: Flavour> Compile for TypeScriptCompiler<F> { impl<F: Flavour> Compile for TypeScriptCompiler<F> {
fn new(options: &HashMap<String, String>) -> Result<Self> { fn new(options: &BTreeMap<String, String>) -> Result<Self> {
let flavour = options let flavour = options
.get("flavour") .get("flavour")
.cloned() .cloned()
@ -544,7 +540,7 @@ impl<F: Flavour> Compile for TypeScriptCompiler<F> {
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut f = FileGenerator::new(); let mut f = FileGenerator::new();
self.add_dependencies(&mut f, &HashSet::new())?; self.add_dependencies(&mut f, &BTreeSet::new())?;
f.a0(format!("enum {} {{", definition.name)); f.a0(format!("enum {} {{", definition.name));
for value in &definition.values { for value in &definition.values {

2
libjrpc/templates/CSharp/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
bin/
obj/