ls.go 5.42 KB
Newer Older
1 2 3 4 5
package unixfs

import (
	"fmt"
	"io"
6
	"sort"
7 8
	"text/tabwriter"

tavit ohanian's avatar
tavit ohanian committed
9
	cmdenv "gitlab.dms3.io/dms3/go-dms3/core/commands/cmdenv"
10

tavit ohanian's avatar
tavit ohanian committed
11 12 13 14
	cmds "gitlab.dms3.io/dms3/go-dms3-cmds"
	merkledag "gitlab.dms3.io/dms3/go-merkledag"
	unixfs "gitlab.dms3.io/dms3/go-unixfs"
	path "gitlab.dms3.io/dms3/interface-go-dms3-core/path"
15 16 17 18 19
)

type LsLink struct {
	Name, Hash string
	Size       uint64
20
	Type       string
21 22 23
}

type LsObject struct {
24 25 26
	Hash  string
	Size  uint64
	Type  string
27
	Links []LsLink
28 29 30
}

type LsOutput struct {
31 32
	Arguments map[string]string
	Objects   map[string]*LsObject
33 34 35
}

var LsCmd = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
36
	Helptext: cmds.HelpText{
tavit ohanian's avatar
tavit ohanian committed
37
		Tagline: "List directory contents for Unix filesystem objects. Deprecated: Use 'dms3 ls' instead.",
38
		ShortDescription: `
tavit ohanian's avatar
tavit ohanian committed
39
Displays the contents of an DMS3 or DMS3NS object(s) at the given path.
Richard Littauer's avatar
Richard Littauer committed
40 41 42

The JSON output contains size information. For files, the child size
is the total size of the file contents. For directories, the child
tavit ohanian's avatar
tavit ohanian committed
43
size is the DMS3 link size.
44

tavit ohanian's avatar
tavit ohanian committed
45 46
This functionality is deprecated, and will be removed in future versions as it duplicates the functionality of 'dms3 ls'.
If possible, please use 'dms3 ls' instead.
Richard Littauer's avatar
Richard Littauer committed
47 48
`,
		LongDescription: `
tavit ohanian's avatar
tavit ohanian committed
49
Displays the contents of an DMS3 or DMS3NS object(s) at the given path.
50

51 52
The JSON output contains size information. For files, the child size
is the total size of the file contents. For directories, the child
tavit ohanian's avatar
tavit ohanian committed
53
size is the DMS3 link size.
Richard Littauer's avatar
Richard Littauer committed
54 55

The path can be a prefixless ref; in this case, we assume it to be an
tavit ohanian's avatar
tavit ohanian committed
56
/dms3 ref and not /dms3ns.
Richard Littauer's avatar
Richard Littauer committed
57 58 59

Example:

tavit ohanian's avatar
tavit ohanian committed
60
    > dms3 file ls QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ
Richard Littauer's avatar
Richard Littauer committed
61
    cat.jpg
tavit ohanian's avatar
tavit ohanian committed
62
    > dms3 file ls /dms3/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ
Richard Littauer's avatar
Richard Littauer committed
63
    cat.jpg
64

tavit ohanian's avatar
tavit ohanian committed
65 66
This functionality is deprecated, and will be removed in future versions as it duplicates the functionality of 'dms3 ls'.
If possible, please use 'dms3 ls' instead.
67 68 69
`,
	},

Steven Allen's avatar
Steven Allen committed
70
	Arguments: []cmds.Argument{
tavit ohanian's avatar
tavit ohanian committed
71
		cmds.StringArg("dms3-path", true, true, "The path to the DMS3 object(s) to list links from.").EnableStdin(),
72
	},
Overbool's avatar
Overbool committed
73 74
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		nd, err := cmdenv.GetNode(env)
75
		if err != nil {
Overbool's avatar
Overbool committed
76
			return err
77 78
		}

79
		api, err := cmdenv.GetApi(env, req)
80
		if err != nil {
Overbool's avatar
Overbool committed
81
			return err
82 83
		}

Overbool's avatar
Overbool committed
84 85 86 87 88
		if err := req.ParseBodyArgs(); err != nil {
			return err
		}

		paths := req.Arguments
89

90 91 92 93 94
		output := LsOutput{
			Arguments: map[string]string{},
			Objects:   map[string]*LsObject{},
		}

