initla code

This commit is contained in:
Louis Frei
2026-08-13 13:25:45 +02:00
commit 42fc9ee2dc
37 changed files with 236 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
target/
.DS_Store
.env*
*.pem
*.key
*.crt
*.cer
*.p12
*.diagnostic
*.log
*.dump
.idea/
.vscode/
postgres-test-data/
local-test-data/
diagnostics/
release-archives/
*.credential-test
*.credentials-test
+38
View File
@@ -0,0 +1,38 @@
[package]
name = "azure-database-tui"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
[features]
postgres-integration = []
[dependencies]
tokio = { version = "1.43", features = ["fs", "io-util", "macros", "net", "process", "rt", "rt-multi-thread", "sync", "time"] }
ratatui = "0.29"
crossterm = "0.28"
async-trait = "0.1"
futures-util = "0.3"
tokio-util = "0.7"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["serde", "v4"] }
thiserror = "2"
zeroize = "1"
secrecy = "0.10"
getrandom = "0.3"
sha2 = "0.10"
bytes = "1"
directories = "6"
rustls = "0.23"
rustls-native-certs = "0.8"
rustls-pemfile = "2"
tokio-postgres = { version = "0.7", features = ["with-serde_json-1"] }
tokio-postgres-rustls = "0.13"
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
keyring = "3"
arboard = "3"
[dev-dependencies]
tempfile = "3"
+23
View File
@@ -0,0 +1,23 @@
# Azure Database TUI
This repository contains a Rust TUI for administering Azure Database for
PostgreSQL flexible servers. It discovers servers through the current Azure CLI
tenant and connects to PostgreSQL using mandatory TLS.
## Prerequisites
Install Azure CLI and authenticate to the intended tenant. Network access to
the server endpoint, including private endpoint, firewall, and DNS access, is
required. Azure CLI visibility does not imply PostgreSQL authorization.
## Build and test
Use the pinned Rust toolchain with `cargo build`, `cargo test`, and the local
integration helper documented in `docs/operations.md`.
Supported release targets are macOS ARM64 and Windows x86_64. Raw SQL is sent
to PostgreSQL as entered; use it only when you understand its authority and
effects.
See [security](docs/security.md), [operations](docs/operations.md), and
[release](docs/release.md).
+3
View File
@@ -0,0 +1,3 @@
[toolchain]
channel = "1.85.0"
components = ["rustfmt", "clippy"]
+2
View File
@@ -0,0 +1,2 @@
pub struct AzureDiscovery;
pub struct AzureCliAdapter;
+1
View File
@@ -0,0 +1 @@
pub mod process; pub mod azure_cli; pub mod postgres;
+5
View File
@@ -0,0 +1,5 @@
pub struct PostgresConnector;
pub struct SessionManager;
pub struct SqlSession;
pub struct GuidedSession;
pub struct ConnectionRequest;
+3
View File
@@ -0,0 +1,3 @@
#[derive(Debug, Clone)] pub struct CommandSpec;
pub trait CommandRunner {}
pub struct TokioCommandRunner;
+3
View File
@@ -0,0 +1,3 @@
use crate::error::AppError;
pub struct AppController;
impl AppController { pub async fn new() -> Result<Self, AppError> { Err(AppError(crate::error::SafeError { kind: crate::error::ErrorKind::Internal, message: "Application unavailable", sqlstate: None, severity: crate::error::ErrorSeverity::Error, retryable: false, operation_id: None })) } pub async fn run(&mut self) -> Result<(), AppError> { Ok(()) } }
+2
View File
@@ -0,0 +1,2 @@
#[derive(Debug, Clone)] pub struct AppCommand;
#[derive(Debug, Clone)] pub struct AppEvent;
+1
View File
@@ -0,0 +1 @@
pub mod controller; pub mod events; pub mod state;
+1
View File
@@ -0,0 +1 @@
#[derive(Debug, Clone)] pub struct AppState;
+10
View File
@@ -0,0 +1,10 @@
#[derive(Debug, Clone)] pub struct CatalogSnapshot;
#[derive(Debug, Clone)] pub struct SchemaMeta;
#[derive(Debug, Clone)] pub struct RelationMeta;
#[derive(Debug, Clone)] pub struct ColumnMeta;
#[derive(Debug, Clone)] pub struct KeysetDefinition;
#[derive(Debug, Clone)] pub struct FunctionMeta;
#[derive(Debug, Clone)] pub struct RoleMeta;
#[derive(Debug, Clone)] pub struct PrivilegeObservation;
#[derive(Debug, Clone)] pub struct DefaultPrivilegeObservation;
#[derive(Debug, Clone)] pub struct ServerCapabilities;
+9
View File
@@ -0,0 +1,9 @@
use uuid::Uuid;
macro_rules! text_id { ($name:ident) => { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name(String); impl $name { pub fn new(value: impl Into<String>) -> Self { Self(value.into()) } pub fn as_str(&self) -> &str { &self.0 } } }; }
text_id!(TenantId); text_id!(SubscriptionId); text_id!(AzureResourceId);
text_id!(CatalogGeneration); text_id!(RoleOid); text_id!(RelationOid);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ProfileId(Uuid);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct OperationId(Uuid);
impl ProfileId { pub fn new() -> Self { Self(Uuid::new_v4()) } }
impl OperationId { pub fn new() -> Self { Self(Uuid::new_v4()) } }
+7
View File
@@ -0,0 +1,7 @@
#[derive(Debug, Clone)] pub struct Tenant;
#[derive(Debug, Clone)] pub struct Subscription;
#[derive(Debug, Clone)] pub struct FlexibleServer;
#[derive(Debug, Clone)] pub struct DatabaseRef;
#[derive(Debug, Clone)] pub struct DiscoveryFailure;
#[derive(Debug, Clone)] pub struct DiscoveryReport;
#[derive(Debug, Clone)] pub struct DiscoveryEvent;
+6
View File
@@ -0,0 +1,6 @@
pub mod catalog;
pub mod ids;
pub mod inventory;
pub mod permissions;
pub mod profile;
pub mod sql;
+8
View File
@@ -0,0 +1,8 @@
#[derive(Debug, Clone)] pub struct DesiredPrivilegeProfile;
#[derive(Debug, Clone)] pub struct PrivilegeScope;
#[derive(Debug, Clone)] pub struct PrivilegeKind;
#[derive(Debug, Clone)] pub struct RoleMembershipIntent;
#[derive(Debug, Clone)] pub struct Grantability;
#[derive(Debug, Clone)] pub struct PlanOperation;
#[derive(Debug, Clone)] pub struct PermissionPlan;
#[derive(Debug, Clone)] pub struct PlanResult;
+6
View File
@@ -0,0 +1,6 @@
#[derive(Debug, Clone)] pub struct ProfileFile;
#[derive(Debug, Clone)] pub struct ConnectionProfile;
#[derive(Debug, Clone)] pub struct SecretReference;
#[derive(Debug, Clone)] pub struct TlsPolicy;
#[derive(Debug, Clone)] pub struct ConnectionDraft;
#[derive(Debug, Clone)] pub struct ProfileValidationError;
+9
View File
@@ -0,0 +1,9 @@
#[derive(Debug, Clone)] pub struct StatementRange;
pub struct SqlSubmission;
#[derive(Debug, Clone)] pub struct SqlRunRequest;
#[derive(Debug, Clone)] pub struct SqlExecutionEvent;
#[derive(Debug, Clone)] pub struct ResultSetMeta;
pub struct DisplayCell;
#[derive(Debug, Clone)] pub struct CopyDirection;
#[derive(Debug, Clone)] pub struct TransactionState;
#[derive(Debug, Clone)] pub struct SqlLexer;
+48
View File
@@ -0,0 +1,48 @@
use std::fmt;
use crate::domain::ids::OperationId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind { Internal, Input, Network, Database, Process }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorSeverity { Info, Warning, Error }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SqlStateCode([u8; 5]);
impl SqlStateCode {
pub fn parse(value: &str) -> Option<Self> {
let bytes = value.as_bytes();
if bytes.len() == 5 && bytes.iter().all(|b| b.is_ascii_uppercase() || b.is_ascii_digit()) {
Some(Self(bytes.try_into().ok()?))
} else { None }
}
}
#[derive(Debug, Clone)]
pub struct SafeError {
pub kind: ErrorKind,
pub message: &'static str,
pub sqlstate: Option<SqlStateCode>,
pub severity: ErrorSeverity,
pub retryable: bool,
pub operation_id: Option<OperationId>,
}
#[derive(Debug, Clone)]
pub struct AppError(pub SafeError);
#[derive(Debug, Clone)]
pub struct SafeDiagnostic {
pub error: SafeError,
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.message.fmt(f) }
}
impl std::error::Error for AppError {}
pub fn redact_sensitive_text(input: &str) -> String {
if input.is_empty() { String::new() } else { "[diagnostic details omitted]".to_owned() }
}
+9
View File
@@ -0,0 +1,9 @@
#![forbid(unsafe_code)]
pub mod error;
pub mod adapters;
pub mod app;
pub mod domain;
pub mod platform;
pub mod services;
pub mod tui;
+7
View File
@@ -0,0 +1,7 @@
use azure_database_tui::app::controller::AppController;
#[tokio::main]
async fn main() -> Result<(), azure_database_tui::error::AppError> {
let mut controller = AppController::new().await?;
controller.run().await
}
+1
View File
@@ -0,0 +1 @@
pub trait ClipboardService {} pub struct SystemClipboardService;
+1
View File
@@ -0,0 +1 @@
pub trait Clock {} pub struct SystemClock;
+1
View File
@@ -0,0 +1 @@
pub trait SecretStore {} pub struct KeyringSecretStore; pub struct UnavailableSecretStore;
+1
View File
@@ -0,0 +1 @@
pub mod paths; pub mod clock; pub mod profile_file; pub mod keychain; pub mod clipboard; pub mod secret_input;
+2
View File
@@ -0,0 +1,2 @@
pub struct AppPaths;
impl AppPaths { pub fn discover() -> Self { Self } }
+1
View File
@@ -0,0 +1 @@
pub struct ProfileFileStore;
+1
View File
@@ -0,0 +1 @@
pub struct SecretInputBuffer; pub struct SecretHandle; pub struct OneTimeSecret; pub struct SecretVault;
+1
View File
@@ -0,0 +1 @@
pub struct CatalogService;
+1
View File
@@ -0,0 +1 @@
pub struct DiscoveryService;
+1
View File
@@ -0,0 +1 @@
pub mod discovery; pub mod profiles; pub mod catalog; pub mod tables; pub mod roles; pub mod sql_executor;
+1
View File
@@ -0,0 +1 @@
pub struct ProfileService;
+1
View File
@@ -0,0 +1 @@
pub struct RoleService;
+1
View File
@@ -0,0 +1 @@
pub struct SqlExecutor;
+1
View File
@@ -0,0 +1 @@
pub struct TableService;
+1
View File
@@ -0,0 +1 @@
pub struct TerminalGuard;