UNPKG

@yume-chan/adb

Version:

TypeScript implementation of Android Debug Bridge (ADB) protocol.

178 lines (163 loc) 4.66 kB
import type { StructValue } from "@yume-chan/struct"; import { struct, u32, u64 } from "@yume-chan/struct"; import { AdbSyncRequestId, adbSyncWriteRequest } from "./request.js"; import { AdbSyncResponseId, adbSyncReadResponse } from "./response.js"; import type { AdbSyncSocket } from "./socket.js"; // https://github.com/python/cpython/blob/4e581d64b8aff3e2eda99b12f080c877bb78dfca/Lib/stat.py#L36 export const LinuxFileType = { Directory: 0o04, File: 0o10, Link: 0o12, } as const; export type LinuxFileType = (typeof LinuxFileType)[keyof typeof LinuxFileType]; export interface AdbSyncStat { mode: number; size: bigint; mtime: bigint; get type(): LinuxFileType; get permission(): number; uid?: number; gid?: number; atime?: bigint; ctime?: bigint; } export const AdbSyncLstatResponse = struct( { mode: u32, size: u32, mtime: u32 }, { littleEndian: true, extra: { get type(): LinuxFileType { return (this.mode >> 12) as LinuxFileType; }, get permission(): number { return this.mode & 0b00001111_11111111; }, }, postDeserialize(value) { if (value.mode === 0 && value.size === 0 && value.mtime === 0) { throw new Error("lstat error"); } return value; }, }, ); export type AdbSyncLstatResponse = StructValue<typeof AdbSyncLstatResponse>; export const AdbSyncStatErrorCode = { SUCCESS: 0, EACCES: 13, EEXIST: 17, EFAULT: 14, EFBIG: 27, EINTR: 4, EINVAL: 22, EIO: 5, EISDIR: 21, ELOOP: 40, EMFILE: 24, ENAMETOOLONG: 36, ENFILE: 23, ENOENT: 2, ENOMEM: 12, ENOSPC: 28, ENOTDIR: 20, EOVERFLOW: 75, EPERM: 1, EROFS: 30, ETXTBSY: 26, } as const; export type AdbSyncStatErrorCode = (typeof AdbSyncStatErrorCode)[keyof typeof AdbSyncStatErrorCode]; const AdbSyncStatErrorName = /* #__PURE__ */ (() => Object.fromEntries( Object.entries(AdbSyncStatErrorCode).map(([key, value]) => [ value, key, ]), ))(); export const AdbSyncStatResponse = struct( { error: u32<AdbSyncStatErrorCode>(), dev: u64, ino: u64, mode: u32, nlink: u32, uid: u32, gid: u32, size: u64, atime: u64, mtime: u64, ctime: u64, }, { littleEndian: true, extra: { get type(): LinuxFileType { return (this.mode >> 12) as LinuxFileType; }, get permission(): number { return this.mode & 0b00001111_11111111; }, }, postDeserialize(value) { if (value.error) { throw new Error(AdbSyncStatErrorName[value.error]); } return value; }, }, ); export type AdbSyncStatResponse = StructValue<typeof AdbSyncStatResponse>; export async function adbSyncLstat( socket: AdbSyncSocket, path: string, v2: boolean, ): Promise<AdbSyncStat> { const locked = await socket.lock(); try { if (v2) { await adbSyncWriteRequest(locked, AdbSyncRequestId.LstatV2, path); return await adbSyncReadResponse( locked, AdbSyncResponseId.Lstat2, AdbSyncStatResponse, ); } else { await adbSyncWriteRequest(locked, AdbSyncRequestId.Lstat, path); const response = await adbSyncReadResponse( locked, AdbSyncResponseId.Lstat, AdbSyncLstatResponse, ); return { mode: response.mode, // Convert to `BigInt` to make it compatible with `AdbSyncStatResponse` size: BigInt(response.size), mtime: BigInt(response.mtime), get type() { return response.type; }, get permission() { return response.permission; }, }; } } finally { locked.release(); } } export async function adbSyncStat( socket: AdbSyncSocket, path: string, ): Promise<AdbSyncStatResponse> { const locked = await socket.lock(); try { await adbSyncWriteRequest(locked, AdbSyncRequestId.Stat, path); return await adbSyncReadResponse( locked, AdbSyncResponseId.Stat, AdbSyncStatResponse, ); } finally { locked.release(); } }