Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display cids with associated filename #124

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,6 @@ dist

# TernJS port file
.tern-port

# IDE files
.idea/
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ $ ipfs-car --pack path/to/files --output path/to/write/a.car
# use --wrapWithDirectory false to avoid this.
$ ipfs-car --pack path/to/file --wrapWithDirectory false --output path/to/write/a.car

# displays which file is being packed
$ ipfs-car --pack path/to/files --verbose
```

`--unpack` files from a .car
Expand All @@ -83,6 +85,9 @@ $ ipfs-car --list-roots path/to/my.car

# list the cids for all the blocks.
$ ipfs-car --list-cids path/to/my.car

# list both the files and their CIDs.
$ ipfs-car --list-full path/to/my.car
```

## API
Expand All @@ -103,7 +108,7 @@ To unpack content-addressable archives to files, you can use the functions provi

### `ipfs-car/pack`

Takes an [ImportCandidateStream](/~https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs-core-types/src/utils.ts#L21) and returns a a [CAR writer](/~https://github.com/ipld/js-car#carwriter) async iterable.
Takes an [ImportCandidateStream](/~https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs-core-types/src/utils.ts#L21) and returns a [CAR writer](/~https://github.com/ipld/js-car#carwriter) async iterable.

```js
import { pack } from 'ipfs-car/pack'
Expand All @@ -112,7 +117,7 @@ import { MemoryBlockStore } from 'ipfs-car/blockstore/memory' // You can also us
const { root, out } = await pack({
input: [new Uint8Array([21, 31, 41])],
blockstore: new MemoryBlockStore(),
wrapWithDirectory: true // Wraps input into a directory. Defaults to `true`
wrapWithDirectory: true, // Wraps input into a directory. Defaults to `true`
maxChunkSize: 262144 // The maximum block size in bytes. Defaults to `262144`. Max safe value is < 1048576 (1MiB)
})

Expand Down Expand Up @@ -205,7 +210,7 @@ for await (const file of unpackStream(inStream)) {
`unpackStream` takes an options object, allowing you to pass in a `BlockStore` implementation. The blocks are unpacked from the stream in the order they appear, which may not be the order needed to reassemble them into the Files and Directories they represent. The blockstore is used to store the blocks as they are consumed from the stream. Once the stream is consumed, the blockstore provides the random access by CID to the blocks, needed to assemble the tree.

The default is a [`MemoryBlockStore`](./src/blockstore/memory.ts), that will store all the blocks in memory.
For larger CARs in the browser you can use IndexedDB by passing in an [IdbBlocksStore]('./src/blockstore/idb.ts'), and in Node.js you can provide a [FsBlockStore] instance to write blocks to the tmp dir.
For larger CARs in the browser you can use IndexedDB by passing in an [IdbBlocksStore](./src/blockstore/idb.ts), and in Node.js you can provide an [FsBlockStore] instance to write blocks to the tmp dir.

```js
/* browser */
Expand All @@ -224,7 +229,7 @@ for await (const file of unpackStream(res.body, { blockstore })) {
blockstore.destroy()
```

When providing a custom Blockstore, it is your responsibiltiy to call `blockstore.destroy()` when you're finished. Failing to do so will fill up the users storage.
When providing a custom Blockstore, it is your responsibility to call `blockstore.destroy()` when you're finished. Failing to do so will fill up the users' storage.

### `ipfs-car/unpack/fs`

Expand Down
23 changes: 21 additions & 2 deletions src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import meow from 'meow'
import { CID } from 'multiformats';
import { packToFs } from '../pack/fs'
import { unpackToFs, unpackStreamToFs } from '../unpack/fs'
import { listFilesInCar, listCidsInCar, listRootsInCar } from './lib'
import {listFilesInCar, listCidsInCar, listRootsInCar, listFilesAndCidsInCar} from './lib'

interface Flags {
output?: string,
Expand All @@ -14,7 +14,9 @@ interface Flags {
list?: string,
listCids?: string
listRoots?: string
listFull?: string
wrapWithDirectory?: boolean
verbose?: boolean
}

const options = {
Expand Down Expand Up @@ -45,10 +47,18 @@ const options = {
listRoots: {
type: 'string'
},
listFull: {
type: 'string'
},
wrapWithDirectory: {
type: 'boolean',
alias: 'w',
default: true
},
verbose: {
type: 'boolean',
alias: 'v',
default: false
}
}
} as const;
Expand All @@ -72,6 +82,9 @@ const cli = meow(`
# pack files without wrapping with top-level directory
$ ipfs-car --wrapWithDirectory false --pack path/to/files --output path/to/write/a.car

# pack files and display which one is being packed
$ ipfs-car --pack /path/to/files --verbose

Unpacking files from a .car

# write 1 or more files to the current working dir.
Expand All @@ -97,6 +110,9 @@ const cli = meow(`
# list the files.
$ ipfs-car --list path/to/my.car

# list both the files' path and their CIDs.
$ ipfs-car --list-full path/to/my.car

TL;DR
--pack <path> --output <my.car>
--unpack <my.car> --output <path>
Expand All @@ -105,7 +121,7 @@ const cli = meow(`

async function handleInput ({ flags }: { flags: Flags }) {
if (flags.pack) {
const { root, filename } = await packToFs({input: flags.pack, output: flags.output, wrapWithDirectory: flags.wrapWithDirectory})
const { root, filename } = await packToFs({input: flags.pack, output: flags.output, wrapWithDirectory: flags.wrapWithDirectory, verbose: flags.verbose})
// tslint:disable-next-line: no-console
console.log(`root CID: ${root.toString()}`)
// tslint:disable-next-line: no-console
Expand All @@ -127,6 +143,9 @@ async function handleInput ({ flags }: { flags: Flags }) {
} else if (flags.listCids) {
return listCidsInCar({input: flags.listCids})

} else if (flags.listFull) {
return listFilesAndCidsInCar({input: flags.listFull})

} else if (!process.stdin.isTTY) {
// maybe stream?
// tslint:disable-next-line: no-console
Expand Down
8 changes: 8 additions & 0 deletions src/cli/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ export async function listRootsInCar ({input}: {input: string}) {
// tslint:disable-next-line: no-console
console.log(root.toString())
}
}

export async function listFilesAndCidsInCar({input}: {input: string}) {
const carReader = await CarIndexedReader.fromFile(input)
for await (const file of unpack(carReader)) {
// tslint:disable-next-line: no-console
console.log(`${file.cid.toString()} ${file.path}`)
}
}
5 changes: 3 additions & 2 deletions src/pack/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface PackToFsProperties extends PackProperties {
output?: string
}

export async function packToFs ({ input, output, blockstore: userBlockstore, hasher, maxChunkSize, maxChildrenPerNode, wrapWithDirectory, rawLeaves }: PackToFsProperties) {
export async function packToFs ({ input, output, blockstore: userBlockstore, hasher, maxChunkSize, maxChildrenPerNode, wrapWithDirectory, rawLeaves, verbose }: PackToFsProperties) {
const blockstore = userBlockstore ? userBlockstore : new FsBlockStore()
const location = output || `${os.tmpdir()}/${(parseInt(String(Math.random() * 1e9), 10)).toString() + Date.now()}`
const writable = fs.createWriteStream(location)
Expand All @@ -25,7 +25,8 @@ export async function packToFs ({ input, output, blockstore: userBlockstore, has
maxChunkSize,
maxChildrenPerNode,
wrapWithDirectory,
rawLeaves
rawLeaves,
verbose
})

if (!userBlockstore) {
Expand Down
3 changes: 2 additions & 1 deletion src/pack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { ImportCandidateStream, ImportCandidate } from 'ipfs-core-types/src
import type { MultihashHasher } from 'multiformats/hashes/interface'
export type { ImportCandidateStream }

import { Blockstore } from '../blockstore/index'
import { Blockstore } from '../blockstore'
import { MemoryBlockStore } from '../blockstore/memory'
import { unixfsImporterOptionsDefault } from './constants'

Expand All @@ -19,6 +19,7 @@ export interface PackProperties {
maxChildrenPerNode?: number,
wrapWithDirectory?: boolean,
hasher?: MultihashHasher,
verbose?: boolean,
/**
* Use raw codec for leaf nodes. Default: true.
*/
Expand Down
17 changes: 14 additions & 3 deletions src/pack/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import last from 'it-last'
import pipe from 'it-pipe'

import { CarWriter } from '@ipld/car'
import { importer } from 'ipfs-unixfs-importer'
import {importer, ImportResult} from 'ipfs-unixfs-importer'
import { normaliseInput } from 'ipfs-core-utils/files/normalise-input-multiple'
import globSource from 'ipfs-utils/src/files/glob-source.js'

Expand All @@ -21,7 +21,7 @@ export interface PackToStreamProperties extends PackProperties {
}

// Node version of toCar with Node Stream Writable
export async function packToStream ({ input, writable, blockstore: userBlockstore, hasher, maxChunkSize, maxChildrenPerNode, wrapWithDirectory, rawLeaves }: PackToStreamProperties) {
export async function packToStream ({ input, writable, blockstore: userBlockstore, hasher, maxChunkSize, maxChildrenPerNode, wrapWithDirectory, rawLeaves, verbose }: PackToStreamProperties) {
if (!input || (Array.isArray(input) && !input.length)) {
throw new Error('given input could not be parsed correctly')
}
Expand All @@ -40,7 +40,8 @@ export async function packToStream ({ input, writable, blockstore: userBlockstor
maxChildrenPerNode: maxChildrenPerNode || unixfsImporterOptionsDefault.maxChildrenPerNode,
wrapWithDirectory: wrapWithDirectory === false ? false : unixfsImporterOptionsDefault.wrapWithDirectory,
rawLeaves: rawLeaves == null ? unixfsImporterOptionsDefault.rawLeaves : rawLeaves
})
}),
(source: any) => printCarContent(source, verbose)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call this printUnixFsContent instead? It is more what it is than car content

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also thinking this should not be part of the lib, but part of the CLI. So, what do you think on receiving an optional handler as parameter to packToStream that would be an async iterator of verbose option?

Than CLI can actually provide the printUnixFsContent, and users can also provide any other function they care, being cautious that in the end it must yield what it received.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call this printUnixFsContent instead? It is more what it is than car content

The function can totally be renamed, no problem.

Also thinking this should not be part of the lib, but part of the CLI. So, what do you think on receiving an optional handler as parameter to packToStream that would be an async iterator of verbose option?

Than CLI can actually provide the printUnixFsContent, and users can also provide any other function they care, being cautious that in the end it must yield what it received.

I see what you mean and I understand the point, I'll look into making that change since it makes more sense as you state.
This allows to keep the cli and lib part on their own

Copy link
Contributor Author

@Breigner01 Breigner01 Jul 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It comes late but here are the edits asked.
LMK if anything else needs to be done here

))

if (!rootEntry || !rootEntry.cid) {
Expand Down Expand Up @@ -86,3 +87,13 @@ async function * legacyGlobSource (input: Iterable<string> | AsyncIterable<strin
}
}
}

async function *printCarContent(root: AsyncGenerator<ImportResult, void, unknown>, verbose: boolean | undefined): AsyncGenerator<ImportResult, void, unknown> {
for await (const entry of root) {
if (verbose && entry.path) {
// tslint:disable-next-line:no-console
console.log(`${entry.cid.toString()} ${entry.path}`)
}
yield entry
}
}