Skip to content

Commit 08d8ae3

Browse files
authored
feat: C++ bindings support (#41)
* feat: C++ bindings support * ci: dev container * Add comprehensive C++ examples for local FCB file operations - local_fcb_example.cpp: Basic example showing metadata and iteration - comprehensive_example.cpp: Detailed example with JSON parsing (requires nlohmann/json) - CMakeLists.txt: Updated to include local_fcb_example target (no HTTP dependencies) * ci: dev container * ci: devcontainer * feat: C++ bindigs and example * chore: format * fix review * dev container * ignore local * ci: fix * ci: update ci * fmt * ci and format * justfile * ci * ci * ci * update just * justfile * ci
1 parent 36f7e55 commit 08d8ae3

30 files changed

+3013
-98
lines changed

.devcontainer/devcontainer.json

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"name": "FlatCityBuf Dev Container",
3+
"image": "mcr.microsoft.com/devcontainers/rust:1-1-bookworm",
4+
"features": {
5+
"ghcr.io/devcontainers/features/common-utils:2": {
6+
"installZsh": true,
7+
"installOhMyZsh": true,
8+
"upgradePackages": true,
9+
},
10+
"ghcr.io/devcontainers/features/git:1": {
11+
"version": "latest",
12+
},
13+
"ghcr.io/devcontainers/features/node:1": {
14+
"version": "lts",
15+
"nodeGypDependencies": true,
16+
"npmPackages": ["typescript"],
17+
},
18+
"ghcr.io/devcontainers/features/github-cli:1": {},
19+
},
20+
"customizations": {
21+
"vscode": {
22+
"extensions": [
23+
"rust-lang.rust-analyzer",
24+
"tamasfe.even-better-toml",
25+
"vadimcn.vscode-lldb",
26+
"mutantdino.resourcemonitor",
27+
"github.copilot",
28+
"streetsidesoftware.code-spell-checker",
29+
"ms-vscode.cpptools",
30+
"ms-vscode.cmake-tools",
31+
],
32+
"settings": {
33+
"rust-analyzer.checkOnSave.command": "clippy",
34+
"rust-analyzer.cargo.features": "all",
35+
"files.watcherExclude": {
36+
"**/target/**": true,
37+
"**/Cargo.lock": true,
38+
},
39+
"editor.formatOnSave": true,
40+
"cmake.configureOnOpen": false,
41+
},
42+
},
43+
},
44+
"mounts": [
45+
"source=${localWorkspaceFolderBasename}-cargo,target=/usr/local/cargo,type=volume",
46+
"source=${localWorkspaceFolderBasename}-target,target=${containerWorkspaceFolder}/src/rust/target,type=volume",
47+
"source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,consistency=cached",
48+
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached",
49+
"source=${env:HOME}/.config/gh,target=/home/vscode/.config/gh,type=bind",
50+
],
51+
"postCreateCommand": "bash -c 'sudo apt-get update && sudo apt-get install -y cmake ninja-build g++ libssl-dev && curl -LsSf https://astral.sh/uv/install.sh | sh && curl -fsSL https://claude.ai/install.sh | bash && cargo install just --locked && rustup target add wasm32-unknown-unknown && cargo install wasm-pack --locked || true && .devcontainer/install-claude-plugins.sh context7 frontend-design code-review feature-dev code-simplifier clangd-lsp claude-code-setup commit-commands explanatory-output-style frontend-design github greptile hookify learning-output-style playwright pr-review-toolkit pyright-lsp ralph-loop rust-analyzer-lsp serena typescript-lsp'",
52+
"remoteUser": "vscode",
53+
"forwardPorts": [8080, 3000],
54+
"portsAttributes": {
55+
"8080": {
56+
"label": "FCB API Server",
57+
"onAutoForward": "openBrowser",
58+
},
59+
},
60+
"runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
61+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/bash
2+
# Claude Code Plugin Installer for Dev Container
3+
# Installs plugins in the devcontainer's independent Claude config
4+
5+
set -e
6+
7+
GREEN='\033[0;32m'
8+
YELLOW='\033[1;33m'
9+
NC='\033[0m'
10+
11+
echo -e "${GREEN}Setting up Claude Code plugins in devcontainer...${NC}"
12+
13+
cd "$(dirname "$0")/.."
14+
15+
# Add marketplace if not present
16+
if ! claude plugin marketplace list 2>/dev/null | grep -q "claude-plugins-official"; then
17+
echo -e "${YELLOW}Adding official plugins marketplace...${NC}"
18+
claude plugin marketplace add anthropics/claude-plugins-official
19+
fi
20+
21+
# Update marketplace
22+
echo -e "${YELLOW}Updating marketplace...${NC}"
23+
claude plugin marketplace update 2>/dev/null || true
24+
25+
# Install plugins
26+
for plugin in "$@"; do
27+
if claude plugin list 2>/dev/null | grep -q "$(echo "$plugin" | cut -d'@' -f1)"; then
28+
echo -e "${GREEN}Already installed: $plugin${NC}"
29+
else
30+
echo -e "${YELLOW}Installing: $plugin${NC}"
31+
claude plugin install "$plugin" || echo "Failed to install $plugin"
32+
fi
33+
done
34+
35+
echo -e "${GREEN}Done!${NC}"

.github/workflows/ci-python.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ on:
55
branches: [main]
66
paths:
77
- "src/rust/fcb_py/**"
8-
- ".github/workflows/**"
8+
- ".github/workflows/ci-python.yml"
99

1010
pull_request:
1111
branches: [main]
1212
paths:
1313
- "src/rust/fcb_py/**"
14-
- ".github/workflows/**"
14+
- ".github/workflows/ci-python.yml"
1515

1616
env:
1717
CARGO_TERM_COLOR: always
@@ -142,7 +142,7 @@ jobs:
142142
strategy:
143143
fail-fast: false
144144
matrix:
145-
os: [macos-latest, windows-latest]
145+
os: [ubuntu-latest, macos-latest, windows-latest]
146146

147147
defaults:
148148
run:
@@ -240,5 +240,5 @@ jobs:
240240

241241
- name: Test wheel installation
242242
run: |
243-
pip install dist/*cp311*linux*.whl
243+
pip install dist/*linux*.whl
244244
python -c "import flatcitybuf; print(flatcitybuf.__version__)"

.github/workflows/ci.yml

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ on:
55
branches: [main]
66
paths:
77
- "src/rust/**"
8-
- ".github/workflows/**"
8+
- "src/cpp/**"
9+
- ".github/workflows/ci.yml"
910

1011
pull_request:
1112
branches: [main]
1213
paths:
1314
- "src/rust/**"
14-
- ".github/workflows/**"
15+
- "src/cpp/**"
16+
- ".github/workflows/ci.yml"
1517

1618
env:
1719
CARGO_TERM_COLOR: always
@@ -32,6 +34,9 @@ jobs:
3234
steps:
3335
- uses: actions/checkout@v4
3436

37+
- name: Install just task runner
38+
uses: taiki-e/install-action@just
39+
3540
- name: Install Rust toolchain
3641
uses: dtolnay/rust-toolchain@stable
3742
with:
@@ -50,10 +55,10 @@ jobs:
5055
uses: taiki-e/install-action@nextest
5156

5257
- name: Check Rust crates
53-
run: make check-common
58+
run: just check-common
5459

5560
- name: Check WASM
56-
run: make check-wasm
61+
run: just check-wasm
5762

5863
check-python:
5964
name: Check Python
@@ -65,6 +70,9 @@ jobs:
6570
steps:
6671
- uses: actions/checkout@v4
6772

73+
- name: Install just task runner
74+
uses: taiki-e/install-action@just
75+
6876
- name: Set up Python
6977
uses: actions/setup-python@v5
7078
with:
@@ -88,4 +96,30 @@ jobs:
8896
uses: astral-sh/setup-uv@v4
8997

9098
- name: Check Python package
91-
run: make check-py
99+
run: just check-py
100+
101+
check-cpp-roundtrip:
102+
name: Check C++ Roundtrip Tests
103+
runs-on: ubuntu-latest
104+
if: github.event_name == 'push' || github.event_name == 'pull_request'
105+
defaults:
106+
run:
107+
working-directory: src/cpp
108+
steps:
109+
- uses: actions/checkout@v4
110+
111+
- name: Install dependencies
112+
run: |
113+
sudo apt-get update
114+
sudo apt-get install -y cmake nlohmann-json3-dev pkg-config libssl-dev
115+
116+
- name: Build and run roundtrip tests
117+
run: |
118+
mkdir -p build
119+
cd build
120+
cmake .. -DCMAKE_BUILD_TYPE=Release
121+
make -j$(nproc) fcb_roundtrip_comprehensive
122+
123+
- name: Run C++ roundtrip test
124+
run: |
125+
./build/fcb_roundtrip_comprehensive ../rust/fcb_core/tests/data

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,8 @@ src/rust/fcb_core/tests/data/*.json
7171
flamegraph*
7272
.roo
7373
.serena/
74-
*.so
74+
*.so
75+
76+
build/
77+
78+
.claude/settings.local.json

.llm/docs/projectStructure.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# FlatCityBuf Project Structure
2+
3+
```
4+
flatcitybuf/
5+
6+
├── 📄 README.md # Project overview and getting started guide
7+
├── 📄 CLAUDE.md # AI/LLM coding guidelines for this repository
8+
├── 📄 CONTRIBUTING.md # Contribution guidelines
9+
├── 📄 LICENSE # MIT License
10+
11+
├── 📁 .llm/ # AI/LLM context and documentation
12+
│ └── docs/
13+
│ ├── productContext.md # Project purpose, problem, and goals
14+
│ ├── specification.md # FlatCityBuf encoding specification
15+
│ └── projectStructure.md # This file - folder structure overview
16+
17+
├── 📁 docs/ # Public documentation and assets
18+
│ ├── logo.png # Project logo
19+
│ └── ...
20+
21+
├── 📁 examples/ # Usage examples and tutorials
22+
│ └── data/ # Example data files
23+
│ └── delft.fcb # Sample FlatCityBuf file
24+
25+
├── 📁 scripts/ # Build and utility scripts
26+
27+
├── 📁 src/ # Source code root
28+
│ │
29+
│ ├── 📁 fbs/ # FlatBuffers schema definitions
30+
│ │ └── cityjson.fbs # CityJSON FlatBuffers schema
31+
│ │
32+
│ ├── 📁 rust/ # Rust workspace (core implementation)
33+
│ │ ├── 📄 Cargo.toml # Workspace configuration
34+
│ │ ├── 📄 Cargo.lock # Dependency lock file
35+
│ │ ├── 📄 CLAUDE.md # Rust-specific coding guidelines
36+
│ │ ├── 📄 Dockerfile # Docker image for Rust builds
37+
│ │ ├── 📄 makefile # Build automation
38+
│ │ │
39+
│ │ ├── 📁 cli/ # fcb_cli - Command-line interface
40+
│ │ │ ├── src/
41+
│ │ │ │ └── main.rs # CLI entry point
42+
│ │ │ └── Cargo.toml
43+
│ │ │
44+
│ │ ├── 📁 fcb_core/ # Core library (crate)
45+
│ │ │ ├── src/
46+
│ │ │ │ ├── lib.rs # Library entry point
47+
│ │ │ │ ├── http/ # HTTP range request client
48+
│ │ │ │ ├── index/ # Spatial (Packed R-tree) & Attribute (B+Tree) indexing
49+
│ │ │ │ ├── reader/ # FlatBuffers reading & deserialization
50+
│ │ │ │ └── writer/ # FlatBuffers writing & serialization
51+
│ │ │ └── Cargo.toml
52+
│ │ │
53+
│ │ ├── 📁 fcb_py/ # Python bindings (PyO3)
54+
│ │ │ ├── src/
55+
│ │ │ │ └── lib.rs # FFI bridge to Python
56+
│ │ │ └── Cargo.toml
57+
│ │ │
58+
│ │ ├── 📁 fcb_cpp/ # C++ bindings (cxx bridge)
59+
│ │ │ ├── src/
60+
│ │ │ │ └── lib.rs # FFI bridge to C++
61+
│ │ │ └── Cargo.toml
62+
│ │ │
63+
│ │ ├── 📁 wasm/ # WebAssembly bindings (wasm-bindgen)
64+
│ │ │ ├── src/
65+
│ │ │ │ └── lib.rs # FFI bridge to JS/TS
66+
│ │ │ └── Cargo.toml
67+
│ │ │
68+
│ │ ├── 📁 fcb_api/ # REST API server (axum)
69+
│ │ │ ├── src/
70+
│ │ │ │ └── main.rs # API server entry point
71+
│ │ │ └── Cargo.toml
72+
│ │ │
73+
│ │ └── 📁 data/ # Test data and fixtures
74+
│ │
75+
│ ├── 📁 cpp/ # C++ library bindings
76+
│ │ ├── 📁 include/ # Public C++ headers
77+
│ │ │ └── flatcitybuf/
78+
│ │ │ └── flatcitybuf.hpp
79+
│ │ ├── 📁 examples/ # C++ usage examples
80+
│ │ │ └── example.cpp
81+
│ │ ├── 📁 build/ # C++ build artifacts (generated)
82+
│ │ ├── 📄 CMakeLists.txt # CMake build configuration
83+
│ │ └── 📄 Doxyfile # Doxygen documentation config
84+
│ │
85+
│ └── 📁 ts/ # TypeScript/JavaScript package
86+
│ ├── 📄 package.json # npm package config
87+
│ ├── 📄 tsconfig.json # TypeScript config
88+
│ ├── 📄 fcb_wasm.js # Generated WASM bindings
89+
│ ├── 📄 fcb_wasm_bg.wasm # WebAssembly binary
90+
│ ├── 📄 fcb_wasm.d.ts # TypeScript definitions
91+
│ └── 📄 index.html # Demo/preview page
92+
93+
├── 📁 data/ # Development data files
94+
│ └── out/ # Output files from conversions
95+
96+
├── 📁 .agent/ # Agent configuration (for AI tools)
97+
│ └── rules/
98+
99+
├── 📁 .cursor/ # Cursor IDE configuration
100+
│ └── rules/
101+
102+
├── 📁 .serena/ # Serena AI cache
103+
│ ├── cache/
104+
│ └── memories/
105+
106+
└── 📁 .vscode/ # VS Code configuration
107+
└── settings.json
108+
```
109+
110+
## Component Overview
111+
112+
### Rust Workspace (`src/rust/`)
113+
114+
The Rust workspace is organized as a multi-crate project with the following members:
115+
116+
| Crate | Purpose | Language Bindings |
117+
|-------|---------|-------------------|
118+
| **fcb_core** | Core library with read/write/indexing capabilities | Pure Rust |
119+
| **cli** | Command-line interface (`fcb` command) | - |
120+
| **fcb_py** | Python bindings via PyO3 | Python |
121+
| **fcb_cpp** | C++ bindings via cxx bridge | C++ |
122+
| **wasm** | WebAssembly bindings via wasm-bindgen | JavaScript/TypeScript |
123+
| **fcb_api** | REST API server using axum | HTTP API |
124+
125+
### Language Bindings
126+
127+
FlatCityBuf provides native bindings for multiple languages:
128+
129+
1. **Python** (`src/rust/fcb_py/`) → Published to PyPI as `flatcitybuf`
130+
2. **C++** (`src/cpp/`) → Standalone C++ library with CMake build
131+
3. **JavaScript/TypeScript** (`src/ts/`) → Published to npm as `@cityjson/flatcitybuf`
132+
133+
### Build Artifacts (Not Tracked)
134+
135+
- `src/rust/target/` - Rust build artifacts (in `.gitignore`)
136+
- `src/cpp/build/` - C++ build artifacts (in `.gitignore`)
137+
138+
## Key Patterns
139+
140+
### Workspace Dependency Management
141+
142+
All dependencies are managed at the workspace level in `src/rust/Cargo.toml`. Individual crates use workspace dependencies:
143+
144+
```toml
145+
[dependencies]
146+
fcb_core = { workspace = true }
147+
```
148+
149+
### Code Organization
150+
151+
Each crate follows standard Rust conventions:
152+
- `src/lib.rs` - Library entry point
153+
- `src/main.rs` - Binary entry point
154+
- Feature flags in `Cargo.toml` for conditional compilation
155+
156+
### Cross-Language Bridge
157+
158+
Language bindings use appropriate FFI technologies:
159+
- **Python**: PyO3 with async runtime support
160+
- **C++**: cxx bridge for automatic Rust/C++ interoperability
161+
- **WASM**: wasm-bindgen for browser compatibility

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717
],
1818
"cursorpyright.analysis.extraPaths": [
1919
"${workspaceFolder}/src/rust/fcb_py/python"
20-
]
20+
],
21+
"cmake.sourceDirectory": "${workspaceFolder}/src/cpp"
2122
}

0 commit comments

Comments
 (0)