Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ comline-core = { git = "https://github.com/ComlineProject/core", rev = "47ac5f10
# pin (below) — `codegen/`'s content is identical at this rev and the later
# `generation` tip (only `conformance/`, which this crate never touches,
# differs), so matching it here keeps one `comline-codegen` in the tree.
comline-codegen = { git = "https://github.com/ComlineProject/generation", rev = "1f8c5e290eba72a578860ebb6dc33453a2f0726b" }
comline-codegen-rust = { git = "https://github.com/ComlineProject/comline-rust", rev = "a0ade3bceeb51876be7565601b3b9c9c3d807196", optional = true }
comline-codegen-typescript = { git = "https://github.com/ComlineProject/comline-typescript", rev = "86fc4eb586529ef92041f8dd3c46ff9340103411", optional = true }
comline-codegen = { git = "https://github.com/ComlineProject/generation", rev = "c70336dcc9485cea2c99b0f1db3789e420deaeda" }
comline-codegen-rust = { git = "https://github.com/ComlineProject/comline-rust", rev = "85fd9670fae794f043d28ada415dca7ed090d713", optional = true }
comline-codegen-typescript = { git = "https://github.com/ComlineProject/comline-typescript", rev = "f72bc3e336ebadbe062751afc0f2430fd216e28c", optional = true }

[features]
# Which language generators are compiled in. Drop one to shed its whole
Expand Down
1 change: 1 addition & 0 deletions src/commands/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ fn generate_once(work_dir: &Path, overrides: &Overrides) -> Result<()> {
gv.package_version.clone()
},
},
default_framing: t.default_framing.clone(),
};
let files =
generator(&request).map_err(|e| miette!("`{}` generator: {e}", t.language))?;
Expand Down
1 change: 1 addition & 0 deletions src/commands/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const COMLINE_TOML: &str = "\
# layout = \"{{language}}/{{namespace}}.{{ext}}\" # path under the root
# mode = \"code\" # code | lib | dylib (only `code` today)
# package_versions = \"latest\" # latest | all | [\"0.3.0\", \"0.4.0\"]
# default_framing = \"jsonrpc\" # wire framing for protocols with no `@framing` (else datagram)
#
# [[generate.target]]
# language = \"rust\"
Expand Down
52 changes: 52 additions & 0 deletions src/gen_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ pub struct Generate {
pub mode: Option<String>,
/// Version selection shared by every target unless overridden.
pub package_versions: Option<VersionSpec>,
/// Fallback wire framing for protocols that do not pick one with `@framing`,
/// shared by every target unless overridden. A generator-recognised name
/// (`"jsonrpc"`, `"datagram"`, …); an unknown one leaves the generator's
/// built-in default in place.
pub default_framing: Option<String>,
/// `[[generate.target]]` blocks.
#[serde(rename = "target")]
pub targets: Vec<Target>,
Expand All @@ -97,6 +102,7 @@ pub struct Target {
pub layout: Option<String>,
pub mode: Option<String>,
pub package_versions: Option<VersionSpec>,
pub default_framing: Option<String>,
}

/// A non-empty `COMLINE_GENERATE_*` env var, if set.
Expand Down Expand Up @@ -145,6 +151,8 @@ pub struct ResolvedTarget {
pub layout: String,
pub mode: String,
pub versions: VersionSpec,
/// Fallback framing handed to the generator; `None` ⇒ its built-in default.
pub default_framing: Option<String>,
}

/// Variables a `layout` template can reference.
Expand Down Expand Up @@ -209,6 +217,7 @@ pub fn resolve(
layout: None,
mode: None,
package_versions: None,
default_framing: None,
})
.collect()
} else {
Expand Down Expand Up @@ -305,13 +314,22 @@ pub fn resolve(
.or_else(|| cfg.generate.package_versions.clone())
.unwrap_or_default();

// `[[target]]` wins over `[generate]`; `None` at both ⇒ the generator's
// built-in default. No flag / env override — schema-shaping config, not
// a per-run knob.
let default_framing = t
.default_framing
.clone()
.or_else(|| cfg.generate.default_framing.clone());

resolved.push(ResolvedTarget {
language: t.language.clone(),
lang_version,
out: work_dir.join(out),
layout,
mode,
versions,
default_framing,
});
}

Expand Down Expand Up @@ -366,4 +384,38 @@ mod tests {
let resolved = resolve(&cfg, &declared, Path::new("/x"), &Overrides::default()).unwrap();
assert_eq!(resolved[0].versions, VersionSpec::Latest);
}

#[test]
fn default_framing_section_reaches_a_declared_target() {
let cfg = parse("[generate]\ndefault_framing = \"jsonrpc\"\n");
let declared = [DeclaredLang {
language: "rust".into(),
lang_version: "1.75".into(),
}];
let resolved = resolve(&cfg, &declared, Path::new("/x"), &Overrides::default()).unwrap();
assert_eq!(resolved[0].default_framing.as_deref(), Some("jsonrpc"));
}

#[test]
fn target_default_framing_wins_over_section() {
let cfg = parse(
"[generate]\ndefault_framing = \"jsonrpc\"\n\n\
[[generate.target]]\nlanguage = \"rust\"\nlang_version = \"1.70.0\"\n\
default_framing = \"datagram\"\n",
);
let declared = [];
let resolved = resolve(&cfg, &declared, Path::new("/x"), &Overrides::default()).unwrap();
assert_eq!(resolved[0].default_framing.as_deref(), Some("datagram"));
}

#[test]
fn default_framing_absent_is_none() {
let cfg = parse("[generate]\n");
let declared = [DeclaredLang {
language: "rust".into(),
lang_version: "1.75".into(),
}];
let resolved = resolve(&cfg, &declared, Path::new("/x"), &Overrides::default()).unwrap();
assert_eq!(resolved[0].default_framing, None);
}
}
40 changes: 40 additions & 0 deletions tests/cli/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,46 @@ fn rejects_an_unconfigured_target() {

// -------- lib mode --------

#[test]
fn comline_toml_default_framing_reaches_the_rust_generator() {
let temp = tempfile::tempdir().unwrap();
let project = copy_fixture("chat_project", temp.path());
fs::write(
project.join("comline.toml"),
"[generate]\ndefault_framing = \"jsonrpc\"\n",
)
.unwrap();

comline_cmd()
.current_dir(&project)
.args(["generate", "--target", "rust"])
.assert()
.success();

// `protocol Chat` has no `@framing` of its own, so it picks up the
// package default: the JSON-RPC stack, not the datagram one.
let chat = fs::read_to_string(project.join("generated/rust/chat.rs")).unwrap();
assert!(chat.contains("comline_runtime::framing::JsonRpcFraming"));
assert!(chat.contains("Client::connect_with_framing"));
assert!(!chat.contains("FRAMING_DATAGRAM"));
}

#[test]
fn without_comline_toml_a_protocol_stays_on_datagram_framing() {
let temp = tempfile::tempdir().unwrap();
let project = copy_fixture("chat_project", temp.path());

comline_cmd()
.current_dir(&project)
.args(["generate", "--target", "rust"])
.assert()
.success();

let chat = fs::read_to_string(project.join("generated/rust/chat.rs")).unwrap();
assert!(chat.contains("FRAMING_DATAGRAM"));
assert!(!chat.contains("JsonRpcFraming"));
}

#[test]
fn lib_mode_emits_a_crate() {
let temp = tempfile::tempdir().unwrap();
Expand Down
Loading