Compare commits

..

No commits in common. "ed2b779d7bd855ef28404f2729812c5fa4b75177" and "e28e3493173657e2a857b113cb9c43de12920c59" have entirely different histories.

17 changed files with 96 additions and 97 deletions

2
.gitignore vendored
View File

@ -17,4 +17,4 @@
# Session exports dropped into the project directory by tooling that runs # Session exports dropped into the project directory by tooling that runs
# alongside it. They are large and unrelated to the service. # alongside it. They are large and unrelated to the service.
apsis-book.html apsis-web.html

14
Cargo.lock generated
View File

@ -2,13 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "apsis-book"
version = "0.1.0"
dependencies = [
"apsis-rs",
]
[[package]] [[package]]
name = "apsis-coord" name = "apsis-coord"
version = "0.1.0" version = "0.1.0"
@ -132,3 +125,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"apsis-core", "apsis-core",
] ]
[[package]]
name = "apsis-web"
version = "0.1.0"
dependencies = [
"apsis-rs",
]

View File

@ -1,4 +1,4 @@
# apsis-book: the web service layer of ApsisRS. # apsis-web: the web service layer of ApsisRS.
# #
# The service exposes the ApsisRS computational kernels over HTTP together # The service exposes the ApsisRS computational kernels over HTTP together
# with the concept documentation, the knowledge graph, and the online tool # with the concept documentation, the knowledge graph, and the online tool
@ -13,7 +13,7 @@
# together under these terms. # together under these terms.
[package] [package]
name = "apsis-book" name = "apsis-web"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
rust-version = "1.85" rust-version = "1.85"
@ -22,7 +22,7 @@ description = "Web service for the ApsisRS computational kernels."
publish = false publish = false
[[bin]] [[bin]]
name = "apsis-book" name = "apsis-web"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]

View File

@ -1,4 +1,4 @@
# apsis-book # apsis-web
[简体中文](./README.zh.md) | English [简体中文](./README.zh.md) | English
@ -59,27 +59,27 @@ cargo build
cargo build --release cargo build --release
``` ```
The binary is at `target/release/apsis-book`. The binary is at `target/release/apsis-web`.
Environment variables: Environment variables:
| Variable | Effect | | Variable | Effect |
|----------|--------| |----------|--------|
| `APSIS_BOOK_HOST` | Interface to bind, default `127.0.0.1` | | `APSIS_WEB_HOST` | Interface to bind, default `127.0.0.1` |
| `APSIS_BOOK_PORT` | Port to bind, default `8080` | | `APSIS_WEB_PORT` | Port to bind, default `8080` |
| `APSIS_BOOK_ADDRESS` | Bind address as `HOST:PORT`, overriding the two above | | `APSIS_WEB_ADDRESS` | Bind address as `HOST:PORT`, overriding the two above |
| `APSIS_BOOK_WORKERS` | Worker threads, default 4 | | `APSIS_WEB_WORKERS` | Worker threads, default 4 |
| `APSIS_BOOK_SKIP_FRONTEND` | Set to `1` to skip the frontend build in a release build | | `APSIS_WEB_SKIP_FRONTEND` | Set to `1` to skip the frontend build in a release build |
| `APSIS_BOOK_FRONTEND_DIR` | Frontend directory, default `frontend` | | `APSIS_WEB_FRONTEND_DIR` | Frontend directory, default `frontend` |
Command line options override the environment: Command line options override the environment:
```sh ```sh
# Bind every interface on port 8080. # Bind every interface on port 8080.
apsis-book --host 0.0.0.0 --port 8080 apsis-web --host 0.0.0.0 --port 8080
# The combined form is equivalent. # The combined form is equivalent.
apsis-book --address 0.0.0.0:8080 apsis-web --address 0.0.0.0:8080
``` ```
`--host` and `--port` may be given together. `--address` cannot be combined `--host` and `--port` may be given together. `--address` cannot be combined
@ -102,7 +102,7 @@ English to every reader, with a note saying so.
## Running ## Running
```sh ```sh
./target/release/apsis-book --address 127.0.0.1:8080 ./target/release/apsis-web --address 127.0.0.1:8080
``` ```
Open `http://127.0.0.1:8080`. The JSON interfaces are available without the Open `http://127.0.0.1:8080`. The JSON interfaces are available without the
@ -186,7 +186,7 @@ a link that the frontend routes without a page load.
## Layout ## Layout
```text ```text
apsis-book/ apsis-web/
├── build.rs embed the frontend build and the text content ├── build.rs embed the frontend build and the text content
├── Cargo.toml one runtime dependency: apsis-rs ├── Cargo.toml one runtime dependency: apsis-rs
├── content/ authored documents, graphs, and generated catalog ├── content/ authored documents, graphs, and generated catalog