95
		for _, p := range paths {
Overbool's avatar
Overbool committed
96
			ctx := req.Context
97

98
			merkleNode, err := api.ResolveNode(ctx, path.New(p))
99
			if err != nil {
Overbool's avatar
Overbool committed
100
				return err
101 102
			}

Jeromy's avatar
Jeromy committed
103
			c := merkleNode.Cid()
104

Jeromy's avatar
Jeromy committed
105
			hash := c.String()
106
			output.Arguments[p] = hash
107 108 109 110 111 112

			if _, ok := output.Objects[hash]; ok {
				// duplicate argument for an already-listed node
				continue
			}

113 114
			ndpb, ok := merkleNode.(*merkledag.ProtoNode)
			if !ok {
Overbool's avatar
Overbool committed
115
				return merkledag.ErrNotProtobuf
116 117
			}

Overbool's avatar
Overbool committed
118
			unixFSNode, err := unixfs.FSNodeFromBytes(ndpb.Data())
119
			if err != nil {
Overbool's avatar
Overbool committed
120
				return err
121
			}
122

Overbool's avatar
Overbool committed
123
			t := unixFSNode.Type()
124 125

			output.Objects[hash] = &LsObject{
Jeromy's avatar
Jeromy committed
126
				Hash: c.String(),
127
				Type: t.String(),
Overbool's avatar
Overbool committed
128
				Size: unixFSNode.FileSize(),
129 130
			}

131
			switch t {
Overbool's avatar
Overbool committed
132
			case unixfs.TFile:
133
				break
Overbool's avatar
Overbool committed
134
			case unixfs.THAMTShard:
135
				// We need a streaming ls API for this.
Overbool's avatar
Overbool committed
136
				return fmt.Errorf("cannot list large directories yet")
Overbool's avatar
Overbool committed
137
			case unixfs.TDirectory:
138
				links := make([]LsLink, len(merkleNode.Links()))
139
				output.Objects[hash].Links = links
140
				for i, link := range merkleNode.Links() {
Overbool's avatar
Overbool committed
141
					linkNode, err := link.GetNode(ctx, nd.DAG)
142
					if err != nil {
Overbool's avatar
Overbool committed
143
						return err
144
					}
145 146
					lnpb, ok := linkNode.(*merkledag.ProtoNode)
					if !ok {
Overbool's avatar
Overbool committed
147
						return merkledag.ErrNotProtobuf
148 149
					}

Overbool's avatar
Overbool committed
150
					d, err := unixfs.FSNodeFromBytes(lnpb.Data())
151
					if err != nil {
Overbool's avatar
Overbool committed
152
						return err
153
					}
Overbool's avatar
Overbool committed
154
					t := d.Type()
155 156
					lsLink := LsLink{
						Name: link.Name,
157
						Hash: link.Cid.String(),
158
						Type: t.String(),
159
					}
Overbool's avatar
Overbool committed
160
					if t == unixfs.TFile {
Overbool's avatar
Overbool committed
161
						lsLink.Size = d.FileSize()
162 163 164
					} else {
						lsLink.Size = link.Size
					}
165
					links[i] = lsLink
166
				}
Overbool's avatar
Overbool committed
167
			case unixfs.TSymlink:
Overbool's avatar
Overbool committed
168
				return fmt.Errorf("cannot list symlinks yet")
Jeromy's avatar
Jeromy committed
169
			default:
Overbool's avatar
Overbool committed
170
				return fmt.Errorf("unrecognized type: %s", t)
171 172 173
			}
		}

Overbool's avatar
Overbool committed
174
		return cmds.EmitOnce(res, &output)
175
	},
Overbool's avatar
Overbool committed
176 177 178
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *LsOutput) error {
			tw := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)
179 180 181

			nonDirectories := []string{}
			directories := []string{}
Overbool's avatar
Overbool committed
182 183
			for argument, hash := range out.Arguments {
				object, ok := out.Objects[hash]
184
				if !ok {
Overbool's avatar
Overbool committed
185
					return fmt.Errorf("unresolved hash: %s", hash)
186 187
				}

188
				if object.Type == "Directory" {
189
					directories = append(directories, argument)
190 191
				} else {
					nonDirectories = append(nonDirectories, argument)
192 193 194 195 196 197
				}
			}
			sort.Strings(nonDirectories)
			sort.Strings(directories)

			for _, argument := range nonDirectories {
Overbool's avatar
Overbool committed
198
				fmt.Fprintf(tw, "%s\n", argument)
199 200 201 202
			}

			seen := map[string]bool{}
			for i, argument := range directories {
Overbool's avatar
Overbool committed
203
				hash := out.Arguments[argument]
204 205 206 207 208
				if _, ok := seen[hash]; ok {
					continue
				}
				seen[hash] = true

Overbool's avatar
Overbool committed
209
				object := out.Objects[hash]
210
				if i > 0 || len(nonDirectories) > 0 {
Overbool's avatar
Overbool committed
211
					fmt.Fprintln(tw)
212
				}
Overbool's avatar
Overbool committed
213
				if len(out.Arguments) > 1 {
214
					for _, arg := range directories[i:] {
Overbool's avatar
Overbool committed
215
						if out.Arguments[arg] == hash {
216
							fmt.Fprintf(tw, "%s:\n", cmdenv.EscNonPrint(arg))
217
						}
218
					}
219 220
				}
				for _, link := range object.Links {
221
					fmt.Fprintf(tw, "%s\n", cmdenv.EscNonPrint(link.Name))
222 223
				}
			}
Overbool's avatar
Overbool committed
224
			tw.Flush()
225

Overbool's avatar
Overbool committed
226 227
			return nil
		}),
228 229 230
	},
	Type: LsOutput{},
}