diff --git a/apps/frontend/src/app/app.config.ts b/apps/frontend/src/app/app.config.ts index 90081d9..ea27f81 100644 --- a/apps/frontend/src/app/app.config.ts +++ b/apps/frontend/src/app/app.config.ts @@ -1,5 +1,6 @@ import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZonelessChangeDetection, NgZone } from '@angular/core'; import { provideRouter } from '@angular/router'; +import { provideHttpClient, withFetch } from '@angular/common/http'; import { MONACO_PATH, MonacoEditorLoaderService } from '@materia-ui/ngx-monaco-editor'; import { routes } from './app.routes'; @@ -9,7 +10,8 @@ export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), provideZonelessChangeDetection(), - provideRouter(routes), + provideRouter(routes), + provideHttpClient(withFetch()), provideClientHydration(withEventReplay()), { provide: MONACO_PATH, diff --git a/apps/frontend/src/app/components/editor/editor.component.ts b/apps/frontend/src/app/components/editor/editor.component.ts index 7f804d9..d07fa6f 100644 --- a/apps/frontend/src/app/components/editor/editor.component.ts +++ b/apps/frontend/src/app/components/editor/editor.component.ts @@ -3,6 +3,7 @@ import { CommonModule, isPlatformBrowser } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'; import { PLATFORM_ID, inject } from '@angular/core'; +import { CompilerService } from '../../services/compiler'; const DEFAULT_RUST_CODE = `// Welcome to Soroban Smart Contract Editor @@ -33,6 +34,7 @@ export class EditorComponent implements OnDestroy { private timeoutIds = new Set(); isLoading = false; isBrowser = isPlatformBrowser(inject(PLATFORM_ID)); + private compilerService = inject(CompilerService); // Validation and output properties errorMessage: string = ''; @@ -114,6 +116,21 @@ export class EditorComponent implements OnDestroy { this.outputType = 'info'; console.log('Compiling Rust smart contract code:', this.code); + this.compilerService.compile(this.code).subscribe({ + next: (response) => { + this.isLoading = false; + this.outputMessage = 'Compilation completed successfully!'; + this.outputType = 'success'; + console.log('Compilation response:', response); + }, + error: (error) => { + this.isLoading = false; + this.errorMessage = 'Compilation failed: ' + (error.message || error); + this.outputType = 'error'; + console.error('Compilation error:', error); + } + }); + // TODO: Implement API call to backend compiler const timeoutId = setTimeout(() => { this.isLoading = false; @@ -140,6 +157,21 @@ export class EditorComponent implements OnDestroy { this.outputType = 'info'; console.log('Testing Rust smart contract code:', this.code); + this.compilerService.test(this.code).subscribe({ + next: (response) => { + this.isLoading = false; + this.outputMessage = 'All tests passed successfully!'; + this.outputType = 'success'; + console.log('Test response:', response); + }, + error: (error) => { + this.isLoading = false; + this.errorMessage = 'Tests failed: ' + (error.message || error); + this.outputType = 'error'; + console.error('Test error:', error); + } + }); + // TODO: Implement API call to backend test runner const timeoutId = setTimeout(() => { this.isLoading = false; diff --git a/apps/frontend/src/app/services/compiler.spec.ts b/apps/frontend/src/app/services/compiler.spec.ts new file mode 100644 index 0000000..79f6569 --- /dev/null +++ b/apps/frontend/src/app/services/compiler.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { Compiler } from './compiler'; + +describe('Compiler', () => { + let service: Compiler; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(Compiler); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/apps/frontend/src/app/services/compiler.ts b/apps/frontend/src/app/services/compiler.ts new file mode 100644 index 0000000..f818033 --- /dev/null +++ b/apps/frontend/src/app/services/compiler.ts @@ -0,0 +1,85 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; + +// Define interfaces for type safety +export interface CompileRequest { + code: string; +} + +export interface CompileResponse { + output: string; + success?: boolean; + error?: string; +} + +export interface TestResponse { + output: string; + success?: boolean; + error?: string; +} + +@Injectable({ + providedIn: 'root' +}) +export class CompilerService { + private readonly API_BASE_URL = 'http://localhost:3000/api'; + private http = inject(HttpClient); + + /** + * Compile Rust smart contract code + */ + compile(code: string): Observable { + const request: CompileRequest = { code }; + + return this.http.post(`${this.API_BASE_URL}/compile`, request) + .pipe( + map(response => ({ + ...response, + success: true + })), + catchError(this.handleError) + ); + } + + /** + * Test Rust smart contract code + */ + test(code: string): Observable { + const request: CompileRequest = { code }; + + return this.http.post(`${this.API_BASE_URL}/test`, request) + .pipe( + map(response => ({ + ...response, + success: true + })), + catchError(this.handleError) + ); + } + + /** + * Handle HTTP errors + */ + private handleError(error: HttpErrorResponse): Observable { + let errorMessage = 'An unknown error occurred'; + + if (error.error instanceof ErrorEvent) { + // Client-side error + errorMessage = `Error: ${error.error.message}`; + } else { + // Server-side error + errorMessage = error.error?.message || + error.error?.output || + `Server Error: ${error.status} - ${error.statusText}`; + } + + console.error('CompilerService Error:', errorMessage); + return throwError(() => ({ + output: errorMessage, + success: false, + error: errorMessage + })); + } +}