View File

@ -1,4 +1,4 @@
# apsis-book # apsis-web
[English](./README.md) | 简体中文 [English](./README.md) | 简体中文
@ -49,27 +49,27 @@ cargo build
cargo build --release cargo build --release
``` ```
二进制位于 `target/release/apsis-book`。 二进制位于 `target/release/apsis-web`。
环境变量: 环境变量:
| 变量 | 作用 | | 变量 | 作用 |
|------|------| |------|------|
| `APSIS_BOOK_HOST` | 绑定的网络接口,默认 `127.0.0.1` | | `APSIS_WEB_HOST` | 绑定的网络接口,默认 `127.0.0.1` |
| `APSIS_BOOK_PORT` | 绑定的端口,默认 `8080` | | `APSIS_WEB_PORT` | 绑定的端口,默认 `8080` |
| `APSIS_BOOK_ADDRESS` | 以 `HOST:PORT` 表示的绑定地址,覆盖上面两项 | | `APSIS_WEB_ADDRESS` | 以 `HOST:PORT` 表示的绑定地址,覆盖上面两项 |
| `APSIS_BOOK_WORKERS` | 工作线程数,默认 4 | | `APSIS_WEB_WORKERS` | 工作线程数,默认 4 |
| `APSIS_BOOK_SKIP_FRONTEND` | 设为 `1` 时在发布构建中跳过前端构建 | | `APSIS_WEB_SKIP_FRONTEND` | 设为 `1` 时在发布构建中跳过前端构建 |
| `APSIS_BOOK_FRONTEND_DIR` | 前端目录,默认 `frontend` | | `APSIS_WEB_FRONTEND_DIR` | 前端目录,默认 `frontend` |
命令行选项优先于环境变量: 命令行选项优先于环境变量:
```sh ```sh
# 在 8080 端口绑定所有网络接口。 # 在 8080 端口绑定所有网络接口。
apsis-book --host 0.0.0.0 --port 8080 apsis-web --host 0.0.0.0 --port 8080
# 合并写法等价。 # 合并写法等价。
apsis-book --address 0.0.0.0:8080 apsis-web --address 0.0.0.0:8080
``` ```
`--host` 与 `--port` 可以同时给出。`--address` 不能与它们中的任何一个合用, `--host` 与 `--port` 可以同时给出。`--address` 不能与它们中的任何一个合用,
@ -88,7 +88,7 @@ apsis-book --address 0.0.0.0:8080
## 运行 ## 运行
```sh ```sh
./target/release/apsis-book --address 127.0.0.1:8080 ./target/release/apsis-web --address 127.0.0.1:8080
``` ```
打开 `http://127.0.0.1:8080`。JSON 接口不依赖前端即可访问: 打开 `http://127.0.0.1:8080`。JSON 接口不依赖前端即可访问:
@ -164,7 +164,7 @@ cargo build --release
## 目录结构 ## 目录结构
```text ```text
apsis-book/ apsis-web/
├── build.rs 嵌入前端构建产物与文本内容 ├── build.rs 嵌入前端构建产物与文本内容
├── Cargo.toml 一个运行时依赖:apsis-rs ├── Cargo.toml 一个运行时依赖:apsis-rs
├── content/ 撰写的文档、图谱与生成的目录 ├── content/ 撰写的文档、图谱与生成的目录

View File

@ -1,4 +1,4 @@
//! Build script for `apsis-book`. //! Build script for `apsis-web`.
//! //!
//! The script has one job: turn the frontend build output into a Rust source //! The script has one job: turn the frontend build output into a Rust source
//! file that embeds every asset in the binary. It never introduces a crate //! file that embeds every asset in the binary. It never introduces a crate
@ -9,8 +9,8 @@
//! - In a debug build the frontend is not rebuilt. The script scans //! - In a debug build the frontend is not rebuilt. The script scans
//! whatever is already in `frontend/dist/` and embeds it. //! whatever is already in `frontend/dist/` and embeds it.
//! - In a release build the script runs the frontend build first, unless //! - In a release build the script runs the frontend build first, unless
//! `APSIS_BOOK_SKIP_FRONTEND=1` is set. The frontend directory can be //! `APSIS_WEB_SKIP_FRONTEND=1` is set. The frontend directory can be
//! changed with `APSIS_BOOK_FRONTEND_DIR`. //! changed with `APSIS_WEB_FRONTEND_DIR`.
//! - When `frontend/dist/` is absent the script emits an empty asset table. //! - When `frontend/dist/` is absent the script emits an empty asset table.
//! The service then serves a built-in placeholder page, so a checkout //! The service then serves a built-in placeholder page, so a checkout
//! without a frontend build still compiles and runs. //! without a frontend build still compiles and runs.
@ -27,7 +27,7 @@ use std::process::Command;
fn main() { fn main() {
let manifest_dir = PathBuf::from(env("CARGO_MANIFEST_DIR")); let manifest_dir = PathBuf::from(env("CARGO_MANIFEST_DIR"));
let out_dir = PathBuf::from(env("OUT_DIR")); let out_dir = PathBuf::from(env("OUT_DIR"));
let frontend_dir = std::env::var("APSIS_BOOK_FRONTEND_DIR") let frontend_dir = std::env::var("APSIS_WEB_FRONTEND_DIR")
.map(PathBuf::from) .map(PathBuf::from)
.unwrap_or_else(|_| manifest_dir.join("frontend")); .unwrap_or_else(|_| manifest_dir.join("frontend"));
let dist_dir = frontend_dir.join("dist"); let dist_dir = frontend_dir.join("dist");
@ -58,13 +58,13 @@ fn main() {
// after a build that embedded nothing causes the next build to embed it. // after a build that embedded nothing causes the next build to embed it.
// Cargo accepts a path that does not exist yet. // Cargo accepts a path that does not exist yet.
println!("cargo:rerun-if-changed={}", dist_dir.display()); println!("cargo:rerun-if-changed={}", dist_dir.display());
println!("cargo:rerun-if-env-changed=APSIS_BOOK_SKIP_FRONTEND"); println!("cargo:rerun-if-env-changed=APSIS_WEB_SKIP_FRONTEND");
println!("cargo:rerun-if-env-changed=APSIS_BOOK_FRONTEND_DIR"); println!("cargo:rerun-if-env-changed=APSIS_WEB_FRONTEND_DIR");
let is_release = std::env::var("DEBUG") let is_release = std::env::var("DEBUG")
.map(|value| value == "false") .map(|value| value == "false")
.unwrap_or(false); .unwrap_or(false);
let skip = std::env::var("APSIS_BOOK_SKIP_FRONTEND") let skip = std::env::var("APSIS_WEB_SKIP_FRONTEND")
.map(|value| value == "1") .map(|value| value == "1")
.unwrap_or(false); .unwrap_or(false);
@ -73,7 +73,7 @@ fn main() {
build_frontend(&frontend_dir); build_frontend(&frontend_dir);
} else { } else {
println!( println!(
"cargo:warning=apsis-book: no frontend/package.json, embedding whatever is in {}", "cargo:warning=apsis-web: no frontend/package.json, embedding whatever is in {}",
dist_dir.display() dist_dir.display()
); );
} }
@ -82,7 +82,7 @@ fn main() {
let assets = if dist_dir.is_dir() { let assets = if dist_dir.is_dir() {
collect(&dist_dir).unwrap_or_else(|error| { collect(&dist_dir).unwrap_or_else(|error| {
println!( println!(
"cargo:warning=apsis-book: cannot scan {}: {error}", "cargo:warning=apsis-web: cannot scan {}: {error}",
dist_dir.display() dist_dir.display()
); );
Vec::new() Vec::new()
@ -93,7 +93,7 @@ fn main() {
if assets.is_empty() { if assets.is_empty() {
println!( println!(
"cargo:warning=apsis-book: no frontend assets found in {}; the placeholder page will be served", "cargo:warning=apsis-web: no frontend assets found in {}; the placeholder page will be served",
dist_dir.display() dist_dir.display()
); );
} }
@ -109,7 +109,7 @@ fn main() {
let content = if content_dir.is_dir() { let content = if content_dir.is_dir() {
collect(&content_dir).unwrap_or_else(|error| { collect(&content_dir).unwrap_or_else(|error| {
println!( println!(
"cargo:warning=apsis-book: cannot scan {}: {error}", "cargo:warning=apsis-web: cannot scan {}: {error}",
content_dir.display() content_dir.display()
); );
Vec::new() Vec::new()
@ -137,7 +137,7 @@ fn build_frontend(frontend_dir: &Path) {
let npm = if cfg!(windows) { "npm.cmd" } else { "npm" }; let npm = if cfg!(windows) { "npm.cmd" } else { "npm" };
if !frontend_dir.join("node_modules").is_dir() { if !frontend_dir.join("node_modules").is_dir() {
println!("cargo:warning=apsis-book: installing frontend dependencies"); println!("cargo:warning=apsis-web: installing frontend dependencies");
match Command::new(npm) match Command::new(npm)
.args(["install", "--no-audit", "--no-fund"]) .args(["install", "--no-audit", "--no-fund"])
.current_dir(frontend_dir) .current_dir(frontend_dir)
@ -145,18 +145,18 @@ fn build_frontend(frontend_dir: &Path) {
{ {
Ok(status) if status.success() => {} Ok(status) if status.success() => {}
Ok(status) => println!( Ok(status) => println!(
"cargo:warning=apsis-book: npm install exited with {status}; embedding the existing dist if any" "cargo:warning=apsis-web: npm install exited with {status}; embedding the existing dist if any"
), ),
Err(error) => { Err(error) => {
println!( println!(
"cargo:warning=apsis-book: cannot run npm ({error}); embedding the existing dist if any" "cargo:warning=apsis-web: cannot run npm ({error}); embedding the existing dist if any"
); );
return; return;
} }
} }
} }
println!("cargo:warning=apsis-book: building the frontend"); println!("cargo:warning=apsis-web: building the frontend");
match Command::new(npm) match Command::new(npm)
.args(["run", "build"]) .args(["run", "build"])
.current_dir(frontend_dir) .current_dir(frontend_dir)
@ -164,10 +164,10 @@ fn build_frontend(frontend_dir: &Path) {
{ {
Ok(status) if status.success() => {} Ok(status) if status.success() => {}
Ok(status) => println!( Ok(status) => println!(
"cargo:warning=apsis-book: npm run build exited with {status}; embedding the existing dist if any" "cargo:warning=apsis-web: npm run build exited with {status}; embedding the existing dist if any"
), ),
Err(error) => println!( Err(error) => println!(
"cargo:warning=apsis-book: cannot run npm run build ({error}); embedding the existing dist if any" "cargo:warning=apsis-web: cannot run npm run build ({error}); embedding the existing dist if any"
), ),
} }
} }

View File

@ -1,4 +1,4 @@
# apsis-book design notes # apsis-web design notes
These notes explain the decisions behind the service. They are for someone These notes explain the decisions behind the service. They are for someone
reading the code, and they describe the design rather than a plan. reading the code, and they describe the design rather than a plan.
@ -202,7 +202,7 @@ does not relink.
`build.rs` runs the frontend build in a release profile and skips it in a `build.rs` runs the frontend build in a release profile and skips it in a
debug profile. The split is deliberate: a debug build iterates on the backend debug profile. The split is deliberate: a debug build iterates on the backend
and should not pay for a frontend build, while a release build should produce and should not pay for a frontend build, while a release build should produce
one binary with no further steps. `APSIS_BOOK_SKIP_FRONTEND=1` forces the skip one binary with no further steps. `APSIS_WEB_SKIP_FRONTEND=1` forces the skip
in a release build. A frontend failure is reported as a warning and the last in a release build. A frontend failure is reported as a warning and the last
successful output is embedded, so a release build without network access successful output is embedded, so a release build without network access
still produces a binary. still produces a binary.

View File

@ -1,4 +1,4 @@
# apsis-book 设计说明 # apsis-web 设计说明
[English](./design.md) | 简体中文 [English](./design.md) | 简体中文
@ -146,7 +146,7 @@ props 的纯函数,并使图在测试或截图中可复现。
`build.rs` 在 release 配置下运行前端构建,在 debug 配置下跳过它。这一划分是刻意 `build.rs` 在 release 配置下运行前端构建,在 debug 配置下跳过它。这一划分是刻意
的:调试构建迭代的是后端,不应为前端构建付出代价;发布构建则应当产出无需后续 的:调试构建迭代的是后端,不应为前端构建付出代价;发布构建则应当产出无需后续
步骤的单一二进制。`APSIS_BOOK_SKIP_FRONTEND=1` 强制在发布构建中跳过。前端构建 步骤的单一二进制。`APSIS_WEB_SKIP_FRONTEND=1` 强制在发布构建中跳过。前端构建
失败会作为警告报告,并嵌入上一次成功的产物,因此在没有网络的情况下发布构建 失败会作为警告报告,并嵌入上一次成功的产物,因此在没有网络的情况下发布构建
仍能产出二进制。 仍能产出二进制。

View File

@ -1,11 +1,11 @@
{ {
"name": "apsis-book-frontend", "name": "apsis-web-frontend",
"version": "0.1.0", "version": "0.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "apsis-book-frontend", "name": "apsis-web-frontend",
"version": "0.1.0", "version": "0.1.0",
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.3", "@sveltejs/vite-plugin-svelte": "^5.0.3",

View File

@ -1,9 +1,9 @@
{ {
"name": "apsis-book-frontend", "name": "apsis-web-frontend",
"private": true, "private": true,
"version": "0.1.0", "version": "0.1.0",
"type": "module", "type": "module",
"description": "Frontend for the apsis-book service. Built output is embedded in the Rust binary.", "description": "Frontend for the apsis-web service. Built output is embedded in the Rust binary.",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",

View File

@ -19,7 +19,7 @@ export type Language = 'zh' | 'en';
export const DEFAULT_LANGUAGE: Language = 'zh'; export const DEFAULT_LANGUAGE: Language = 'zh';
/** The key the choice is stored under. */ /** The key the choice is stored under. */
const STORAGE_KEY = 'apsis-book-language'; const STORAGE_KEY = 'apsis-web-language';
/** A string carried in both languages. */ /** A string carried in both languages. */
export interface Text { export interface Text {

View File

@ -4,11 +4,11 @@
The service presents the public interface of the `apsis-rs` workspace as it The service presents the public interface of the `apsis-rs` workspace as it
is written in the source, so the catalog is generated from the source rather is written in the source, so the catalog is generated from the source rather
than maintained by hand. This script reads the crate directories of the than maintained by hand. This script reads the crate directories of the
layers that `apsis-book` enables, collects the crate level documentation and layers that `apsis-web` enables, collects the crate level documentation and
every public item with its doc comment and signature, and writes the result every public item with its doc comment and signature, and writes the result
to `content/api.json`. The file is embedded in the binary by `build.rs`. to `content/api.json`. The file is embedded in the binary by `build.rs`.
Run it from the `apsis-book` directory when the `apsis-rs` sources change: Run it from the `apsis-web` directory when the `apsis-rs` sources change:
scripts/extract_api.sh scripts/extract_api.sh

View File

@ -5,7 +5,7 @@
# `apsis-rs`. It is generated once and committed, so that building the Rust # `apsis-rs`. It is generated once and committed, so that building the Rust
# binary does not depend on the apsis-rs checkout or on Python. # binary does not depend on the apsis-rs checkout or on Python.
# #
# Usage, from the apsis-book directory: # Usage, from the apsis-web directory:
# #
# scripts/extract_api.sh # scripts/extract_api.sh
# #

View File

@ -117,7 +117,7 @@ impl Router {
fn health(&self) -> Response { fn health(&self) -> Response {
let body = json_object! { let body = json_object! {
"status" => "ok", "status" => "ok",
"service" => "apsis-book", "service" => "apsis-web",
"version" => env!("CARGO_PKG_VERSION"), "version" => env!("CARGO_PKG_VERSION"),
"documents" => self.state.docs.len(), "documents" => self.state.docs.len(),
"layers" => self "layers" => self

View File

@ -95,7 +95,7 @@ impl Server {
let config = self.config; let config = self.config;
handles.push( handles.push(
thread::Builder::new() thread::Builder::new()
.name("apsis-book-worker".to_string()) .name("apsis-web-worker".to_string())
.spawn(move || worker_loop(&receiver, &*handler, config))?, .spawn(move || worker_loop(&receiver, &*handler, config))?,
); );
} }

View File

@ -1,4 +1,4 @@
//! The `apsis-book` service binary. //! The `apsis-web` service binary.
//! //!
//! The process binds an HTTP/1.1 listener and answers requests from three //! The process binds an HTTP/1.1 listener and answers requests from three
//! sources of content that are all embedded in the binary: //! sources of content that are all embedded in the binary:
@ -46,7 +46,7 @@ fn main() -> ExitCode {
let options = match Options::from_environment() { let options = match Options::from_environment() {
Ok(options) => options, Ok(options) => options,
Err(message) => { Err(message) => {
eprintln!("apsis-book: {message}"); eprintln!("apsis-web: {message}");
return ExitCode::FAILURE; return ExitCode::FAILURE;
} }
}; };
@ -61,7 +61,7 @@ fn main() -> ExitCode {
let server = match Server::bind_address(options.address, config) { let server = match Server::bind_address(options.address, config) {
Ok(server) => server, Ok(server) => server,
Err(error) => { Err(error) => {
eprintln!("apsis-book: cannot bind {}: {error}", options.address); eprintln!("apsis-web: cannot bind {}: {error}", options.address);
return ExitCode::FAILURE; return ExitCode::FAILURE;
} }
}; };
@ -70,15 +70,15 @@ fn main() -> ExitCode {
.local_address() .local_address()
.map(|address| address.to_string()) .map(|address| address.to_string())
.unwrap_or_else(|_| options.address.to_string()); .unwrap_or_else(|_| options.address.to_string());
println!("apsis-book listening on http://{bound}"); println!("apsis-web listening on http://{bound}");
if !embedded::is_populated() { if !embedded::is_populated() {
println!("apsis-book: no frontend build is embedded; serving the placeholder page"); println!("apsis-web: no frontend build is embedded; serving the placeholder page");
} }
match server.run(router) { match server.run(router) {
Ok(()) => ExitCode::SUCCESS, Ok(()) => ExitCode::SUCCESS,
Err(error) => { Err(error) => {
eprintln!("apsis-book: server stopped: {error}"); eprintln!("apsis-web: server stopped: {error}");
ExitCode::FAILURE ExitCode::FAILURE
} }
} }
@ -95,8 +95,8 @@ struct Options {
impl Options { impl Options {
/// Builds the options from the process arguments. /// Builds the options from the process arguments.
/// ///
/// Arguments override the environment variables `APSIS_BOOK_HOST`, /// Arguments override the environment variables `APSIS_WEB_HOST`,
/// `APSIS_BOOK_PORT`, `APSIS_BOOK_ADDRESS`, and `APSIS_BOOK_WORKERS`. /// `APSIS_WEB_PORT`, `APSIS_WEB_ADDRESS`, and `APSIS_WEB_WORKERS`.
/// `--help` and `--version` print and exit. /// `--help` and `--version` print and exit.
/// ///
/// The host and the port may be given separately with `--host` and /// The host and the port may be given separately with `--host` and
@ -104,26 +104,25 @@ impl Options {
/// either of the two separate options is an error, because the order in /// either of the two separate options is an error, because the order in
/// which they would apply is not defined. /// which they would apply is not defined.
fn from_environment() -> Result<Self, String> { fn from_environment() -> Result<Self, String> {
let mut host = let mut host = std::env::var("APSIS_WEB_HOST").unwrap_or_else(|_| DEFAULT_HOST.to_string());
std::env::var("APSIS_BOOK_HOST").unwrap_or_else(|_| DEFAULT_HOST.to_string()); let mut port = match std::env::var("APSIS_WEB_PORT") {
let mut port = match std::env::var("APSIS_BOOK_PORT") { Ok(value) => parse_port(&value).map_err(|error| format!("APSIS_WEB_PORT: {error}"))?,
Ok(value) => parse_port(&value).map_err(|error| format!("APSIS_BOOK_PORT: {error}"))?,
Err(_) => DEFAULT_PORT, Err(_) => DEFAULT_PORT,
}; };
let mut combined: Option<SocketAddr> = match std::env::var("APSIS_BOOK_ADDRESS") { let mut combined: Option<SocketAddr> = match std::env::var("APSIS_WEB_ADDRESS") {
Ok(value) => Some( Ok(value) => {
parse_address(&value).map_err(|error| format!("APSIS_BOOK_ADDRESS: {error}"))?, Some(parse_address(&value).map_err(|error| format!("APSIS_WEB_ADDRESS: {error}"))?)
), }
Err(_) => None, Err(_) => None,
}; };
let mut workers = match std::env::var("APSIS_BOOK_WORKERS") { let mut workers = match std::env::var("APSIS_WEB_WORKERS") {
Ok(value) => value Ok(value) => value
.parse() .parse()
.map_err(|_| "APSIS_BOOK_WORKERS must be a positive integer".to_string())?, .map_err(|_| "APSIS_WEB_WORKERS must be a positive integer".to_string())?,
Err(_) => 4, Err(_) => 4,
}; };
if workers == 0 { if workers == 0 {
return Err("APSIS_BOOK_WORKERS must be greater than zero".to_string()); return Err("APSIS_WEB_WORKERS must be greater than zero".to_string());
} }
let mut host_given = false; let mut host_given = false;
@ -165,7 +164,7 @@ impl Options {
std::process::exit(0); std::process::exit(0);
} }
"--version" | "-V" => { "--version" | "-V" => {
println!("apsis-book {}", env!("CARGO_PKG_VERSION")); println!("apsis-web {}", env!("CARGO_PKG_VERSION"));
std::process::exit(0); std::process::exit(0);
} }
other => return Err(format!("unknown argument {other:?}; try --help")), other => return Err(format!("unknown argument {other:?}; try --help")),
@ -186,7 +185,7 @@ impl Options {
} else if let Some(address) = combined { } else if let Some(address) = combined {
address address
} else { } else {
resolve(&host, port).map_err(|error| format!("APSIS_BOOK_HOST: {error}"))? resolve(&host, port).map_err(|error| format!("APSIS_WEB_HOST: {error}"))?
}; };
Ok(Self { address, workers }) Ok(Self { address, workers })
@ -245,10 +244,10 @@ fn resolve(host: &str, port: u16) -> Result<SocketAddr, String> {
/// Usage text. /// Usage text.
const USAGE: &str = "\ const USAGE: &str = "\
apsis-book: web service for the ApsisRS computational kernels apsis-web: web service for the ApsisRS computational kernels
USAGE: USAGE:
apsis-book [OPTIONS] apsis-web [OPTIONS]
OPTIONS: OPTIONS:
-a, --address <HOST:PORT> address to bind (default 127.0.0.1:8080) -a, --address <HOST:PORT> address to bind (default 127.0.0.1:8080)
@ -259,11 +258,11 @@ OPTIONS:
-V, --version print the version -V, --version print the version
ENVIRONMENT: ENVIRONMENT:
APSIS_BOOK_ADDRESS bind address, as HOST:PORT APSIS_WEB_ADDRESS bind address, as HOST:PORT
APSIS_BOOK_HOST interface to bind APSIS_WEB_HOST interface to bind
APSIS_BOOK_PORT port to bind APSIS_WEB_PORT port to bind
APSIS_BOOK_WORKERS worker threads APSIS_WEB_WORKERS worker threads
EXAMPLES: EXAMPLES:
apsis-book --host 0.0.0.0 --port 8080 apsis-web --host 0.0.0.0 --port 8080
apsis-book --address 127.0.0.1:9000"; apsis-web --address 127.0.0.1:9000";

View File

@ -5,7 +5,7 @@
//! exercises the request parser, the router, the JSON codec, the embedded //! exercises the request parser, the router, the JSON codec, the embedded
//! content, and the tool handlers together, which the unit tests cannot do. //! content, and the tool handlers together, which the unit tests cannot do.
//! //!
//! The binary is located through the `CARGO_BIN_EXE_apsis-book` variable that //! The binary is located through the `CARGO_BIN_EXE_apsis-web` variable that
//! cargo sets for an integration test, so the test always runs against the //! cargo sets for an integration test, so the test always runs against the
//! binary built from the same sources. //! binary built from the same sources.
@ -25,7 +25,7 @@ struct Service {
impl Service { impl Service {
/// Starts the binary and waits for it to report its address. /// Starts the binary and waits for it to report its address.
fn start() -> Self { fn start() -> Self {
let mut child = Command::new(env!("CARGO_BIN_EXE_apsis-book")) let mut child = Command::new(env!("CARGO_BIN_EXE_apsis-web"))
.args(["--address", "127.0.0.1:0", "--workers", "2"]) .args(["--address", "127.0.0.1:0", "--workers", "2"])
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::piped()) .stderr(Stdio::piped())